diff options
Diffstat (limited to 'WebCore/rendering')
201 files changed, 7492 insertions, 6689 deletions
diff --git a/WebCore/rendering/CounterNode.cpp b/WebCore/rendering/CounterNode.cpp index c164c81..ac83d5a 100644 --- a/WebCore/rendering/CounterNode.cpp +++ b/WebCore/rendering/CounterNode.cpp @@ -41,6 +41,11 @@ CounterNode::CounterNode(RenderObject* o, bool hasResetType, int value) { } +PassRefPtr<CounterNode> CounterNode::create(RenderObject* renderer, bool hasResetType, int value) +{ + return adoptRef(new CounterNode(renderer, hasResetType, value)); +} + CounterNode* CounterNode::nextInPreOrderAfterChildren(const CounterNode* stayWithin) const { if (this == stayWithin) diff --git a/WebCore/rendering/CounterNode.h b/WebCore/rendering/CounterNode.h index e35fb61..529d409 100644 --- a/WebCore/rendering/CounterNode.h +++ b/WebCore/rendering/CounterNode.h @@ -24,6 +24,7 @@ #include <wtf/Forward.h> #include <wtf/Noncopyable.h> +#include <wtf/RefCounted.h> // This implements a counter tree that is used for finding parents in counters() lookup, // and for propagating count changes when nodes are added or removed. @@ -38,9 +39,9 @@ namespace WebCore { class RenderObject; -class CounterNode : public Noncopyable { +class CounterNode : public RefCounted<CounterNode> { public: - CounterNode(RenderObject*, bool isReset, int value); + static PassRefPtr<CounterNode> create(RenderObject*, bool isReset, int value); bool actsAsReset() const { return m_hasResetType || !m_parent; } bool hasResetType() const { return m_hasResetType; } @@ -64,6 +65,7 @@ public: void removeChild(CounterNode*, const AtomicString& identifier); private: + CounterNode(RenderObject*, bool isReset, int value); int computeCountInParent() const; void recount(const AtomicString& identifier); diff --git a/WebCore/rendering/EllipsisBox.cpp b/WebCore/rendering/EllipsisBox.cpp index 1d71d35..f9c4f03 100644 --- a/WebCore/rendering/EllipsisBox.cpp +++ b/WebCore/rendering/EllipsisBox.cpp @@ -114,7 +114,7 @@ bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu } IntRect boundsRect = IntRect(tx, ty, m_logicalWidth, m_height); - if (visibleToHitTesting() && boundsRect.intersects(result.rectFromPoint(x, y))) { + if (visibleToHitTesting() && boundsRect.intersects(result.rectForPoint(x, y))) { renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); if (!result.addNodeToRectBasedTestResult(renderer()->node(), x, y, boundsRect)) return true; diff --git a/WebCore/rendering/HitTestResult.cpp b/WebCore/rendering/HitTestResult.cpp index 35a6d23..1bc238f 100644 --- a/WebCore/rendering/HitTestResult.cpp +++ b/WebCore/rendering/HitTestResult.cpp @@ -29,6 +29,7 @@ #include "HTMLInputElement.h" #include "HTMLMediaElement.h" #include "HTMLNames.h" +#include "HTMLParserIdioms.h" #include "RenderImage.h" #include "Scrollbar.h" #include "SelectionController.h" @@ -305,14 +306,14 @@ KURL HitTestResult::absoluteImageURL() const } else return KURL(); - return m_innerNonSharedNode->document()->completeURL(deprecatedParseURL(urlString)); + return m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString)); } KURL HitTestResult::absoluteMediaURL() const { #if ENABLE(VIDEO) if (HTMLMediaElement* mediaElt = mediaElement()) - return m_innerNonSharedNode->document()->completeURL(deprecatedParseURL(mediaElt->currentSrc())); + return m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(mediaElt->currentSrc())); return KURL(); #else return KURL(); @@ -461,7 +462,7 @@ KURL HitTestResult::absoluteLinkURL() const else return KURL(); - return m_innerURLElement->document()->completeURL(deprecatedParseURL(urlString)); + return m_innerURLElement->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString)); } bool HitTestResult::isLiveLink() const @@ -530,7 +531,7 @@ bool HitTestResult::addNodeToRectBasedTestResult(Node* node, int x, int y, const node = node->shadowAncestorNode(); m_rectBasedTestResult.add(node); - return !rect.contains(rectFromPoint(x, y)); + return !rect.contains(rectForPoint(x, y)); } void HitTestResult::append(const HitTestResult& other) @@ -552,7 +553,7 @@ void HitTestResult::append(const HitTestResult& other) m_rectBasedTestResult.add(it->get()); } -IntRect HitTestResult::rectFromPoint(const IntPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) +IntRect HitTestResult::rectForPoint(const IntPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) { IntPoint actualPoint(point); actualPoint -= IntSize(leftPadding, topPadding); diff --git a/WebCore/rendering/HitTestResult.h b/WebCore/rendering/HitTestResult.h index 1304e22..8d9b18d 100644 --- a/WebCore/rendering/HitTestResult.h +++ b/WebCore/rendering/HitTestResult.h @@ -98,9 +98,9 @@ public: // Rect-based hit test related methods. bool isRectBasedTest() const { return m_isRectBased; } - IntRect rectFromPoint(int x, int y) const; - IntRect rectFromPoint(const IntPoint&) const; - static IntRect rectFromPoint(const IntPoint&, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); + IntRect rectForPoint(int x, int y) const; + IntRect rectForPoint(const IntPoint&) const; + static IntRect rectForPoint(const IntPoint&, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); int topPadding() const { return m_topPadding; } int rightPadding() const { return m_rightPadding; } int bottomPadding() const { return m_bottomPadding; } @@ -134,9 +134,9 @@ private: ListHashSet<RefPtr<Node> > m_rectBasedTestResult; }; -inline IntRect HitTestResult::rectFromPoint(int x, int y) const +inline IntRect HitTestResult::rectForPoint(int x, int y) const { - return rectFromPoint(IntPoint(x, y), m_topPadding, m_rightPadding, m_bottomPadding, m_leftPadding); + return rectForPoint(IntPoint(x, y), m_topPadding, m_rightPadding, m_bottomPadding, m_leftPadding); } // Formula: @@ -144,9 +144,9 @@ inline IntRect HitTestResult::rectFromPoint(int x, int y) const // y = p.y() - topPadding // width = leftPadding + rightPadding + 1 // height = topPadding + bottomPadding + 1 -inline IntRect HitTestResult::rectFromPoint(const IntPoint& point) const +inline IntRect HitTestResult::rectForPoint(const IntPoint& point) const { - return rectFromPoint(point, m_topPadding, m_rightPadding, m_bottomPadding, m_leftPadding); + return rectForPoint(point, m_topPadding, m_rightPadding, m_bottomPadding, m_leftPadding); } String displayString(const String&, const Node*); diff --git a/WebCore/rendering/InlineBox.cpp b/WebCore/rendering/InlineBox.cpp index 91cbaff..2028d4e 100644 --- a/WebCore/rendering/InlineBox.cpp +++ b/WebCore/rendering/InlineBox.cpp @@ -95,14 +95,14 @@ int InlineBox::logicalHeight() const if (renderer()->isText()) return m_isText ? renderer()->style(m_firstLine)->font().height() : 0; if (renderer()->isBox() && parent()) - return toRenderBox(m_renderer)->height(); + return m_isVertical ? toRenderBox(m_renderer)->width() : toRenderBox(m_renderer)->height(); ASSERT(isInlineFlowBox()); RenderBoxModelObject* flowObject = boxModelObject(); const Font& font = renderer()->style(m_firstLine)->font(); int result = font.height(); if (parent()) - result += flowObject->borderAndPaddingHeight(); + result += flowObject->borderAndPaddingLogicalHeight(); return result; } diff --git a/WebCore/rendering/InlineBox.h b/WebCore/rendering/InlineBox.h index 1cd88ff..38a7805 100644 --- a/WebCore/rendering/InlineBox.h +++ b/WebCore/rendering/InlineBox.h @@ -21,6 +21,7 @@ #ifndef InlineBox_h #define InlineBox_h +#include "RenderBR.h" #include "RenderBoxModelObject.h" #include "TextDirection.h" @@ -139,6 +140,7 @@ public: virtual bool isRootInlineBox() const { return false; } #if ENABLE(SVG) virtual bool isSVGInlineTextBox() const { return false; } + virtual bool isSVGInlineFlowBox() const { return false; } virtual bool isSVGRootInlineBox() const { return false; } #endif @@ -216,6 +218,7 @@ public: // The logicalLeft position is the left edge of the line box in a horizontal line and the top edge in a vertical line. int logicalLeft() const { return !m_isVertical ? m_x : m_y; } + int logicalRight() const { return logicalLeft() + logicalWidth(); } void setLogicalLeft(int left) { if (!m_isVertical) @@ -241,8 +244,9 @@ public: // The logical height is our extent in the block flow direction, i.e., height for horizontal text and width for vertical text. int logicalHeight() const; - inline int baselinePosition(bool isRootLineBox) const { return renderer()->baselinePosition(m_firstLine, isRootLineBox); } - inline int lineHeight(bool isRootLineBox) const { return renderer()->lineHeight(m_firstLine, isRootLineBox); } + virtual int baselinePosition() const { return boxModelObject()->baselinePosition(m_firstLine, m_isVertical ? VerticalLine : HorizontalLine, PositionOnContainingLine); } + virtual int lineHeight() const { return boxModelObject()->lineHeight(m_firstLine, m_isVertical ? VerticalLine : HorizontalLine, PositionOnContainingLine); } + virtual int caretMinOffset() const; virtual int caretMaxOffset() const; @@ -251,8 +255,9 @@ public: unsigned char bidiLevel() const { return m_bidiEmbeddingLevel; } void setBidiLevel(unsigned char level) { m_bidiEmbeddingLevel = level; } TextDirection direction() const { return m_bidiEmbeddingLevel % 2 ? RTL : LTR; } - int caretLeftmostOffset() const { return direction() == LTR ? caretMinOffset() : caretMaxOffset(); } - int caretRightmostOffset() const { return direction() == LTR ? caretMaxOffset() : caretMinOffset(); } + bool isLeftToRightDirection() const { return direction() == LTR; } + int caretLeftmostOffset() const { return isLeftToRightDirection() ? caretMinOffset() : caretMaxOffset(); } + int caretRightmostOffset() const { return isLeftToRightDirection() ? caretMaxOffset() : caretMinOffset(); } virtual void clearTruncation() { } diff --git a/WebCore/rendering/InlineFlowBox.cpp b/WebCore/rendering/InlineFlowBox.cpp index 588d054..79c571d 100644 --- a/WebCore/rendering/InlineFlowBox.cpp +++ b/WebCore/rendering/InlineFlowBox.cpp @@ -83,8 +83,6 @@ void InlineFlowBox::addToLine(InlineBox* child) child->setIsVertical(m_isVertical); if (child->isText()) m_hasTextChildren = true; - if (child->renderer()->selectionState() != RenderObject::SelectionNone) - root()->setHasSelectedChildren(true); checkConsistency(); } @@ -209,7 +207,7 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* en // The root inline box never has borders/margins/padding. if (parent()) { - bool ltr = renderer()->style()->direction() == LTR; + bool ltr = renderer()->style()->isLeftToRightDirection(); // Check to see if all initial lines are unconstructed. If so, then // we know the inline began on this line (unless we are a continuation). @@ -253,24 +251,24 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* en } } -int InlineFlowBox::placeBoxesInInlineDirection(int xPos, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) +int InlineFlowBox::placeBoxesInInlineDirection(int logicalLeft, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) { // Set our x position. - setX(xPos); + setLogicalLeft(logicalLeft); - int leftLayoutOverflow = xPos; - int rightLayoutOverflow = xPos; - int leftVisualOverflow = xPos; - int rightVisualOverflow = xPos; + int logicalLeftLayoutOverflow = logicalLeft; + int logicalRightLayoutOverflow = logicalLeft; + int logicalLeftVisualOverflow = logicalLeft; + int logicalRightVisualOverflow = logicalLeft; - int boxShadowLeft; - int boxShadowRight; - renderer()->style(m_firstLine)->getBoxShadowHorizontalExtent(boxShadowLeft, boxShadowRight); + int boxShadowLogicalLeft; + int boxShadowLogicalRight; + renderer()->style(m_firstLine)->getBoxShadowInlineDirectionExtent(boxShadowLogicalLeft, boxShadowLogicalRight); - leftVisualOverflow = min(xPos + boxShadowLeft, leftVisualOverflow); + logicalLeftVisualOverflow = min(logicalLeft + boxShadowLogicalLeft, logicalLeftVisualOverflow); - int startX = xPos; - xPos += borderLogicalLeft() + paddingLogicalLeft(); + int startLogicalLeft = logicalLeft; + logicalLeft += borderLogicalLeft() + paddingLogicalLeft(); for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { if (curr->renderer()->isText()) { @@ -278,81 +276,87 @@ int InlineFlowBox::placeBoxesInInlineDirection(int xPos, bool& needsWordSpacing, RenderText* rt = toRenderText(text->renderer()); if (rt->textLength()) { if (needsWordSpacing && isSpaceOrNewline(rt->characters()[text->start()])) - xPos += rt->style(m_firstLine)->font().wordSpacing(); + logicalLeft += rt->style(m_firstLine)->font().wordSpacing(); needsWordSpacing = !isSpaceOrNewline(rt->characters()[text->end()]); } - text->setX(xPos); + text->setLogicalLeft(logicalLeft); int strokeOverflow = static_cast<int>(ceilf(rt->style()->textStrokeWidth() / 2.0f)); // If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is // applied to the right, so this is not an issue with left overflow. int letterSpacing = min(0, (int)rt->style(m_firstLine)->font().letterSpacing()); - rightLayoutOverflow = max(xPos + text->logicalWidth() - letterSpacing, rightLayoutOverflow); + logicalRightLayoutOverflow = max(logicalLeft + text->logicalWidth() - letterSpacing, logicalRightLayoutOverflow); GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(static_cast<InlineTextBox*>(curr)); GlyphOverflow* glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->second.second; - int leftGlyphOverflow = -strokeOverflow - (glyphOverflow ? glyphOverflow->left : 0); - int rightGlyphOverflow = strokeOverflow - letterSpacing + (glyphOverflow ? glyphOverflow->right : 0); + int logicalLeftGlyphOverflow = -strokeOverflow - (glyphOverflow ? glyphOverflow->left : 0); + int logicalRightGlyphOverflow = strokeOverflow - letterSpacing + (glyphOverflow ? glyphOverflow->right : 0); - int childOverflowLeft = leftGlyphOverflow; - int childOverflowRight = rightGlyphOverflow; - for (const ShadowData* shadow = rt->style()->textShadow(); shadow; shadow = shadow->next()) { - childOverflowLeft = min(childOverflowLeft, shadow->x() - shadow->blur() + leftGlyphOverflow); - childOverflowRight = max(childOverflowRight, shadow->x() + shadow->blur() + rightGlyphOverflow); - } - - leftVisualOverflow = min(xPos + childOverflowLeft, leftVisualOverflow); - rightVisualOverflow = max(xPos + text->logicalWidth() + childOverflowRight, rightVisualOverflow); + int childOverflowLogicalLeft = logicalLeftGlyphOverflow; + int childOverflowLogicalRight = logicalRightGlyphOverflow; + int textShadowLogicalLeft; + int textShadowLogicalRight; + rt->style(m_firstLine)->getTextShadowInlineDirectionExtent(textShadowLogicalLeft, textShadowLogicalRight); + childOverflowLogicalLeft = min(childOverflowLogicalLeft, textShadowLogicalLeft); + childOverflowLogicalRight = max(childOverflowLogicalRight, textShadowLogicalRight); + logicalLeftVisualOverflow = min(logicalLeft + childOverflowLogicalLeft, logicalLeftVisualOverflow); + logicalRightVisualOverflow = max(logicalLeft + text->logicalWidth() + childOverflowLogicalRight, logicalRightVisualOverflow); - xPos += text->logicalWidth(); + logicalLeft += text->logicalWidth(); } else { if (curr->renderer()->isPositioned()) { - if (curr->renderer()->parent()->style()->direction() == LTR) - curr->setX(xPos); + if (curr->renderer()->parent()->style()->isLeftToRightDirection()) + curr->setLogicalLeft(logicalLeft); else // Our offset that we cache needs to be from the edge of the right border box and // not the left border box. We have to subtract |x| from the width of the block // (which can be obtained from the root line box). - curr->setX(root()->block()->width() - xPos); + curr->setLogicalLeft(root()->block()->logicalWidth() - logicalLeft); continue; // The positioned object has no effect on the width. } if (curr->renderer()->isRenderInline()) { InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr); - xPos += flow->marginLogicalLeft(); - xPos = flow->placeBoxesInInlineDirection(xPos, needsWordSpacing, textBoxDataMap); - xPos += flow->marginLogicalRight(); - leftLayoutOverflow = min(leftLayoutOverflow, flow->leftLayoutOverflow()); - rightLayoutOverflow = max(rightLayoutOverflow, flow->rightLayoutOverflow()); - leftVisualOverflow = min(leftVisualOverflow, flow->leftVisualOverflow()); - rightVisualOverflow = max(rightVisualOverflow, flow->rightVisualOverflow()); + logicalLeft += flow->marginLogicalLeft(); + logicalLeft = flow->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing, textBoxDataMap); + logicalLeft += flow->marginLogicalRight(); + logicalLeftLayoutOverflow = min(logicalLeftLayoutOverflow, flow->logicalLeftLayoutOverflow()); + logicalRightLayoutOverflow = max(logicalRightLayoutOverflow, flow->logicalRightLayoutOverflow()); + logicalLeftVisualOverflow = min(logicalLeftVisualOverflow, flow->logicalLeftVisualOverflow()); + logicalRightVisualOverflow = max(logicalRightVisualOverflow, flow->logicalRightVisualOverflow()); } else if (!curr->renderer()->isListMarker() || toRenderListMarker(curr->renderer())->isInside()) { - xPos += curr->boxModelObject()->marginLeft(); - curr->setX(xPos); - + // The box can have a different writing-mode than the overall line, so this is a bit complicated. + // Just get all the physical margin and overflow values by hand based off |isVertical|. + int logicalLeftMargin = !isVertical() ? curr->boxModelObject()->marginLeft() : curr->boxModelObject()->marginTop(); + int logicalRightMargin = !isVertical() ? curr->boxModelObject()->marginRight() : curr->boxModelObject()->marginBottom(); + + logicalLeft += logicalLeftMargin; + curr->setLogicalLeft(logicalLeft); + RenderBox* box = toRenderBox(curr->renderer()); - int childLeftOverflow = box->hasOverflowClip() ? 0 : box->leftLayoutOverflow(); - int childRightOverflow = box->hasOverflowClip() ? curr->logicalWidth() : box->rightLayoutOverflow(); + + int childOverflowLogicalLeft = box->hasOverflowClip() ? 0 : (!isVertical() ? box->leftLayoutOverflow() : box->topLayoutOverflow()); + int childOverflowLogicalRight = box->hasOverflowClip() ? curr->logicalWidth() : (!isVertical() ? box->rightLayoutOverflow() : box->bottomLayoutOverflow()); - leftLayoutOverflow = min(xPos + childLeftOverflow, leftLayoutOverflow); - rightLayoutOverflow = max(xPos + childRightOverflow, rightLayoutOverflow); + logicalLeftLayoutOverflow = min(logicalLeft + childOverflowLogicalLeft, logicalLeftLayoutOverflow); + logicalRightLayoutOverflow = max(logicalLeft + childOverflowLogicalRight, logicalRightLayoutOverflow); - leftVisualOverflow = min(xPos + box->leftVisualOverflow(), leftVisualOverflow); - rightVisualOverflow = max(xPos + box->rightVisualOverflow(), rightVisualOverflow); + logicalLeftVisualOverflow = min(logicalLeft + (!isVertical() ? box->leftVisualOverflow() : box->topVisualOverflow()), logicalLeftVisualOverflow); + logicalRightVisualOverflow = max(logicalLeft + (!isVertical() ? box->rightVisualOverflow() : box->bottomVisualOverflow()), logicalRightVisualOverflow); - xPos += curr->logicalWidth() + curr->boxModelObject()->marginRight(); + logicalLeft += curr->logicalWidth() + logicalRightMargin; } } } - xPos += borderLogicalRight() + paddingLogicalRight(); - setLogicalWidth(xPos - startX); - rightVisualOverflow = max(x() + logicalWidth() + boxShadowRight, rightVisualOverflow); - rightLayoutOverflow = max(x() + logicalWidth(), rightLayoutOverflow); + logicalLeft += borderLogicalRight() + paddingLogicalRight(); + setLogicalWidth(logicalLeft - startLogicalLeft); + logicalRightVisualOverflow = max(logicalLeft + boxShadowLogicalRight, logicalRightVisualOverflow); + logicalRightLayoutOverflow = max(logicalLeft, logicalRightLayoutOverflow); - setInlineDirectionOverflowPositions(leftLayoutOverflow, rightLayoutOverflow, leftVisualOverflow, rightVisualOverflow); - return xPos; + setInlineDirectionOverflowPositions(logicalLeftLayoutOverflow, logicalRightLayoutOverflow, logicalLeftVisualOverflow, logicalRightVisualOverflow); + return logicalLeft; } void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, @@ -363,9 +367,9 @@ void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, // positioned elements if (curr->renderer()->isPositioned()) continue; // Positioned placeholders don't affect calculations. - if (curr->y() == PositionTop || curr->y() == PositionBottom) { - int lineHeight = curr->lineHeight(false); - if (curr->y() == PositionTop) { + if (curr->logicalTop() == PositionTop || curr->logicalTop() == PositionBottom) { + int lineHeight = curr->lineHeight(); + if (curr->logicalTop() == PositionTop) { if (maxAscent + maxDescent < lineHeight) maxDescent = lineHeight - maxAscent; } @@ -386,7 +390,7 @@ void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, static int verticalPositionForBox(InlineBox* curr, bool firstLine) { if (curr->renderer()->isText()) - return curr->parent()->y(); + return curr->parent()->logicalTop(); if (curr->renderer()->isBox()) return toRenderBox(curr->renderer())->verticalPosition(firstLine); return toRenderInline(curr->renderer())->verticalPositionFromCache(firstLine); @@ -397,8 +401,8 @@ void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositi { if (isRootInlineBox()) { // Examine our root box. - int height = lineHeight(true); - int baseline = baselinePosition(true); + int height = lineHeight(); + int baseline = baselinePosition(); if (hasTextChildren() || strictMode) { int ascent = baseline; int descent = height - ascent; @@ -451,19 +455,19 @@ void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositi } } } else { - lineHeight = curr->lineHeight(false); - baseline = curr->baselinePosition(false); + lineHeight = curr->lineHeight(); + baseline = curr->baselinePosition(); } - curr->setY(verticalPositionForBox(curr, m_firstLine)); - if (curr->y() == PositionTop) { + curr->setLogicalTop(verticalPositionForBox(curr, m_firstLine)); + if (curr->logicalTop() == PositionTop) { if (maxPositionTop < lineHeight) maxPositionTop = lineHeight; - } else if (curr->y() == PositionBottom) { + } else if (curr->logicalTop() == PositionBottom) { if (maxPositionBottom < lineHeight) maxPositionBottom = lineHeight; - } else if ((!isInlineFlow || static_cast<InlineFlowBox*>(curr)->hasTextChildren()) || curr->boxModelObject()->hasHorizontalBordersOrPadding() || strictMode) { - int ascent = baseline - curr->y(); + } else if ((!isInlineFlow || static_cast<InlineFlowBox*>(curr)->hasTextChildren()) || curr->boxModelObject()->hasInlineDirectionBordersOrPadding() || strictMode) { + int ascent = baseline - curr->logicalTop(); int descent = lineHeight - ascent; if (maxAscent < ascent) maxAscent = ascent; @@ -476,10 +480,10 @@ void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositi } } -void InlineFlowBox::placeBoxesInBlockDirection(int yPos, int maxHeight, int maxAscent, bool strictMode, int& selectionTop, int& selectionBottom) +void InlineFlowBox::placeBoxesInBlockDirection(int top, int maxHeight, int maxAscent, bool strictMode, int& lineTop, int& lineBottom) { if (isRootInlineBox()) - setY(yPos + maxAscent - baselinePosition(true)); // Place our root box. + setLogicalTop(top + maxAscent - baselinePosition()); // Place our root box. for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { if (curr->renderer()->isPositioned()) @@ -489,47 +493,70 @@ void InlineFlowBox::placeBoxesInBlockDirection(int yPos, int maxHeight, int maxA // line-height). bool isInlineFlow = curr->isInlineFlowBox(); if (isInlineFlow) - static_cast<InlineFlowBox*>(curr)->placeBoxesInBlockDirection(yPos, maxHeight, maxAscent, strictMode, selectionTop, selectionBottom); + static_cast<InlineFlowBox*>(curr)->placeBoxesInBlockDirection(top, maxHeight, maxAscent, strictMode, lineTop, lineBottom); bool childAffectsTopBottomPos = true; - if (curr->y() == PositionTop) - curr->setY(yPos); - else if (curr->y() == PositionBottom) - curr->setY(yPos + maxHeight - curr->lineHeight(false)); + if (curr->logicalTop() == PositionTop) + curr->setLogicalTop(top); + else if (curr->logicalTop() == PositionBottom) + curr->setLogicalTop(top + maxHeight - curr->lineHeight()); else { - if ((isInlineFlow && !static_cast<InlineFlowBox*>(curr)->hasTextChildren()) && !curr->boxModelObject()->hasHorizontalBordersOrPadding() && !strictMode) + if ((isInlineFlow && !static_cast<InlineFlowBox*>(curr)->hasTextChildren()) && !curr->boxModelObject()->hasInlineDirectionBordersOrPadding() && !strictMode) childAffectsTopBottomPos = false; - int posAdjust = maxAscent - curr->baselinePosition(false); - curr->setY(curr->y() + yPos + posAdjust); + int posAdjust = maxAscent - curr->baselinePosition(); + curr->setLogicalTop(curr->logicalTop() + top + posAdjust); } - int newY = curr->y(); + int newLogicalTop = curr->logicalTop(); if (curr->isText() || curr->isInlineFlowBox()) { const Font& font = curr->renderer()->style(m_firstLine)->font(); - newY += curr->baselinePosition(false) - font.ascent(); - if (curr->isInlineFlowBox()) - newY -= curr->boxModelObject()->borderTop() + curr->boxModelObject()->paddingTop(); + newLogicalTop += curr->baselinePosition() - font.ascent(); + if (curr->isInlineFlowBox()) { + RenderBoxModelObject* boxObject = toRenderBoxModelObject(curr->renderer()); + newLogicalTop -= boxObject->style(m_firstLine)->isHorizontalWritingMode() ? boxObject->borderTop() + boxObject->paddingTop() : + boxObject->borderRight() + boxObject->paddingRight(); + } } else if (!curr->renderer()->isBR()) { RenderBox* box = toRenderBox(curr->renderer()); - newY += box->marginTop(); + newLogicalTop += box->style(m_firstLine)->isHorizontalWritingMode() ? box->marginTop() : box->marginRight(); } - curr->setY(newY); + curr->setLogicalTop(newLogicalTop); if (childAffectsTopBottomPos) { int boxHeight = curr->logicalHeight(); - selectionTop = min(selectionTop, newY); - selectionBottom = max(selectionBottom, newY + boxHeight); + lineTop = min(lineTop, newLogicalTop); + lineBottom = max(lineBottom, newLogicalTop + boxHeight); } } if (isRootInlineBox()) { const Font& font = renderer()->style(m_firstLine)->font(); - setY(y() + baselinePosition(true) - font.ascent()); + setLogicalTop(logicalTop() + baselinePosition() - font.ascent()); + if (hasTextChildren() || strictMode) { - selectionTop = min(selectionTop, y()); - selectionBottom = max(selectionBottom, y() + logicalHeight()); + lineTop = min(lineTop, logicalTop()); + lineBottom = max(lineBottom, logicalTop() + logicalHeight()); } + + if (renderer()->style()->isFlippedLinesWritingMode()) + flipLinesInBlockDirection(lineTop, lineBottom); + } +} + +void InlineFlowBox::flipLinesInBlockDirection(int lineTop, int lineBottom) +{ + // Flip the box on the line such that the top is now relative to the lineBottom instead of the lineTop. + setLogicalTop(lineBottom - (logicalTop() - lineTop) - logicalHeight()); + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders aren't affected here. + + if (curr->isInlineFlowBox()) + static_cast<InlineFlowBox*>(curr)->flipLinesInBlockDirection(lineTop, lineBottom); + else + curr->setLogicalTop(lineBottom - (curr->logicalTop() - lineTop) - curr->logicalHeight()); } } @@ -612,7 +639,7 @@ bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re { IntRect overflowRect(visibleOverflowRect()); overflowRect.move(tx, ty); - if (!overflowRect.intersects(result.rectFromPoint(x, y))) + if (!overflowRect.intersects(result.rectForPoint(x, y))) return false; // Check children first. @@ -625,7 +652,7 @@ bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re // Now check ourselves. IntRect rect(tx + m_x, ty + m_y, m_logicalWidth, logicalHeight()); - if (visibleToHitTesting() && rect.intersects(result.rectFromPoint(x, y))) { + if (visibleToHitTesting() && rect.intersects(result.rectForPoint(x, y))) { renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); // Don't add in m_x or m_y here, we want coords in the containing block's space. if (!result.addNodeToRectBasedTestResult(renderer()->node(), x, y, rect)) return true; @@ -758,16 +785,18 @@ void InlineFlowBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) int x = m_x; int y = m_y; - int w = logicalWidth(); - int h = logicalHeight(); + int w = m_isVertical ? logicalHeight() : logicalWidth(); + int h = m_isVertical ? logicalWidth() : logicalHeight(); // Constrain our background/border painting to the line top and bottom if necessary. bool noQuirksMode = renderer()->document()->inNoQuirksMode(); if (!hasTextChildren() && !noQuirksMode) { RootInlineBox* rootBox = root(); - int bottom = min(rootBox->lineBottom(), y + h); - y = max(rootBox->lineTop(), y); - h = bottom - y; + int& top = m_isVertical ? x : y; + int& logicalHeight = m_isVertical ? w : h; + int bottom = min(rootBox->lineBottom(), top + logicalHeight); + top = max(rootBox->lineTop(), top); + logicalHeight = bottom - top; } // Move x/y to our coordinates. @@ -834,16 +863,18 @@ void InlineFlowBox::paintMask(PaintInfo& paintInfo, int tx, int ty) int x = m_x; int y = m_y; - int w = logicalWidth(); - int h = logicalHeight(); + int w = m_isVertical ? logicalHeight() : logicalWidth(); + int h = m_isVertical ? logicalWidth() : logicalHeight(); // Constrain our background/border painting to the line top and bottom if necessary. bool noQuirksMode = renderer()->document()->inNoQuirksMode(); if (!hasTextChildren() && !noQuirksMode) { RootInlineBox* rootBox = root(); - int bottom = min(rootBox->lineBottom(), y + h); - y = max(rootBox->lineTop(), y); - h = bottom - y; + int& top = m_isVertical ? x : y; + int& logicalHeight = m_isVertical ? w : h; + int bottom = min(rootBox->lineBottom(), top + logicalHeight); + top = max(rootBox->lineTop(), top); + logicalHeight = bottom - top; } // Move x/y to our coordinates. @@ -945,7 +976,7 @@ void InlineFlowBox::paintTextDecorations(PaintInfo& paintInfo, int tx, int ty, b if (rootLine->ellipsisBox()) { int ellipsisX = m_x + rootLine->ellipsisBox()->x(); int ellipsisWidth = rootLine->ellipsisBox()->logicalWidth(); - bool ltr = renderer()->style()->direction() == LTR; + bool ltr = renderer()->style()->isLeftToRightDirection(); if (rootLine == this) { // Trim w and x so that the underline isn't drawn underneath the ellipsis. // ltr: is our right edge farther right than the right edge of the ellipsis. diff --git a/WebCore/rendering/InlineFlowBox.h b/WebCore/rendering/InlineFlowBox.h index 2d57cca..ee16a0f 100644 --- a/WebCore/rendering/InlineFlowBox.h +++ b/WebCore/rendering/InlineFlowBox.h @@ -154,12 +154,13 @@ public: void determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject); int getFlowSpacingLogicalWidth(); bool onEndChain(RenderObject* endObject); - int placeBoxesInInlineDirection(int x, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); + int placeBoxesInInlineDirection(int logicalLeft, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); void computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, int& maxAscent, int& maxDescent, bool strictMode, GlyphOverflowAndFallbackFontsMap&); void adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, int maxPositionTop, int maxPositionBottom); - void placeBoxesInBlockDirection(int y, int maxHeight, int maxAscent, bool strictMode, int& lineTop, int& lineBottom); + void placeBoxesInBlockDirection(int logicalTop, int maxHeight, int maxAscent, bool strictMode, int& lineTop, int& lineBottom); + void flipLinesInBlockDirection(int lineTop, int lineBottom); void computeBlockDirectionOverflow(int lineTop, int lineBottom, bool strictMode, GlyphOverflowAndFallbackFontsMap&); void removeChild(InlineBox* child); @@ -185,15 +186,21 @@ public: int leftLayoutOverflow() const { return m_overflow ? m_overflow->leftLayoutOverflow() : m_x; } int rightLayoutOverflow() const { return m_overflow ? m_overflow->rightLayoutOverflow() : m_x + m_logicalWidth; } IntRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : IntRect(m_x, m_y, m_logicalWidth, logicalHeight()); } - + int logicalLeftLayoutOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? leftLayoutOverflow() : topLayoutOverflow(); } + int logicalRightLayoutOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? rightLayoutOverflow() : bottomLayoutOverflow(); } + int topVisualOverflow() const { return m_overflow ? m_overflow->topVisualOverflow() : m_y; } int bottomVisualOverflow() const { return m_overflow ? m_overflow->bottomVisualOverflow() : m_y + logicalHeight(); } int leftVisualOverflow() const { return m_overflow ? m_overflow->leftVisualOverflow() : m_x; } int rightVisualOverflow() const { return m_overflow ? m_overflow->rightVisualOverflow() : m_x + m_logicalWidth; } IntRect visualOverflowRect() const { return m_overflow ? m_overflow->visualOverflowRect() : IntRect(m_x, m_y, m_logicalWidth, logicalHeight()); } - - void setInlineDirectionOverflowPositions(int leftLayoutOverflow, int rightLayoutOverflow, int leftVisualOverflow, int rightVisualOverflow); - void setBlockDirectionOverflowPositions(int topLayoutOverflow, int bottomLayoutOverflow, int topVisualOverflow, int bottomVisualOverflow, int boxHeight); + int logicalLeftVisualOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? leftVisualOverflow() : topVisualOverflow(); } + int logicalRightVisualOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? rightVisualOverflow() : bottomVisualOverflow(); } + + void setInlineDirectionOverflowPositions(int logicalLeftLayoutOverflow, int logicalRightLayoutOverflow, + int logicalLeftVisualOverflow, int logicalRightVisualOverflow); + void setBlockDirectionOverflowPositions(int logicalTopLayoutOverflow, int logicalBottomLayoutOverflow, + int logicalTopVisualOverflow, int logicalBottomVisualOverflow, int boxLogicalHeight); protected: OwnPtr<RenderOverflow> m_overflow; @@ -215,18 +222,31 @@ protected: #endif }; -inline void InlineFlowBox::setInlineDirectionOverflowPositions(int leftLayoutOverflow, int rightLayoutOverflow, int leftVisualOverflow, int rightVisualOverflow) +inline void InlineFlowBox::setInlineDirectionOverflowPositions(int logicalLeftLayoutOverflow, int logicalRightLayoutOverflow, + int logicalLeftVisualOverflow, int logicalRightVisualOverflow) { if (!m_overflow) { - if (leftLayoutOverflow == m_x && rightLayoutOverflow == m_x + m_logicalWidth && leftVisualOverflow == m_x && rightVisualOverflow == m_x + m_logicalWidth) + if (logicalLeftLayoutOverflow == logicalLeft() && logicalRightLayoutOverflow == logicalRight() + && logicalLeftVisualOverflow == logicalLeft() && logicalRightVisualOverflow == logicalRight()) return; - m_overflow = adoptPtr(new RenderOverflow(IntRect(m_x, m_y, m_logicalWidth, m_renderer->style(m_firstLine)->font().height()))); + + int width = isVertical() ? m_renderer->style(m_firstLine)->font().height() : logicalWidth(); + int height = isVertical() ? logicalWidth() : m_renderer->style(m_firstLine)->font().height(); + + m_overflow = adoptPtr(new RenderOverflow(IntRect(m_x, m_y, width, height))); } - m_overflow->setLeftLayoutOverflow(leftLayoutOverflow); - m_overflow->setRightLayoutOverflow(rightLayoutOverflow); - m_overflow->setLeftVisualOverflow(leftVisualOverflow); - m_overflow->setRightVisualOverflow(rightVisualOverflow); + if (isVertical()) { + m_overflow->setTopLayoutOverflow(logicalLeftLayoutOverflow); + m_overflow->setBottomLayoutOverflow(logicalRightLayoutOverflow); + m_overflow->setTopVisualOverflow(logicalLeftVisualOverflow); + m_overflow->setBottomVisualOverflow(logicalRightVisualOverflow); + } else { + m_overflow->setLeftLayoutOverflow(logicalLeftLayoutOverflow); + m_overflow->setRightLayoutOverflow(logicalRightLayoutOverflow); + m_overflow->setLeftVisualOverflow(logicalLeftVisualOverflow); + m_overflow->setRightVisualOverflow(logicalRightVisualOverflow); + } } inline void InlineFlowBox::setBlockDirectionOverflowPositions(int topLayoutOverflow, int bottomLayoutOverflow, int topVisualOverflow, int bottomVisualOverflow, int boxHeight) diff --git a/WebCore/rendering/InlineIterator.h b/WebCore/rendering/InlineIterator.h index 9310ea8..270364f 100644 --- a/WebCore/rendering/InlineIterator.h +++ b/WebCore/rendering/InlineIterator.h @@ -53,7 +53,7 @@ public: bool atEnd() const; UChar current() const; - WTF::Unicode::Direction direction() const; + ALWAYS_INLINE WTF::Unicode::Direction direction() const; RenderBlock* block; RenderObject* obj; @@ -220,7 +220,7 @@ ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const return WTF::Unicode::direction(c); if (obj && obj->isListMarker()) - return obj->style()->direction() == LTR ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; + return obj->style()->isLeftToRightDirection() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; return WTF::Unicode::OtherNeutral; } diff --git a/WebCore/rendering/InlineTextBox.cpp b/WebCore/rendering/InlineTextBox.cpp index 68bff3c..cf100e0 100644 --- a/WebCore/rendering/InlineTextBox.cpp +++ b/WebCore/rendering/InlineTextBox.cpp @@ -43,6 +43,22 @@ using namespace std; namespace WebCore { +int InlineTextBox::baselinePosition() const +{ + if (!isText() || !parent()) + return 0; + return parent()->baselinePosition(); +} + +int InlineTextBox::lineHeight() const +{ + if (!isText() || !parent()) + return 0; + if (m_renderer->isBR()) + return toRenderBR(m_renderer)->lineHeight(m_firstLine); + return parent()->lineHeight(); +} + int InlineTextBox::selectionTop() { return root()->selectionTop(); @@ -137,7 +153,7 @@ IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos) ePos = len; } - IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(characters, len, textObj->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride), + IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(characters, len, textObj->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride), IntPoint(tx + m_x, ty + selTop), selHeight, sPos, ePos)); if (r.x() > tx + m_x + m_logicalWidth) r.setWidth(0); @@ -198,7 +214,7 @@ int InlineTextBox::placeEllipsisBox(bool flowIsLTR, int visibleLeftEdge, int vis // The inline box may have different directionality than it's parent. Since truncation // behavior depends both on both the parent and the inline block's directionality, we // must keep track of these separately. - bool ltr = direction() == LTR; + bool ltr = isLeftToRightDirection(); if (ltr != flowIsLTR) { // Width in pixels of the visible portion of the box, excluding the ellipsis. int visibleBoxWidth = visibleRightEdge - visibleLeftEdge - ellipsisWidth; @@ -287,7 +303,7 @@ bool InlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, in return false; IntRect rect(tx + m_x, ty + m_y, m_logicalWidth, logicalHeight()); - if (m_truncation != cFullTruncation && visibleToHitTesting() && rect.intersects(result.rectFromPoint(x, y))) { + if (m_truncation != cFullTruncation && visibleToHitTesting() && rect.intersects(result.rectForPoint(x, y))) { renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); if (!result.addNodeToRectBasedTestResult(renderer()->node(), x, y, rect)) return true; @@ -382,8 +398,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) return; if (m_truncation != cNoTruncation) { - TextDirection flowDirection = renderer()->containingBlock()->style()->direction(); - if (flowDirection != direction()) { + if (renderer()->containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection()) { // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin // at which we start drawing text. // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is: @@ -395,7 +410,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) int widthOfVisibleText = toRenderText(renderer())->width(m_start, m_truncation, textPos(), m_firstLine); int widthOfHiddenText = m_logicalWidth - widthOfVisibleText; // FIXME: The hit testing logic also needs to take this translation int account. - tx += direction() == LTR ? widthOfHiddenText : -widthOfHiddenText; + tx += isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText; } } @@ -504,7 +519,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) int baseline = renderer()->style(m_firstLine)->font().ascent(); IntPoint textOrigin(m_x + tx, m_y + ty + baseline); - TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()); + TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || styleToUse->visuallyOrdered()); int sPos = 0; int ePos = 0; @@ -635,7 +650,7 @@ void InlineTextBox::paintSelection(GraphicsContext* context, int tx, int ty, Ren context->clip(IntRect(m_x + tx, y + ty, m_logicalWidth, h)); context->drawHighlightForText(font, TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd, - direction() == RTL, m_dirOverride || style->visuallyOrdered()), + !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), IntPoint(m_x + tx, y + ty), h, c, style->colorSpace(), sPos, ePos); context->restore(); } @@ -658,7 +673,7 @@ void InlineTextBox::paintCompositionBackground(GraphicsContext* context, int tx, int y = selectionTop(); int h = selectionHeight(); context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, - direction() == RTL, m_dirOverride || style->visuallyOrdered()), + !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), IntPoint(m_x + tx, y + ty), h, c, style->colorSpace(), sPos, ePos); context->restore(); } @@ -694,7 +709,7 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, in int width = m_logicalWidth; if (m_truncation != cNoTruncation) { width = toRenderText(renderer())->width(m_start, m_truncation, textPos(), m_firstLine); - if (direction() == RTL) + if (!isLeftToRightDirection()) tx += (m_logicalWidth - width); } @@ -813,7 +828,7 @@ void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, in // Calculate start & width IntPoint startPoint(tx + m_x, ty + selectionTop()); - TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); int h = selectionHeight(); IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, startPosition, endPosition)); @@ -858,7 +873,7 @@ void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, co int sPos = max(marker.startOffset - m_start, (unsigned)0); int ePos = min(marker.endOffset - m_start, (unsigned)m_len); - TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates. IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(m_x, y), h, sPos, ePos)); @@ -886,7 +901,7 @@ void InlineTextBox::computeRectForReplacementMarker(int /*tx*/, int /*ty*/, cons int sPos = max(marker.startOffset - m_start, (unsigned)0); int ePos = min(marker.endOffset - m_start, (unsigned)m_len); - TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); IntPoint startPoint = IntPoint(m_x, y); // Compute and store the rect associated with this marker. @@ -1031,7 +1046,7 @@ int InlineTextBox::textPos() const return 0; RenderBlock* blockElement = renderer()->containingBlock(); - return direction() == RTL ? x() - blockElement->borderRight() - blockElement->paddingRight() + return !isLeftToRightDirection() ? x() - blockElement->borderRight() - blockElement->paddingRight() : x() - blockElement->borderLeft() - blockElement->paddingLeft(); } @@ -1043,7 +1058,7 @@ int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const RenderText* text = toRenderText(renderer()); RenderStyle* style = text->style(m_firstLine); const Font* f = &style->font(); - return f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), + return f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), _x - m_x, includePartialGlyphs); } @@ -1057,10 +1072,10 @@ int InlineTextBox::positionForOffset(int offset) const RenderText* text = toRenderText(renderer()); const Font& f = text->style(m_firstLine)->font(); - int from = direction() == RTL ? offset - m_start : 0; - int to = direction() == RTL ? m_len : offset - m_start; + int from = !isLeftToRightDirection() ? offset - m_start : 0; + int to = !isLeftToRightDirection() ? m_len : offset - m_start; // FIXME: Do we need to add rightBearing here? - return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride), + return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride), IntPoint(m_x, 0), 0, from, to)).right(); } diff --git a/WebCore/rendering/InlineTextBox.h b/WebCore/rendering/InlineTextBox.h index 2bf099b..f010c39 100644 --- a/WebCore/rendering/InlineTextBox.h +++ b/WebCore/rendering/InlineTextBox.h @@ -69,6 +69,10 @@ public: void setHasHyphen(bool hasHyphen) { m_hasEllipsisBoxOrHyphen = hasHyphen; } static inline bool compareByStart(const InlineTextBox* first, const InlineTextBox* second) { return first->start() < second->start(); } + + virtual int baselinePosition() const; + virtual int lineHeight() const; + private: virtual int selectionTop(); virtual int selectionHeight(); @@ -78,7 +82,7 @@ public: virtual IntRect selectionRect(int absx, int absy, int startPos, int endPos); bool isSelected(int startPos, int endPos) const; - virtual void selectionStartEnd(int& sPos, int& ePos); + void selectionStartEnd(int& sPos, int& ePos); protected: virtual void paint(PaintInfo&, int tx, int ty); diff --git a/WebCore/rendering/LayoutState.cpp b/WebCore/rendering/LayoutState.cpp index 0d81b15..3eea51f 100644 --- a/WebCore/rendering/LayoutState.cpp +++ b/WebCore/rendering/LayoutState.cpp @@ -34,7 +34,7 @@ namespace WebCore { -LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const IntSize& offset, int pageHeight, ColumnInfo* columnInfo) +LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const IntSize& offset, int pageHeight, bool pageHeightChanged, ColumnInfo* columnInfo) : m_columnInfo(columnInfo) , m_next(prev) #ifndef NDEBUG @@ -86,9 +86,11 @@ LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const IntSize& m_pageHeight = pageHeight; m_pageOffset = IntSize(m_layoutOffset.width() + renderer->borderLeft() + renderer->paddingLeft(), m_layoutOffset.height() + renderer->borderTop() + renderer->paddingTop()); + m_pageHeightChanged = pageHeightChanged; } else { // If we don't establish a new page height, then propagate the old page height and offset down. m_pageHeight = m_next->m_pageHeight; + m_pageHeightChanged = m_next->m_pageHeightChanged; m_pageOffset = m_next->m_pageOffset; // Disable pagination for objects we don't support. For now this includes overflow:scroll/auto and inline blocks. @@ -107,6 +109,7 @@ LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const IntSize& LayoutState::LayoutState(RenderObject* root) : m_clipped(false) , m_pageHeight(0) + , m_pageHeightChanged(false) , m_columnInfo(0) , m_next(0) #ifndef NDEBUG diff --git a/WebCore/rendering/LayoutState.h b/WebCore/rendering/LayoutState.h index ca4cbfb..cca3e42 100644 --- a/WebCore/rendering/LayoutState.h +++ b/WebCore/rendering/LayoutState.h @@ -42,6 +42,7 @@ public: LayoutState() : m_clipped(false) , m_pageHeight(0) + , m_pageHeightChanged(false) , m_columnInfo(0) , m_next(0) #ifndef NDEBUG @@ -50,7 +51,7 @@ public: { } - LayoutState(LayoutState*, RenderBox*, const IntSize& offset, int pageHeight, ColumnInfo*); + LayoutState(LayoutState*, RenderBox*, const IntSize& offset, int pageHeight, bool pageHeightChanged, ColumnInfo*); LayoutState(RenderObject*); void destroy(RenderArena*); @@ -67,6 +68,9 @@ public: int pageY(int childY) const; void addForcedColumnBreak(int childY); + bool pageHeight() const { return m_pageHeight; } + bool pageHeightChanged() const { return m_pageHeightChanged; } + private: // The normal operator new is disallowed. void* operator new(size_t) throw(); @@ -81,6 +85,7 @@ public: // This is a total delta accumulated from the root. int m_pageHeight; // The current page height for the pagination model that encloses us. + bool m_pageHeightChanged; // If our page height has changed, this will force all blocks to relayout. IntSize m_pageOffset; // The offset of the start of the first page in the nearest enclosing pagination model. ColumnInfo* m_columnInfo; // If the enclosing pagination model is a column model, then this will store column information for easy retrieval/manipulation. diff --git a/WebCore/rendering/RenderApplet.cpp b/WebCore/rendering/RenderApplet.cpp index 63a18a0..f9eb56e 100644 --- a/WebCore/rendering/RenderApplet.cpp +++ b/WebCore/rendering/RenderApplet.cpp @@ -39,6 +39,10 @@ RenderApplet::RenderApplet(HTMLAppletElement* applet, const HashMap<String, Stri setInline(true); } +RenderApplet::~RenderApplet() +{ +} + IntSize RenderApplet::intrinsicSize() const { // FIXME: This doesn't make sense. We can't just start returning diff --git a/WebCore/rendering/RenderApplet.h b/WebCore/rendering/RenderApplet.h index 62f46cd..641568d 100644 --- a/WebCore/rendering/RenderApplet.h +++ b/WebCore/rendering/RenderApplet.h @@ -32,6 +32,7 @@ namespace WebCore { class RenderApplet : public RenderWidget { public: RenderApplet(HTMLAppletElement*, const HashMap<String, String>& args); + virtual ~RenderApplet(); void createWidgetIfNecessary(); diff --git a/WebCore/rendering/RenderBR.cpp b/WebCore/rendering/RenderBR.cpp index 340d6b7..fb136a4 100644 --- a/WebCore/rendering/RenderBR.cpp +++ b/WebCore/rendering/RenderBR.cpp @@ -38,36 +38,17 @@ RenderBR::~RenderBR() { } -int RenderBR::baselinePosition(bool firstLine, bool isRootLineBox) const +int RenderBR::lineHeight(bool firstLine) const { - if (firstTextBox() && !firstTextBox()->isText()) - return 0; - return RenderText::baselinePosition(firstLine, isRootLineBox); -} - -int RenderBR::lineHeight(bool firstLine, bool /*isRootLineBox*/) const -{ - if (firstTextBox() && !firstTextBox()->isText()) - return 0; - - if (firstLine) { + if (firstLine && document()->usesFirstLineRules()) { RenderStyle* s = style(firstLine); - Length lh = s->lineHeight(); - if (lh.isNegative()) { - if (s == style()) { - if (m_lineHeight == -1) - m_lineHeight = RenderObject::lineHeight(false); - return m_lineHeight; - } - return s->font().lineSpacing(); - } - if (lh.isPercent()) - return lh.calcMinValue(s->fontSize()); - return lh.value(); + if (s != style()) + return s->computedLineHeight(); } - + if (m_lineHeight == -1) - m_lineHeight = RenderObject::lineHeight(false); + m_lineHeight = style()->computedLineHeight(); + return m_lineHeight; } diff --git a/WebCore/rendering/RenderBR.h b/WebCore/rendering/RenderBR.h index 8850d46..7216b5a 100644 --- a/WebCore/rendering/RenderBR.h +++ b/WebCore/rendering/RenderBR.h @@ -43,8 +43,7 @@ public: virtual unsigned width(unsigned /*from*/, unsigned /*len*/, const Font&, int /*xpos*/) const { return 0; } virtual unsigned width(unsigned /*from*/, unsigned /*len*/, int /*xpos*/, bool /*firstLine = false*/) const { return 0; } - virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; - virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; + int lineHeight(bool firstLine) const; // overrides virtual bool isBR() const { return true; } @@ -62,6 +61,22 @@ private: mutable int m_lineHeight; }; + +inline RenderBR* toRenderBR(RenderObject* object) +{ + ASSERT(!object || object->isBR()); + return static_cast<RenderBR*>(object); +} + +inline const RenderBR* toRenderBR(const RenderObject* object) +{ + ASSERT(!object || object->isBR()); + return static_cast<const RenderBR*>(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderBR(const RenderBR*); + } // namespace WebCore #endif // RenderBR_h diff --git a/WebCore/rendering/RenderBlock.cpp b/WebCore/rendering/RenderBlock.cpp index 1e378d8..d951277 100644 --- a/WebCore/rendering/RenderBlock.cpp +++ b/WebCore/rendering/RenderBlock.cpp @@ -94,7 +94,7 @@ RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int beforeBorderPadding, // we're positioned, floating, a table cell. m_canCollapseWithChildren = !block->isRenderView() && !block->isRoot() && !block->isPositioned() && !block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable() - && !block->isBlockFlowRoot(); + && !block->isWritingModeRoot(); m_canCollapseMarginBeforeWithChildren = m_canCollapseWithChildren && (beforeBorderPadding == 0) && block->style()->marginBeforeCollapse() != MSEPARATE; @@ -108,8 +108,8 @@ RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int beforeBorderPadding, m_quirkContainer = block->isTableCell() || block->isBody() || block->style()->marginBeforeCollapse() == MDISCARD || block->style()->marginAfterCollapse() == MDISCARD; - m_posMargin = m_canCollapseMarginBeforeWithChildren ? block->maxMarginBefore(RenderBox::PositiveMargin) : 0; - m_negMargin = m_canCollapseMarginBeforeWithChildren ? block->maxMarginBefore(RenderBox::NegativeMargin) : 0; + m_positiveMargin = m_canCollapseMarginBeforeWithChildren ? block->maxPositiveMarginBefore() : 0; + m_negativeMargin = m_canCollapseMarginBeforeWithChildren ? block->maxNegativeMarginBefore() : 0; } // ------------------------------------------------------------------------------------------------------- @@ -1023,24 +1023,25 @@ bool RenderBlock::isSelfCollapsingBlock() const // (c) have border/padding, // (d) have a min-height // (e) have specified that one of our margins can't collapse using a CSS extension - if (height() > 0 - || isTable() || borderAndPaddingHeight() - || style()->minHeight().isPositive() + if (logicalHeight() > 0 + || isTable() || borderAndPaddingLogicalHeight() + || style()->logicalMinHeight().isPositive() || style()->marginBeforeCollapse() == MSEPARATE || style()->marginAfterCollapse() == MSEPARATE) return false; - bool hasAutoHeight = style()->height().isAuto(); - if (style()->height().isPercent() && !document()->inQuirksMode()) { + Length logicalHeightLength = style()->logicalHeight(); + bool hasAutoHeight = logicalHeightLength.isAuto(); + if (logicalHeightLength.isPercent() && !document()->inQuirksMode()) { hasAutoHeight = true; for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) { - if (cb->style()->height().isFixed() || cb->isTableCell()) + if (cb->style()->logicalHeight().isFixed() || cb->isTableCell()) hasAutoHeight = false; } } // If the height is 0 or auto, then whether or not we are a self-collapsing block depends // on whether we have content that is all self-collapsing or not. - if (hasAutoHeight || ((style()->height().isFixed() || style()->height().isPercent()) && style()->height().isZero())) { + if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercent()) && logicalHeightLength.isZero())) { // If the block has inline children, see if we generated any line boxes. If we have any // line boxes, then we can't be self-collapsing, since we have content. if (childrenInline()) @@ -1125,7 +1126,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren, int pageHeight) LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout()); - int oldWidth = width(); + int oldWidth = logicalWidth(); int oldColumnWidth = desiredColumnWidth(); computeLogicalWidth(); @@ -1145,6 +1146,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren, int pageHeight) int previousHeight = logicalHeight(); setLogicalHeight(0); bool hasSpecifiedPageHeight = false; + bool pageHeightChanged = false; ColumnInfo* colInfo = columnInfo(); if (hasColumns()) { if (!pageHeight) { @@ -1160,14 +1162,14 @@ void RenderBlock::layoutBlock(bool relayoutChildren, int pageHeight) } if (colInfo->columnHeight() != pageHeight && m_everHadLayout) { colInfo->setColumnHeight(pageHeight); - markDescendantBlocksAndLinesForLayout(); // We need to dirty all descendant blocks and lines, since the column height is different now. + pageHeightChanged = true; } if (!hasSpecifiedPageHeight && !pageHeight) colInfo->clearForcedBreaks(); } - LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection(), pageHeight, colInfo); + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection(), pageHeight, pageHeightChanged, colInfo); // We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg, to track // our current maximal positive and negative margins. These values are used when we @@ -1204,20 +1206,20 @@ void RenderBlock::layoutBlock(bool relayoutChildren, int pageHeight) layer()->setHasVerticalScrollbar(true); } - int repaintTop = 0; - int repaintBottom = 0; - int maxFloatBottom = 0; + int repaintLogicalTop = 0; + int repaintLogicalBottom = 0; + int maxFloatLogicalBottom = 0; if (!firstChild() && !isAnonymousBlock()) setChildrenInline(true); if (childrenInline()) - layoutInlineChildren(relayoutChildren, repaintTop, repaintBottom); + layoutInlineChildren(relayoutChildren, repaintLogicalTop, repaintLogicalBottom); else - layoutBlockChildren(relayoutChildren, maxFloatBottom); + layoutBlockChildren(relayoutChildren, maxFloatLogicalBottom); // Expand our intrinsic height to encompass floats. - int toAdd = borderAfter() + paddingAfter() + horizontalScrollbarHeight(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=46645, overflow and block-flow. - if (floatBottom() > (logicalHeight() - toAdd) && expandsToEncloseOverhangingFloats()) - setLogicalHeight(floatBottom() + toAdd); + int toAdd = borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + if (lowestFloatLogicalBottom() > (logicalHeight() - toAdd) && expandsToEncloseOverhangingFloats()) + setLogicalHeight(lowestFloatLogicalBottom() + toAdd); if (layoutColumns(hasSpecifiedPageHeight, pageHeight, statePusher)) return; @@ -1227,13 +1229,13 @@ void RenderBlock::layoutBlock(bool relayoutChildren, int pageHeight) computeLogicalHeight(); int newHeight = logicalHeight(); if (oldHeight != newHeight) { - if (oldHeight > newHeight && maxFloatBottom > newHeight && !childrenInline()) { + if (oldHeight > newHeight && maxFloatLogicalBottom > newHeight && !childrenInline()) { // One of our children's floats may have become an overhanging float for us. We need to look for it. for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (child->isBlockFlow() && !child->isFloatingOrPositioned()) { RenderBlock* block = toRenderBlock(child); - if (block->floatBottom() + block->logicalTop() > newHeight) - addOverhangingFloats(block, -block->x(), -block->y(), false); + if (block->lowestFloatLogicalBottom() + block->logicalTop() > newHeight) + addOverhangingFloats(block, -block->logicalLeft(), -block->logicalTop(), false); } } } @@ -1273,10 +1275,15 @@ void RenderBlock::layoutBlock(bool relayoutChildren, int pageHeight) // Repaint with our new bounds if they are different from our old bounds. bool didFullRepaint = repainter.repaintAfterLayout(); - if (!didFullRepaint && repaintTop != repaintBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) { - int repaintLeft = min(leftVisualOverflow(), leftLayoutOverflow()); - int repaintRight = max(rightVisualOverflow(), rightLayoutOverflow()); - IntRect repaintRect(repaintLeft, repaintTop, repaintRight - repaintLeft, repaintBottom - repaintTop); + if (!didFullRepaint && repaintLogicalTop != repaintLogicalBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) { + int repaintLogicalLeft = min(logicalLeftVisualOverflow(), logicalLeftLayoutOverflow()); + int repaintLogicalRight = max(logicalRightVisualOverflow(), logicalRightLayoutOverflow()); + + IntRect repaintRect; + if (style()->isHorizontalWritingMode()) + repaintRect = IntRect(repaintLogicalLeft, repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop); + else + repaintRect = IntRect(repaintLogicalTop, repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft); // The repaint rect may be split across columns, in which case adjustRectForColumns() will return the union. adjustRectForColumns(repaintRect); @@ -1318,7 +1325,7 @@ void RenderBlock::addOverflowFromFloats() DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for (; (r = it.current()); ++it) { if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) - addOverflowFromChild(r->m_renderer, IntSize(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop())); + addOverflowFromChild(r->m_renderer, IntSize(r->left() + r->m_renderer->marginLeft(), r->top() + r->m_renderer->marginTop())); } return; } @@ -1326,13 +1333,13 @@ void RenderBlock::addOverflowFromFloats() bool RenderBlock::expandsToEncloseOverhangingFloats() const { return isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBox()) - || hasColumns() || isTableCell() || isFieldset() || isBlockFlowRoot(); + || hasColumns() || isTableCell() || isFieldset() || isWritingModeRoot(); } void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marginInfo) { if (child->style()->hasStaticX()) { - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) child->layer()->setStaticX(borderLeft() + paddingLeft()); else child->layer()->setStaticX(borderRight() + paddingRight()); @@ -1343,8 +1350,8 @@ void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marg if (!marginInfo.canCollapseWithMarginBefore()) { child->computeBlockDirectionMargins(this); int marginTop = child->marginTop(); - int collapsedTopPos = marginInfo.posMargin(); - int collapsedTopNeg = marginInfo.negMargin(); + int collapsedTopPos = marginInfo.positiveMargin(); + int collapsedTopNeg = marginInfo.negativeMargin(); if (marginTop > 0) { if (marginTop > collapsedTopPos) collapsedTopPos = marginTop; @@ -1378,9 +1385,9 @@ void RenderBlock::adjustFloatingBlock(const MarginInfo& marginInfo) // http://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/046.html for // an example of this scenario. int marginOffset = marginInfo.canCollapseWithMarginBefore() ? 0 : marginInfo.margin(); - setLogicalHeight(height() + marginOffset); + setLogicalHeight(logicalHeight() + marginOffset); positionNewFloats(); - setLogicalHeight(height() - marginOffset); + setLogicalHeight(logicalHeight() - marginOffset); } bool RenderBlock::handleSpecialChild(RenderBox* child, const MarginInfo& marginInfo) @@ -1474,15 +1481,18 @@ bool RenderBlock::handleRunInChild(RenderBox* child) int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) { + // Get the four margin values for the child and cache them. + const MarginValues childMargins = marginValuesForChild(child); + // Get our max pos and neg top margins. - int posTop = child->maxMarginBefore(PositiveMargin); - int negTop = child->maxMarginBefore(NegativeMargin); + int posTop = childMargins.positiveMarginBefore(); + int negTop = childMargins.negativeMarginBefore(); // For self-collapsing blocks, collapse our bottom margins into our // top to get new posTop and negTop values. if (child->isSelfCollapsingBlock()) { - posTop = max(posTop, child->maxMarginAfter(PositiveMargin)); - negTop = max(negTop, child->maxMarginAfter(NegativeMargin)); + posTop = max(posTop, childMargins.positiveMarginAfter()); + negTop = max(negTop, childMargins.negativeMarginAfter()); } // See if the top margin is quirky. We only care if this child has @@ -1494,18 +1504,18 @@ int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) // 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, maxPosMarginBefore()), max(negTop, maxNegMarginBefore())); + 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)) { + if (!marginInfo.determinedMarginBeforeQuirk() && !topQuirk && (posTop - negTop)) { setMarginBeforeQuirk(false); marginInfo.setDeterminedMarginBeforeQuirk(true); } - if (!marginInfo.determinedMarginBeforeQuirk() && topQuirk && marginTop() == 0) + 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. @@ -1517,44 +1527,44 @@ int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) if (marginInfo.quirkContainer() && marginInfo.atBeforeSideOfBlock() && (posTop - negTop)) marginInfo.setMarginBeforeQuirk(topQuirk); - int beforeCollapseY = height(); - int ypos = beforeCollapseY; + int beforeCollapseLogicalTop = logicalHeight(); + int 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. - int collapsedTopPos = max(marginInfo.posMargin(), child->maxMarginBefore(PositiveMargin)); - int collapsedTopNeg = max(marginInfo.negMargin(), child->maxMarginBefore(NegativeMargin)); - marginInfo.setMargin(collapsedTopPos, collapsedTopNeg); + int collapsedBeforePos = max(marginInfo.positiveMargin(), childMargins.positiveMarginBefore()); + int 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.setPosMarginIfLarger(child->maxMarginAfter(PositiveMargin)); - marginInfo.setNegMarginIfLarger(child->maxMarginAfter(NegativeMargin)); + 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). - ypos = height() + collapsedTopPos - collapsedTopNeg; + logicalTop = logicalHeight() + collapsedBeforePos - collapsedBeforeNeg; } else { if (child->style()->marginBeforeCollapse() == MSEPARATE) { - setLogicalHeight(height() + marginInfo.margin() + child->marginTop()); - ypos = height(); + setLogicalHeight(logicalHeight() + marginInfo.margin() + marginBeforeForChild(child)); + logicalTop = logicalHeight(); } else if (!marginInfo.atBeforeSideOfBlock() || (!marginInfo.canCollapseMarginBeforeWithChildren() && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginBeforeQuirk()))) { // We're collapsing with a previous sibling's margins and not // with the top of the block. - setLogicalHeight(height() + max(marginInfo.posMargin(), posTop) - max(marginInfo.negMargin(), negTop)); - ypos = height(); + setLogicalHeight(logicalHeight() + max(marginInfo.positiveMargin(), posTop) - max(marginInfo.negativeMargin(), negTop)); + logicalTop = logicalHeight(); } - marginInfo.setPosMargin(child->maxMarginAfter(PositiveMargin)); - marginInfo.setNegMargin(child->maxMarginAfter(NegativeMargin)); + marginInfo.setPositiveMargin(childMargins.positiveMarginAfter()); + marginInfo.setNegativeMargin(childMargins.negativeMarginAfter()); if (marginInfo.margin()) marginInfo.setMarginAfterQuirk(child->isMarginAfterQuirk() || style()->marginAfterCollapse() == MDISCARD); @@ -1563,12 +1573,12 @@ int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) // If margins would pull us past the top of the next page, then we need to pull back and pretend like the margins // collapsed into the page edge. bool paginated = view()->layoutState()->isPaginated(); - if (paginated && ypos > beforeCollapseY) { - int oldY = ypos; - ypos = min(ypos, nextPageTop(beforeCollapseY)); - setLogicalHeight(height() + (ypos - oldY)); + if (paginated && logicalTop > beforeCollapseLogicalTop) { + int oldLogicalTop = logicalTop; + logicalTop = min(logicalTop, nextPageTop(beforeCollapseLogicalTop)); + setLogicalHeight(logicalHeight() + (logicalTop - oldLogicalTop)); } - return ypos; + return logicalTop; } int RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin, int yPos) @@ -1590,12 +1600,14 @@ int RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, i if (!curr->isFloatingOrPositioned()) atBottomOfBlock = false; } + + MarginValues childMargins = marginValuesForChild(child); if (atBottomOfBlock) { - marginInfo.setPosMargin(child->maxMarginAfter(PositiveMargin)); - marginInfo.setNegMargin(child->maxMarginAfter(NegativeMargin)); + marginInfo.setPositiveMargin(childMargins.positiveMarginAfter()); + marginInfo.setNegativeMargin(childMargins.negativeMarginAfter()); } else { - marginInfo.setPosMargin(max(child->maxMarginBefore(PositiveMargin), child->maxMarginAfter(PositiveMargin))); - marginInfo.setNegMargin(max(child->maxMarginBefore(NegativeMargin), child->maxMarginAfter(NegativeMargin))); + marginInfo.setPositiveMargin(max(childMargins.positiveMarginBefore(), childMargins.positiveMarginAfter())); + marginInfo.setNegativeMargin(max(childMargins.negativeMarginBefore(), childMargins.negativeMarginAfter())); } // Adjust our height such that we are ready to be collapsed with subsequent siblings (or the bottom @@ -1618,89 +1630,68 @@ int RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, i return yPos + heightIncrease; } -int RenderBlock::estimateVerticalPosition(RenderBox* child, const MarginInfo& marginInfo) +int RenderBlock::estimateLogicalTopPosition(RenderBox* child, const MarginInfo& marginInfo) { // FIXME: We need to eliminate the estimation of vertical position, because when it's wrong we sometimes trigger a pathological // relayout if there are intruding floats. - int yPosEstimate = height(); + int logicalTopEstimate = logicalHeight(); if (!marginInfo.canCollapseWithMarginBefore()) { - int childMarginTop = child->selfNeedsLayout() ? child->marginTop() : child->collapsedMarginBefore(); - yPosEstimate += max(marginInfo.margin(), childMarginTop); + int childMarginBefore = child->selfNeedsLayout() ? marginBeforeForChild(child) : collapsedMarginBeforeForChild(child); + logicalTopEstimate += max(marginInfo.margin(), childMarginBefore); } bool paginated = view()->layoutState()->isPaginated(); - // Adjust yPosEstimate down to the next page if the margins are so large that we don't fit on the current + // Adjust logicalTopEstimate down to the next page if the margins are so large that we don't fit on the current // page. - if (paginated && yPosEstimate > height()) - yPosEstimate = min(yPosEstimate, nextPageTop(height())); + if (paginated && logicalTopEstimate > logicalHeight()) + logicalTopEstimate = min(logicalTopEstimate, nextPageTop(logicalHeight())); - yPosEstimate += getClearDelta(child, yPosEstimate); + logicalTopEstimate += getClearDelta(child, logicalTopEstimate); if (paginated) { // If the object has a page or column break value of "before", then we should shift to the top of the next page. - yPosEstimate = applyBeforeBreak(child, yPosEstimate); + logicalTopEstimate = applyBeforeBreak(child, logicalTopEstimate); // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one. - yPosEstimate = adjustForUnsplittableChild(child, yPosEstimate); + logicalTopEstimate = adjustForUnsplittableChild(child, logicalTopEstimate); if (!child->selfNeedsLayout() && child->isRenderBlock()) - yPosEstimate += toRenderBlock(child)->paginationStrut(); + logicalTopEstimate += toRenderBlock(child)->paginationStrut(); } - return yPosEstimate; + return logicalTopEstimate; } -void RenderBlock::determineHorizontalPosition(RenderBox* child) +void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child) { - int xPos = borderLeft() + paddingLeft(); - if (style()->direction() == LTR) { - // Add in our left margin. - int chPos = xPos + child->marginLeft(); + int startPosition = borderStart() + paddingStart(); + int totalAvailableLogicalWidth = borderAndPaddingLogicalWidth() + availableLogicalWidth(); + + // Add in our start margin. + int childMarginStart = marginStartForChild(child); + int newPosition = startPosition + childMarginStart; - // 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()) { - int leftOff = logicalLeftOffsetForLine(height(), false); - if (style()->textAlign() != WEBKIT_CENTER && child->style()->marginLeft().type() != Auto) { - if (child->marginLeft() < 0) - leftOff += child->marginLeft(); - chPos = max(chPos, leftOff); // Let the float sit in the child's margin if it can fit. - } - else if (leftOff != xPos) { - // The object is shifting right. The object might be centered, so we need to - // recalculate our horizontal margins. Note that the containing block content - // width computation will take into account the delta between |leftOff| and |xPos| - // so that we can just pass the content width in directly to the |computeMarginsInContainingBlockInlineDirection| - // function. - child->computeInlineDirectionMargins(this, availableLogicalWidthForLine(child->y(), false), child->width()); - chPos = leftOff + child->marginLeft(); - } - } - view()->addLayoutDelta(IntSize(child->x() - chPos, 0)); - child->setLocation(chPos, child->y()); - } else { - xPos += availableLogicalWidth(); - int chPos = xPos - (child->width() + child->marginRight()); - if (child->avoidsFloats()) { - int rightOff = logicalRightOffsetForLine(height(), false); - if (style()->textAlign() != WEBKIT_CENTER && child->style()->marginRight().type() != Auto) { - if (child->marginRight() < 0) - rightOff -= child->marginRight(); - chPos = min(chPos, rightOff - child->width()); // Let the float sit in the child's margin if it can fit. - } else if (rightOff != xPos) { - // The object is shifting left. The object might be centered, so we need to - // recalculate our horizontal margins. Note that the containing block content - // width computation will take into account the delta between |rightOff| and |xPos| - // so that we can just pass the content width in directly to the |computeInlineDirectionMargins| - // function. - child->computeInlineDirectionMargins(this, availableLogicalWidthForLine(child->y(), false), child->width()); - chPos = rightOff - child->marginRight() - child->width(); - } + // 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()) { + int startOff = style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(logicalHeight(), false) : totalAvailableLogicalWidth - logicalRightOffsetForLine(logicalHeight(), false); + if (style()->textAlign() != WEBKIT_CENTER && !child->style()->marginStartUsing(style()).isAuto()) { + if (childMarginStart < 0) + startOff += childMarginStart; + newPosition = max(newPosition, startOff); // Let the float sit in the child's margin if it can fit. + } else if (startOff != startPosition) { + // The object is shifting to the "end" side of the block. The object might be centered, so we need to + // recalculate our inline direction margins. Note that the containing block content + // width computation will take into account the delta between |startOff| and |startPosition| + // so that we can just pass the content width in directly to the |computeMarginsInContainingBlockInlineDirection| + // function. + child->computeInlineDirectionMargins(this, availableLogicalWidthForLine(logicalTopForChild(child), false), logicalWidthForChild(child)); + newPosition = startOff + marginStartForChild(child); } - view()->addLayoutDelta(IntSize(child->x() - chPos, 0)); - child->setLocation(chPos, child->y()); } + + setLogicalLeftForChild(child, style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), ApplyLayoutDelta); } void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) @@ -1708,12 +1699,12 @@ void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) if (marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore()) { // Update our max pos/neg bottom margins, since we collapsed our bottom margins // with our children. - setMaxMarginAfterValues(max(maxPosMarginAfter(), marginInfo.posMargin()), max(maxNegMarginAfter(), marginInfo.negMargin())); + setMaxMarginAfterValues(max(maxPositiveMarginAfter(), marginInfo.positiveMargin()), max(maxNegativeMarginAfter(), marginInfo.negativeMargin())); if (!marginInfo.marginAfterQuirk()) setMarginAfterQuirk(false); - if (marginInfo.marginAfterQuirk() && marginBottom() == 0) + if (marginInfo.marginAfterQuirk() && marginAfter() == 0) // 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. @@ -1721,27 +1712,53 @@ void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) } } -void RenderBlock::handleBottomOfBlock(int top, int bottom, MarginInfo& marginInfo) +void RenderBlock::handleAfterSideOfBlock(int beforeSide, int afterSide, MarginInfo& marginInfo) { marginInfo.setAtAfterSideOfBlock(true); // If we can't collapse with children then go ahead and add in the bottom margin. if (!marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore() && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginAfterQuirk())) - setLogicalHeight(height() + marginInfo.margin()); + setLogicalHeight(logicalHeight() + marginInfo.margin()); // Now add in our bottom border/padding. - setLogicalHeight(height() + bottom); + setLogicalHeight(logicalHeight() + afterSide); // Negative margins can cause our height to shrink below our minimal height (border/padding). // If this happens, ensure that the computed height is increased to the minimal height. - setLogicalHeight(max(height(), top + bottom)); + setLogicalHeight(max(logicalHeight(), beforeSide + afterSide)); // Update our bottom collapsed margin info. setCollapsedBottomMargin(marginInfo); } -void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom) +void RenderBlock::setLogicalLeftForChild(RenderBox* child, int logicalLeft, ApplyLayoutDeltaMode applyDelta) +{ + if (style()->isHorizontalWritingMode()) { + if (applyDelta == ApplyLayoutDelta) + view()->addLayoutDelta(IntSize(child->x() - logicalLeft, 0)); + child->setX(logicalLeft); + } else { + if (applyDelta == ApplyLayoutDelta) + view()->addLayoutDelta(IntSize(0, child->y() - logicalLeft)); + child->setY(logicalLeft); + } +} + +void RenderBlock::setLogicalTopForChild(RenderBox* child, int logicalTop, ApplyLayoutDeltaMode applyDelta) +{ + if (style()->isHorizontalWritingMode()) { + if (applyDelta == ApplyLayoutDelta) + view()->addLayoutDelta(IntSize(0, child->y() - logicalTop)); + child->setY(logicalTop); + } else { + if (applyDelta == ApplyLayoutDelta) + view()->addLayoutDelta(IntSize(child->x() - logicalTop, 0)); + child->setX(logicalTop); + } +} + +void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatLogicalBottom) { if (gPercentHeightDescendantsMap) { if (HashSet<RenderBox*>* descendants = gPercentHeightDescendantsMap->get(this)) { @@ -1761,20 +1778,22 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom } } - int top = borderTop() + paddingTop(); - int bottom = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + int beforeEdge = borderBefore() + paddingBefore(); + int afterEdge = borderAfter() + paddingAfter() + scrollbarLogicalHeight(); - setLogicalHeight(top); + setLogicalHeight(beforeEdge); // The margin struct caches all our current margin collapsing state. The compact struct caches state when we encounter compacts, - MarginInfo marginInfo(this, top, bottom); + MarginInfo marginInfo(this, beforeEdge, afterEdge); // Fieldsets need to find their legend and position it inside the border of the object. // The legend then gets skipped during normal layout. + // FIXME: Make fieldsets work with block-flow. + // https://bugs.webkit.org/show_bug.cgi?id=46785 RenderObject* legend = layoutLegend(relayoutChildren); - int previousFloatBottom = 0; - maxFloatBottom = 0; + int previousFloatLogicalBottom = 0; + maxFloatLogicalBottom = 0; RenderBox* next = firstChildBox(); @@ -1788,11 +1807,11 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom // 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->style()->height().isPercent() || child->style()->minHeight().isPercent() || child->style()->maxHeight().isPercent()) && !isRenderView())) + if (relayoutChildren || ((child->style()->logicalHeight().isPercent() || child->style()->logicalMinHeight().isPercent() || child->style()->logicalMaxHeight().isPercent()) && !isRenderView())) child->setChildNeedsLayout(true, false); - // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. - if (relayoutChildren && (child->style()->paddingLeft().isPercent() || child->style()->paddingRight().isPercent())) + // If relayoutChildren is set and the child has percentage padding, we also need to invalidate the child's pref widths. + if (relayoutChildren && (child->style()->paddingStart().isPercent() || child->style()->paddingEnd().isPercent())) child->setPreferredLogicalWidthsDirty(true, false); // Handle the four types of special elements first. These include positioned content, floating content, compacts and @@ -1801,63 +1820,64 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom continue; // Lay out the child. - layoutBlockChild(child, marginInfo, previousFloatBottom, maxFloatBottom); + layoutBlockChild(child, marginInfo, previousFloatLogicalBottom, maxFloatLogicalBottom); } // Now do the handling of the bottom of the block, adding in our bottom border/padding and // determining the correct collapsed bottom margin information. - handleBottomOfBlock(top, bottom, marginInfo); + handleAfterSideOfBlock(beforeEdge, afterEdge, marginInfo); } -void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int& previousFloatBottom, int& maxFloatBottom) +void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int& previousFloatLogicalBottom, int& maxFloatLogicalBottom) { - int oldTopPosMargin = maxPosMarginBefore(); - int oldTopNegMargin = maxNegMarginBefore(); + int oldPosMarginBefore = maxPositiveMarginBefore(); + int oldNegMarginBefore = maxNegativeMarginBefore(); - // The child is a normal flow object. Compute its vertical margins now. + // The child is a normal flow object. Compute the margins we will use for collapsing now. child->computeBlockDirectionMargins(this); - // Do not allow a collapse if the margin top collapse style is set to SEPARATE. + // Do not allow a collapse if the margin-before-collapse style is set to SEPARATE. if (child->style()->marginBeforeCollapse() == MSEPARATE) { marginInfo.setAtBeforeSideOfBlock(false); marginInfo.clearMargin(); } - // Try to guess our correct y position. In most cases this guess will - // be correct. Only if we're wrong (when we compute the real y position) + // 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. - int yPosEstimate = estimateVerticalPosition(child, marginInfo); + int logicalTopEstimate = estimateLogicalTopPosition(child, marginInfo); // Cache our old rect so that we can dirty the proper repaint rects if the child moves. IntRect oldRect(child->x(), child->y() , child->width(), child->height()); + int oldLogicalTop = logicalTopForChild(child); + #ifndef NDEBUG IntSize oldLayoutDelta = view()->layoutDelta(); #endif // Go ahead and position the child as though it didn't collapse with the top. - view()->addLayoutDelta(IntSize(0, child->y() - yPosEstimate)); - child->setLocation(child->x(), yPosEstimate); + setLogicalTopForChild(child, logicalTopEstimate, ApplyLayoutDelta); RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0; bool markDescendantsWithFloats = false; - if (yPosEstimate != oldRect.y() && !child->avoidsFloats() && childRenderBlock && childRenderBlock->containsFloats()) + if (logicalTopEstimate != oldLogicalTop && !child->avoidsFloats() && childRenderBlock && childRenderBlock->containsFloats()) markDescendantsWithFloats = true; else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) { // If an element might be affected by the presence of floats, then always mark it for // layout. - int fb = max(previousFloatBottom, floatBottom()); - if (fb > yPosEstimate) + int fb = max(previousFloatLogicalBottom, lowestFloatLogicalBottom()); + if (fb > logicalTopEstimate) markDescendantsWithFloats = true; } if (childRenderBlock) { if (markDescendantsWithFloats) childRenderBlock->markAllDescendantsWithFloatsForLayout(); - previousFloatBottom = max(previousFloatBottom, oldRect.y() + toRenderBlock(child)->floatBottom()); + if (!child->isWritingModeRoot()) + previousFloatLogicalBottom = max(previousFloatLogicalBottom, oldLogicalTop + childRenderBlock->lowestFloatLogicalBottom()); } - bool paginated = view()->layoutState()->isPaginated(); - if (!child->needsLayout() && paginated && view()->layoutState()->m_pageHeight && childRenderBlock && view()->layoutState()->pageY(child->y()) != childRenderBlock->pageY()) - childRenderBlock->markForPaginationRelayout(); + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); bool childHadLayout = child->m_everHadLayout; bool childNeededLayout = child->needsLayout(); @@ -1869,23 +1889,24 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int // Now determine the correct ypos based off examination of collapsing margin // values. - int yBeforeClear = collapseMargins(child, marginInfo); + int logicalTopBeforeClear = collapseMargins(child, marginInfo); // Now check for clear. - int yAfterClear = clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin, yBeforeClear); + int logicalTopAfterClear = clearFloatsIfNeeded(child, marginInfo, oldPosMarginBefore, oldNegMarginBefore, logicalTopBeforeClear); + bool paginated = view()->layoutState()->isPaginated(); if (paginated) { - int oldY = yAfterClear; + int oldTop = logicalTopAfterClear; // If the object has a page or column break value of "before", then we should shift to the top of the next page. - yAfterClear = applyBeforeBreak(child, yAfterClear); + logicalTopAfterClear = applyBeforeBreak(child, logicalTopAfterClear); // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one. - int yBeforeUnsplittableAdjustment = yAfterClear; - int yAfterUnsplittableAdjustment = adjustForUnsplittableChild(child, yAfterClear); + int logicalTopBeforeUnsplittableAdjustment = logicalTopAfterClear; + int logicalTopAfterUnsplittableAdjustment = adjustForUnsplittableChild(child, logicalTopAfterClear); int paginationStrut = 0; - int unsplittableAdjustmentDelta = yAfterUnsplittableAdjustment - yBeforeUnsplittableAdjustment; + int unsplittableAdjustmentDelta = logicalTopAfterUnsplittableAdjustment - logicalTopBeforeUnsplittableAdjustment; if (unsplittableAdjustmentDelta) paginationStrut = unsplittableAdjustmentDelta; else if (childRenderBlock && childRenderBlock->paginationStrut()) @@ -1894,26 +1915,25 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int if (paginationStrut) { // We are willing to propagate out to our parent block as long as we were at the top of the block prior // to collapsing our margins, and as long as we didn't clear or move as a result of other pagination. - if (atBeforeSideOfBlock && oldY == yBeforeClear && !isPositioned() && !isTableCell()) { + if (atBeforeSideOfBlock && oldTop == logicalTopBeforeClear && !isPositioned() && !isTableCell()) { // FIXME: Should really check if we're exceeding the page height before propagating the strut, but we don't // have all the information to do so (the strut only has the remaining amount to push). Gecko gets this wrong too // and pushes to the next page anyway, so not too concerned about it. - setPaginationStrut(yAfterClear + paginationStrut); + setPaginationStrut(logicalTopAfterClear + paginationStrut); if (childRenderBlock) childRenderBlock->setPaginationStrut(0); } else - yAfterClear += paginationStrut; + logicalTopAfterClear += paginationStrut; } // Similar to how we apply clearance. Go ahead and boost height() to be the place where we're going to position the child. - setLogicalHeight(height() + (yAfterClear - oldY)); + setLogicalHeight(logicalHeight() + (logicalTopAfterClear - oldTop)); } - view()->addLayoutDelta(IntSize(0, yPosEstimate - yAfterClear)); - child->setLocation(child->x(), yAfterClear); + setLogicalTopForChild(child, logicalTopAfterClear, ApplyLayoutDelta); - // Now we have a final y position. See if it really does end up being different from our estimate. - if (yAfterClear != yPosEstimate) { + // Now we have a final top position. See if it really does end up being different from our estimate. + if (logicalTopAfterClear != logicalTopEstimate) { if (child->shrinkToAvoidFloats()) { // The child's width depends on the line width. // When the child shifts to clear an item, its width can @@ -1924,8 +1944,8 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int if (childRenderBlock) { if (!child->avoidsFloats() && childRenderBlock->containsFloats()) childRenderBlock->markAllDescendantsWithFloatsForLayout(); - if (paginated && !child->needsLayout() && view()->layoutState()->m_pageHeight && view()->layoutState()->pageY(child->y()) != childRenderBlock->pageY()) - childRenderBlock->markForPaginationRelayout(); + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); } // Our guess was wrong. Make the child lay itself out again. @@ -1937,19 +1957,19 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int if (marginInfo.atBeforeSideOfBlock() && !child->isSelfCollapsingBlock()) marginInfo.setAtBeforeSideOfBlock(false); - // Now place the child in the correct horizontal position - determineHorizontalPosition(child); + // Now place the child in the correct left position + determineLogicalLeftPositionForChild(child); // Update our height now that the child has been placed in the correct position. - setLogicalHeight(height() + child->height()); + setLogicalHeight(logicalHeight() + logicalHeightForChild(child)); if (child->style()->marginAfterCollapse() == MSEPARATE) { - setLogicalHeight(height() + child->marginBottom()); + setLogicalHeight(logicalHeight() + marginAfterForChild(child)); marginInfo.clearMargin(); } // If the child has overhanging floats that intrude into following siblings (or possibly out // of this block), then the parent gets notified of the floats now. if (childRenderBlock && childRenderBlock->containsFloats()) - maxFloatBottom = max(maxFloatBottom, addOverhangingFloats(toRenderBlock(child), -child->x(), -child->y(), !childNeededLayout)); + maxFloatLogicalBottom = max(maxFloatLogicalBottom, addOverhangingFloats(toRenderBlock(child), -child->logicalLeft(), -child->logicalTop(), !childNeededLayout)); IntSize childOffset(child->x() - oldRect.x(), child->y() - oldRect.y()); if (childOffset.width() || childOffset.height()) { @@ -2014,8 +2034,6 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren) if (hasColumns()) view()->layoutState()->clearPaginationInformation(); // Positioned objects are not part of the column flow, so they don't paginate with the columns. - bool paginated = view()->layoutState()->isPaginated(); - RenderBox* r; Iterator end = m_positionedObjects->end(); for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { @@ -2031,11 +2049,8 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren) //if (relayoutChildren && (r->style()->paddingLeft().isPercent() || r->style()->paddingRight().isPercent())) r->setPreferredLogicalWidthsDirty(true, false); - if (!r->needsLayout() && paginated && view()->layoutState()->m_pageHeight) { - RenderBlock* childRenderBlock = r->isRenderBlock() ? toRenderBlock(r) : 0; - if (childRenderBlock && view()->layoutState()->pageY(childRenderBlock->y()) != childRenderBlock->pageY()) - childRenderBlock->markForPaginationRelayout(); - } + if (!r->needsLayout()) + r->markForPaginationRelayoutIfNeeded(); // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width // and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout. @@ -2061,6 +2076,16 @@ void RenderBlock::markPositionedObjectsForLayout() } } +void RenderBlock::markForPaginationRelayoutIfNeeded() +{ + ASSERT(!needsLayout()); + if (needsLayout()) + return; + + if (view()->layoutState()->pageHeightChanged() || (view()->layoutState()->pageHeight() && view()->layoutState()->pageY(y()) != pageY())) + setChildNeedsLayout(true, false); +} + void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) { // Repaint any overhanging floats (if we know we're the one to paint them). @@ -2081,7 +2106,7 @@ void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) // Only repaint the object if it is overhanging, is not in its own layer, and // is our responsibility to paint (m_shouldPaint is set). When paintAllDescendants is true, the latter // condition is replaced with being a descendant of us. - if (r->m_bottom > height() && ((paintAllDescendants && r->m_renderer->isDescendantOf(this)) || r->m_shouldPaint) && !r->m_renderer->hasSelfPaintingLayer()) { + if (logicalBottomForFloat(r) > logicalHeight() && ((paintAllDescendants && r->m_renderer->isDescendantOf(this)) || r->m_shouldPaint) && !r->m_renderer->hasSelfPaintingLayer()) { r->m_renderer->repaint(); r->m_renderer->repaintOverhangingFloats(); } @@ -2134,14 +2159,14 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, int tx, int ty) // We need to do multiple passes, breaking up our child painting into strips. ColumnInfo* colInfo = columnInfo(); unsigned colCount = columnCount(colInfo); - int currXOffset = style()->direction() == LTR ? 0 : contentWidth(); + int currXOffset = style()->isLeftToRightDirection() ? 0 : contentWidth(); int ruleAdd = borderLeft() + paddingLeft(); - int ruleX = style()->direction() == LTR ? 0 : contentWidth(); + int ruleX = style()->isLeftToRightDirection() ? 0 : contentWidth(); for (unsigned i = 0; i < colCount; i++) { IntRect colRect = columnRectAt(colInfo, i); // Move to the next position. - if (style()->direction() == LTR) { + if (style()->isLeftToRightDirection()) { ruleX += colRect.width() + colGap / 2; currXOffset += colRect.width() + colGap; } else { @@ -2156,7 +2181,7 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, int tx, int ty) int ruleTop = ty + borderTop() + paddingTop(); int ruleBottom = ruleTop + contentHeight(); drawLineForBoxSide(paintInfo.context, ruleStart, ruleTop, ruleEnd, ruleBottom, - style()->direction() == LTR ? BSLeft : BSRight, ruleColor, ruleStyle, 0, 0); + style()->isLeftToRightDirection() ? BSLeft : BSRight, ruleColor, ruleStyle, 0, 0); } ruleX = currXOffset; @@ -2172,7 +2197,7 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, int tx, int ty, bool unsigned colCount = columnCount(colInfo); if (!colCount) return; - int currXOffset = style()->direction() == LTR ? 0 : contentWidth() - columnRectAt(colInfo, 0).width(); + int currXOffset = style()->isLeftToRightDirection() ? 0 : contentWidth() - columnRectAt(colInfo, 0).width(); int currYOffset = 0; for (unsigned i = 0; i < colCount; i++) { // For each rect, we clip to the rect, and then we adjust our coords. @@ -2200,7 +2225,7 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, int tx, int ty, bool } // Move to the next position. - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) currXOffset += colRect.width() + colGap; else currXOffset -= (colRect.width() + colGap); @@ -2393,8 +2418,8 @@ void RenderBlock::paintFloats(PaintInfo& paintInfo, int tx, int ty, bool preserv if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) { PaintInfo currentPaintInfo(paintInfo); currentPaintInfo.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; - int currentTX = tx + r->m_left - r->m_renderer->x() + r->m_renderer->marginLeft(); - int currentTY = ty + r->m_top - r->m_renderer->y() + r->m_renderer->marginTop(); + int currentTX = tx + r->left() - r->m_renderer->x() + r->m_renderer->marginLeft(); + int currentTY = ty + r->top() - r->m_renderer->y() + r->m_renderer->marginTop(); r->m_renderer->paint(currentPaintInfo, currentTX, currentTY); if (!preservePhase) { currentPaintInfo.phase = PaintPhaseChildBlockBackgrounds; @@ -2603,8 +2628,8 @@ GapRects RenderBlock::fillSelectionGaps(RenderBlock* rootBlock, int blockX, int if (m_floatingObjects) { for (DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); it.current(); ++it) { FloatingObject* r = it.current(); - paintInfo->context->clipOut(IntRect(tx + r->m_left + r->m_renderer->marginLeft(), - ty + r->m_top + r->m_renderer->marginTop(), + paintInfo->context->clipOut(IntRect(tx + r->left() + r->m_renderer->marginLeft(), + ty + r->top() + r->m_renderer->marginTop(), r->m_renderer->width(), r->m_renderer->height())); } } @@ -2815,7 +2840,7 @@ IntRect RenderBlock::fillRightSelectionGap(RenderObject* selObj, int xPos, int y void RenderBlock::getHorizontalSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap) { - bool ltr = style()->direction() == LTR; + bool ltr = style()->isLeftToRightDirection(); leftGap = (state == RenderObject::SelectionInside) || (state == RenderObject::SelectionEnd && ltr) || (state == RenderObject::SelectionStart && !ltr); @@ -2933,20 +2958,21 @@ RenderBlock::FloatingObject* RenderBlock::insertFloatingObject(RenderBox* o) // Create the special object entry & append it to the list FloatingObject* newObj = new FloatingObject(o->style()->floating() == FLEFT ? FloatingObject::FloatLeft : FloatingObject::FloatRight); - - newObj->m_top = -1; - newObj->m_bottom = -1; // Our location is irrelevant if we're unsplittable or no pagination is in effect. // Just go ahead and lay out the float. - bool affectedByPagination = o->isRenderBlock() && view()->layoutState()->m_pageHeight; - if (!affectedByPagination) + bool isChildRenderBlock = o->isRenderBlock(); + if (isChildRenderBlock && !o->needsLayout() && view()->layoutState()->pageHeightChanged()) + o->setChildNeedsLayout(true, false); + + bool affectedByPagination = isChildRenderBlock && view()->layoutState()->m_pageHeight; + if (!affectedByPagination || isWritingModeRoot()) // We are unsplittable if we're a block flow root. o->layoutIfNeeded(); else { o->computeLogicalWidth(); o->computeBlockDirectionMargins(this); } - newObj->m_width = o->width() + o->marginLeft() + o->marginRight(); + setLogicalWidthForFloat(newObj, logicalWidthForChild(o) + marginStartForChild(o) + marginEndForChild(o)); newObj->m_shouldPaint = !o->hasSelfPaintingLayer(); // If a layer exists, the float will paint itself. Otherwise someone else will. newObj->m_isDescendant = true; @@ -2964,12 +2990,14 @@ void RenderBlock::removeFloatingObject(RenderBox* o) while (it.current()) { if (it.current()->m_renderer == o) { if (childrenInline()) { - int bottom = it.current()->m_bottom; + int logicalTop = logicalTopForFloat(it.current()); + int logicalBottom = logicalBottomForFloat(it.current()); + // Special-case zero- and less-than-zero-height floats: those don't touch // the line that they're on, but it still needs to be dirtied. This is // accomplished by pretending they have a height of 1. - bottom = max(bottom, it.current()->m_top + 1); - markLinesDirtyInVerticalRange(0, bottom); + logicalBottom = max(logicalBottom, logicalTop + 1); + markLinesDirtyInBlockRange(0, logicalBottom); } m_floatingObjects->removeRef(it.current()); } @@ -2984,7 +3012,7 @@ void RenderBlock::removeFloatingObjectsBelow(FloatingObject* lastFloat, int y) return; FloatingObject* curr = m_floatingObjects->last(); - while (curr != lastFloat && (curr->m_top == -1 || curr->m_top >= y)) { + while (curr != lastFloat && (!curr->isPlaced() || curr->top() >= y)) { m_floatingObjects->removeLast(); curr = m_floatingObjects->last(); } @@ -2995,111 +3023,118 @@ bool RenderBlock::positionNewFloats() if (!m_floatingObjects) return false; - FloatingObject* f = m_floatingObjects->last(); + FloatingObject* floatingObject = m_floatingObjects->last(); // If all floats have already been positioned, then we have no work to do. - if (!f || f->m_top != -1) + if (!floatingObject || floatingObject->isPlaced()) return false; // Move backwards through our floating object list until we find a float that has // already been positioned. Then we'll be able to move forward, positioning all of // the new floats that need it. FloatingObject* lastFloat = m_floatingObjects->getPrev(); - while (lastFloat && lastFloat->m_top == -1) { - f = m_floatingObjects->prev(); + while (lastFloat && !lastFloat->isPlaced()) { + floatingObject = m_floatingObjects->prev(); lastFloat = m_floatingObjects->getPrev(); } - int y = height(); + int logicalTop = logicalHeight(); - // The float cannot start above the y position of the last positioned float. + // The float cannot start above the top position of the last positioned float. if (lastFloat) - y = max(lastFloat->m_top, y); + logicalTop = max(logicalTopForFloat(lastFloat), logicalTop); // Now walk through the set of unpositioned floats and place them. - while (f) { + while (floatingObject) { // The containing block is responsible for positioning floats, so if we have floats in our // list that come from somewhere else, do not attempt to position them. - if (f->m_renderer->containingBlock() != this) { - f = m_floatingObjects->next(); + if (floatingObject->renderer()->containingBlock() != this) { + floatingObject = m_floatingObjects->next(); continue; } - RenderBox* o = f->m_renderer; + RenderBox* childBox = floatingObject->renderer(); + int childLogicalLeftMargin = style()->isLeftToRightDirection() ? marginStartForChild(childBox) : marginEndForChild(childBox); - int ro = logicalRightOffsetForContent(); // Constant part of right offset. - int lo = logicalLeftOffsetForContent(); // Constant part of left offset. - int fwidth = f->m_width; // The width we look for. - if (ro - lo < fwidth) - fwidth = ro - lo; // Never look for more than what will be available. + int rightOffset = logicalRightOffsetForContent(); // Constant part of right offset. + int leftOffset = logicalLeftOffsetForContent(); // Constant part of left offset. + int floatLogicalWidth = logicalWidthForFloat(floatingObject); // The width we look for. + if (rightOffset - leftOffset < floatLogicalWidth) + floatLogicalWidth = rightOffset - leftOffset; // Never look for more than what will be available. - IntRect oldRect(o->x(), o->y() , o->width(), o->height()); + IntRect oldRect(childBox->x(), childBox->y() , childBox->width(), childBox->height()); - if (o->style()->clear() & CLEFT) - y = max(leftBottom(), y); - if (o->style()->clear() & CRIGHT) - y = max(rightBottom(), y); + if (childBox->style()->clear() & CLEFT) + logicalTop = max(lowestFloatLogicalBottom(FloatingObject::FloatLeft), logicalTop); + if (childBox->style()->clear() & CRIGHT) + logicalTop = max(lowestFloatLogicalBottom(FloatingObject::FloatRight), logicalTop); - if (o->style()->floating() == FLEFT) { + int floatLogicalLeft; + if (childBox->style()->floating() == FLEFT) { int heightRemainingLeft = 1; int heightRemainingRight = 1; - int fx = logicalLeftOffsetForLine(y, lo, false, &heightRemainingLeft); - while (logicalRightOffsetForLine(y, ro, false, &heightRemainingRight)-fx < fwidth) { - y += min(heightRemainingLeft, heightRemainingRight); - fx = logicalLeftOffsetForLine(y, lo, false, &heightRemainingLeft); + floatLogicalLeft = logicalLeftOffsetForLine(logicalTop, leftOffset, false, &heightRemainingLeft); + while (logicalRightOffsetForLine(logicalTop, rightOffset, false, &heightRemainingRight) - floatLogicalLeft < floatLogicalWidth) { + logicalTop += min(heightRemainingLeft, heightRemainingRight); + floatLogicalLeft = logicalLeftOffsetForLine(logicalTop, leftOffset, false, &heightRemainingLeft); } - fx = max(0, fx); - f->m_left = fx; - o->setLocation(fx + o->marginLeft(), y + o->marginTop()); + floatLogicalLeft = max(0, floatLogicalLeft); } else { int heightRemainingLeft = 1; int heightRemainingRight = 1; - int fx = logicalRightOffsetForLine(y, ro, false, &heightRemainingRight); - while (fx - logicalLeftOffsetForLine(y, lo, false, &heightRemainingLeft) < fwidth) { - y += min(heightRemainingLeft, heightRemainingRight); - fx = logicalRightOffsetForLine(y, ro, false, &heightRemainingRight); + floatLogicalLeft = logicalRightOffsetForLine(logicalTop, rightOffset, false, &heightRemainingRight); + while (floatLogicalLeft - logicalLeftOffsetForLine(logicalTop, leftOffset, false, &heightRemainingLeft) < floatLogicalWidth) { + logicalTop += min(heightRemainingLeft, heightRemainingRight); + floatLogicalLeft = logicalRightOffsetForLine(logicalTop, rightOffset, false, &heightRemainingRight); } - f->m_left = fx - f->m_width; - o->setLocation(fx - o->marginRight() - o->width(), y + o->marginTop()); + floatLogicalLeft -= logicalWidthForFloat(floatingObject); // Use the original width of the float here, since the local variable + // |floatLogicalWidth| was capped to the available line width. + // See fast/block/float/clamped-right-float.html. } + + setLogicalLeftForFloat(floatingObject, floatLogicalLeft); + setLogicalLeftForChild(childBox, floatLogicalLeft + childLogicalLeftMargin); + setLogicalTopForChild(childBox, logicalTop + marginBeforeForChild(childBox)); if (view()->layoutState()->isPaginated()) { - RenderBlock* childBlock = o->isRenderBlock() ? toRenderBlock(o) : 0; + RenderBlock* childBlock = childBox->isRenderBlock() ? toRenderBlock(childBox) : 0; - if (childBlock && view()->layoutState()->m_pageHeight && view()->layoutState()->pageY(o->y()) != childBlock->pageY()) - childBlock->markForPaginationRelayout(); - o->layoutIfNeeded(); + if (!childBox->needsLayout()) + childBox->markForPaginationRelayoutIfNeeded();; + childBox->layoutIfNeeded(); // If we are unsplittable and don't fit, then we need to move down. // We include our margins as part of the unsplittable area. - int newY = adjustForUnsplittableChild(o, y, true); + int newLogicalTop = adjustForUnsplittableChild(childBox, logicalTop, true); // See if we have a pagination strut that is making us move down further. // Note that an unsplittable child can't also have a pagination strut, so this is // exclusive with the case above. if (childBlock && childBlock->paginationStrut()) { - newY += childBlock->paginationStrut(); + newLogicalTop += childBlock->paginationStrut(); childBlock->setPaginationStrut(0); } - if (newY != y) { - f->m_paginationStrut = newY - y; - y = newY; - o->setY(y + o->marginTop()); + if (newLogicalTop != logicalTop) { + floatingObject->m_paginationStrut = newLogicalTop - logicalTop; + logicalTop = newLogicalTop; + setLogicalTopForChild(childBox, logicalTop + marginBeforeForChild(childBox)); if (childBlock) childBlock->setChildNeedsLayout(true, false); - o->layoutIfNeeded(); + childBox->layoutIfNeeded(); } } - f->m_top = y; - f->m_bottom = f->m_top + o->marginTop() + o->height() + o->marginBottom(); - + setLogicalTopForFloat(floatingObject, logicalTop); + setLogicalHeightForFloat(floatingObject, logicalHeightForChild(childBox) + marginBeforeForChild(childBox) + marginAfterForChild(childBox)); + + floatingObject->setIsPlaced(); + // If the child moved, we have to repaint it. - if (o->checkForRepaintDuringLayout()) - o->repaintDuringLayoutIfMoved(oldRect); + if (childBox->checkForRepaintDuringLayout()) + childBox->repaintDuringLayoutIfMoved(oldRect); - f = m_floatingObjects->next(); + floatingObject = m_floatingObjects->next(); } return true; } @@ -3110,30 +3145,29 @@ bool RenderBlock::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObjec if (!didPosition || !newFloat->m_paginationStrut) return didPosition; - int floatTop = newFloat->m_top; + int floatLogicalTop = logicalTopForFloat(newFloat); int paginationStrut = newFloat->m_paginationStrut; FloatingObject* f = m_floatingObjects->last(); ASSERT(f == newFloat); - if (floatTop - paginationStrut != height()) + if (floatLogicalTop - paginationStrut != logicalHeight()) return didPosition; for (f = m_floatingObjects->prev(); f && f != lastFloatFromPreviousLine; f = m_floatingObjects->prev()) { - if (f->m_top == height()) { + if (logicalTopForFloat(f) == logicalHeight()) { ASSERT(!f->m_paginationStrut); f->m_paginationStrut = paginationStrut; RenderBox* o = f->m_renderer; - o->setY(o->y() + o->marginTop() + paginationStrut); + setLogicalTopForChild(o, logicalTopForChild(o) + marginBeforeForChild(o) + paginationStrut); if (o->isRenderBlock()) toRenderBlock(o)->setChildNeedsLayout(true, false); o->layoutIfNeeded(); - f->m_top += f->m_paginationStrut; - f->m_bottom += f->m_paginationStrut; + setLogicalTopForFloat(f, logicalTopForFloat(f) + f->m_paginationStrut); } } - setLogicalHeight(height() + paginationStrut); + setLogicalHeight(logicalHeight() + paginationStrut); return didPosition; } @@ -3146,13 +3180,13 @@ void RenderBlock::newLine(EClear clear) switch (clear) { case CLEFT: - newY = leftBottom(); + newY = lowestFloatLogicalBottom(FloatingObject::FloatLeft); break; case CRIGHT: - newY = rightBottom(); + newY = lowestFloatLogicalBottom(FloatingObject::FloatRight); break; case CBOTH: - newY = floatBottom(); + newY = lowestFloatLogicalBottom(); default: break; } @@ -3220,30 +3254,26 @@ HashSet<RenderBox*>* RenderBlock::percentHeightDescendants() const return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0; } -int RenderBlock::logicalLeftOffsetForContent() const -{ - return borderLeft() + paddingLeft(); -} - -int RenderBlock::logicalLeftOffsetForLine(int y, int fixedOffset, bool applyTextIndent, int* heightRemaining) const +int RenderBlock::logicalLeftOffsetForLine(int logicalTop, int fixedOffset, bool applyTextIndent, int* heightRemaining) const { int left = fixedOffset; if (m_floatingObjects) { - if ( heightRemaining ) *heightRemaining = 1; + if (heightRemaining) + *heightRemaining = 1; FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); - for ( ; (r = it.current()); ++it ) - { - if (r->m_top <= y && r->m_bottom > y && - r->type() == FloatingObject::FloatLeft && - r->m_left + r->m_width > left) { - left = r->m_left + r->m_width; - if ( heightRemaining ) *heightRemaining = r->m_bottom - y; + for ( ; (r = it.current()); ++it) { + if (r->isPlaced() && logicalTopForFloat(r) <= logicalTop && logicalBottomForFloat(r) > logicalTop + && r->type() == FloatingObject::FloatLeft + && logicalRightForFloat(r) > left) { + left = logicalRightForFloat(r); + if (heightRemaining) + *heightRemaining = logicalBottomForFloat(r) - logicalTop; } } } - if (applyTextIndent && style()->direction() == LTR) { + if (applyTextIndent && style()->isLeftToRightDirection()) { int cw = 0; if (style()->textIndent().isPercent()) cw = containingBlock()->availableLogicalWidth(); @@ -3253,31 +3283,27 @@ int RenderBlock::logicalLeftOffsetForLine(int y, int fixedOffset, bool applyText return left; } -int RenderBlock::logicalRightOffsetForContent() const -{ - return borderLeft() + paddingLeft() + availableLogicalWidth(); -} - -int RenderBlock::logicalRightOffsetForLine(int y, int fixedOffset, bool applyTextIndent, int* heightRemaining) const +int RenderBlock::logicalRightOffsetForLine(int logicalTop, int fixedOffset, bool applyTextIndent, int* heightRemaining) const { int right = fixedOffset; if (m_floatingObjects) { - if (heightRemaining) *heightRemaining = 1; + if (heightRemaining) + *heightRemaining = 1; FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); - for ( ; (r = it.current()); ++it ) - { - if (r->m_top <= y && r->m_bottom > y && - r->type() == FloatingObject::FloatRight && - r->m_left < right) { - right = r->m_left; - if ( heightRemaining ) *heightRemaining = r->m_bottom - y; + for ( ; (r = it.current()); ++it) { + if (r->isPlaced() && logicalTopForFloat(r) <= logicalTop && logicalBottomForFloat(r) > logicalTop + && r->type() == FloatingObject::FloatRight + && logicalLeftForFloat(r) < right) { + right = logicalLeftForFloat(r); + if (heightRemaining) + *heightRemaining = logicalBottomForFloat(r) - logicalTop; } } } - if (applyTextIndent && style()->direction() == RTL) { + if (applyTextIndent && !style()->isLeftToRightDirection()) { int cw = 0; if (style()->textIndent().isPercent()) cw = containingBlock()->availableLogicalWidth(); @@ -3294,7 +3320,7 @@ RenderBlock::availableLogicalWidthForLine(int position, bool firstLine) const return (result < 0) ? 0 : result; } -int RenderBlock::nextFloatBottomBelow(int height) const +int RenderBlock::nextFloatLogicalBottomBelow(int logicalHeight) const { if (!m_floatingObjects) return 0; @@ -3303,36 +3329,130 @@ int RenderBlock::nextFloatBottomBelow(int height) const FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it) { - if (r->m_bottom > height) - bottom = min(r->m_bottom, bottom); + int floatBottom = logicalBottomForFloat(r); + if (floatBottom > logicalHeight) + bottom = min(floatBottom, bottom); } return bottom == INT_MAX ? 0 : bottom; } -int -RenderBlock::floatBottom() const +int RenderBlock::lowestFloatLogicalBottom(FloatingObject::Type floatType) const { - if (!m_floatingObjects) return 0; - int bottom = 0; + if (!m_floatingObjects) + return 0; + int lowestFloatBottom = 0; FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); - for ( ; (r = it.current()); ++it ) - if (r->m_bottom>bottom) - bottom = r->m_bottom; - return bottom; + for ( ; (r = it.current()); ++it) { + if (r->isPlaced() && r->type() & floatType) + lowestFloatBottom = max(lowestFloatBottom, logicalBottomForFloat(r)); + } + return lowestFloatBottom; } -int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) const +int RenderBlock::topmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const { - int bottom = includeSelf && width() > 0 ? height() : 0; - + IntRect transformedRect = transformedFrameRect(); + int transformedTop = includeSelf && transformedRect.width() > 0 ? 0 : transformedRect.height(); + if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) - return bottom; + return transformedTop; - if (!firstChild() && (!width() || !height())) - return bottom; + if (!firstChild() && (!transformedRect.width() || !transformedRect.height())) + return transformedTop; + + int top = includeSelf && width() > 0 ? 0 : height(); + + if (!hasColumns()) { + // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. + // For now, we have to descend into all the children, since we may have a huge abs div inside + // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to + // the abs div. + for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { + if (!c->isFloatingOrPositioned() && c->isBox()) { + RenderBox* childBox = toRenderBox(c); + top = min(top, childBox->transformedFrameRect().y() + childBox->topmostPosition(false)); + } + } + } + + if (includeSelf && isRelPositioned()) + top += relativePositionOffsetY(); + + if (!includeOverflowInterior && hasOverflowClip()) + return top; + int relativeOffset = includeSelf && isRelPositioned() ? relativePositionOffsetY() : 0; + + if (includeSelf) + top = min(top, topLayoutOverflow() + relativeOffset); + + if (m_positionedObjects) { + RenderBox* r; + Iterator end = m_positionedObjects->end(); + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + r = *it; + // Fixed positioned objects do not scroll and thus should not constitute + // part of the topmost position. + if (r->style()->position() != FixedPosition) { + // FIXME: Should work for overflow sections too. + // If a positioned object lies completely to the left of the root it will be unreachable via scrolling. + // Therefore we should not allow it to contribute to the topmost position. + IntRect transformedR = r->transformedFrameRect(); + if (!isRenderView() || transformedR.x() + transformedR.width() > 0 || transformedR.x() + r->rightmostPosition(false) > 0) { + int tp = transformedR.y() + r->topmostPosition(false); + top = min(top, tp + relativeOffset); + } + } + } + } + + if (hasColumns()) { + ColumnInfo* colInfo = columnInfo(); + for (unsigned i = 0; i < columnCount(colInfo); i++) + top = min(top, columnRectAt(colInfo, i).y() + relativeOffset); + return top; + } + + if (m_floatingObjects) { + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it) { + if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { + int tp = r->top() + r->m_renderer->marginTop() + r->m_renderer->topmostPosition(false); + top = min(top, tp + relativeOffset); + } + } + } + + if (!includeSelf && firstRootBox()) + top = min(top, firstRootBox()->selectionTop() + relativeOffset); + + if (applyTransform == IncludeTransform && includeSelf && layer() && layer()->hasTransform()) { + int bottom = lowestPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int right = rightmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int left = leftmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + IntRect transformRect = applyLayerTransformToRect(IntRect(left, top, right - left, bottom - top)); + return transformRect.y(); + } + + return top; +} + +int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const +{ + IntRect transformedRect = transformedFrameRect(); + int transformedBottom = includeSelf && transformedRect.width() > 0 ? transformedRect.height() : 0; + + if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) + return transformedBottom; + + if (!firstChild() && (!transformedRect.width() || !transformedRect.height())) + return transformedBottom; + + int bottom = includeSelf && width() > 0 ? height() : 0; + if (!hasColumns()) { // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. // For now, we have to descend into all the children, since we may have a huge abs div inside @@ -3343,7 +3463,7 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { if (!c->isFloatingOrPositioned() && c->isBox()) { RenderBox* childBox = toRenderBox(c); - bottom = max(bottom, childBox->y() + childBox->lowestPosition(false)); + bottom = max(bottom, childBox->transformedFrameRect().y() + childBox->lowestPosition(false)); } } } @@ -3369,8 +3489,9 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) // FIXME: Should work for overflow sections too. // If a positioned object lies completely to the left of the root it will be unreachable via scrolling. // Therefore we should not allow it to contribute to the lowest position. - if (!isRenderView() || r->x() + r->width() > 0 || r->x() + r->rightmostPosition(false) > 0) { - int lp = r->y() + r->lowestPosition(false); + IntRect transformedR = r->transformedFrameRect(); + if (!isRenderView() || transformedR.x() + transformedR.width() > 0 || transformedR.x() + r->rightmostPosition(false) > 0) { + int lp = transformedR.y() + r->lowestPosition(false); bottom = max(bottom, lp + relativeOffset); } } @@ -3389,7 +3510,7 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it ) { if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { - int lp = r->m_top + r->m_renderer->marginTop() + r->m_renderer->lowestPosition(false); + int lp = r->top() + r->m_renderer->marginTop() + r->m_renderer->lowestPosition(false); bottom = max(bottom, lp + relativeOffset); } } @@ -3408,24 +3529,36 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) while (currBox && currBox->isFloatingOrPositioned()) currBox = currBox->previousSiblingBox(); if (currBox) { - int childBottomEdge = currBox->y() + currBox->height() + currBox->collapsedMarginAfter(); // FIXME: "after" is wrong here for lowestPosition. + IntRect transformedCurrBox = currBox->transformedFrameRect(); + int childBottomEdge = transformedCurrBox.y() + transformedCurrBox.height() + currBox->collapsedMarginAfter(); // FIXME: "after" is wrong here for lowestPosition. bottom = max(bottom, childBottomEdge + paddingBottom() + relativeOffset); } } } - + + if (applyTransform == IncludeTransform && includeSelf && layer() && layer()->hasTransform()) { + int top = topmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int right = rightmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int left = leftmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + IntRect transformRect = applyLayerTransformToRect(IntRect(left, top, right - left, bottom - top)); + return transformRect.height(); + } + return bottom; } -int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const +int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const { - int right = includeSelf && height() > 0 ? width() : 0; + IntRect transformedRect = transformedFrameRect(); + int transformedRight = includeSelf && transformedRect.height() > 0 ? transformedRect.width() : 0; if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) - return right; + return transformedRight; - if (!firstChild() && (!width() || !height())) - return right; + if (!firstChild() && (!transformedRect.width() || !transformedRect.height())) + return transformedRight; + + int right = includeSelf && height() > 0 ? width() : 0; if (!hasColumns()) { // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. @@ -3435,7 +3568,7 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { if (!c->isFloatingOrPositioned() && c->isBox()) { RenderBox* childBox = toRenderBox(c); - right = max(right, childBox->x() + childBox->rightmostPosition(false)); + right = max(right, childBox->transformedFrameRect().x() + childBox->rightmostPosition(false)); } } } @@ -3462,8 +3595,9 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel // FIXME: Should work for overflow sections too. // If a positioned object lies completely above the root it will be unreachable via scrolling. // Therefore we should not allow it to contribute to the rightmost position. - if (!isRenderView() || r->y() + r->height() > 0 || r->y() + r->lowestPosition(false) > 0) { - int rp = r->x() + r->rightmostPosition(false); + IntRect transformedR = r->transformedFrameRect(); + if (!isRenderView() || transformedR.y() + transformedR.height() > 0 || transformedR.y() + r->lowestPosition(false) > 0) { + int rp = transformedR.x() + r->rightmostPosition(false); right = max(right, rp + relativeOffset); } } @@ -3472,12 +3606,19 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel if (hasColumns()) { // This only matters for LTR - if (style()->direction() == LTR) { + if (style()->isLeftToRightDirection()) { ColumnInfo* colInfo = columnInfo(); unsigned count = columnCount(colInfo); if (count) right = max(columnRectAt(colInfo, count - 1).right() + relativeOffset, right); } + if (applyTransform == IncludeTransform && includeSelf && layer() && layer()->hasTransform()) { + int top = topmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int bottom = lowestPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int left = leftmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + IntRect transformRect = applyLayerTransformToRect(IntRect(left, top, right - left, bottom - top)); + return transformRect.width(); + } return right; } @@ -3486,7 +3627,7 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it ) { if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { - int rp = r->m_left + r->m_renderer->marginLeft() + r->m_renderer->rightmostPosition(false); + int rp = r->left() + r->m_renderer->marginLeft() + r->m_renderer->rightmostPosition(false); right = max(right, rp + relativeOffset); } } @@ -3500,7 +3641,7 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel // If this node is a root editable element, then the rightmostPosition should account for a caret at the end. // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to. - if (node() && node()->isContentEditable() && node() == node()->rootEditableElement() && style()->direction() == LTR && !paddingRight()) + if (node() && node()->isContentEditable() && node() == node()->rootEditableElement() && style()->isLeftToRightDirection() && !paddingRight()) childRightEdge += 1; right = max(right, childRightEdge + paddingRight() + relativeOffset); } @@ -3509,24 +3650,36 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel for (RenderBox* currBox = firstChildBox(); currBox; currBox = currBox->nextSiblingBox()) { if (currBox->isFloatingOrPositioned()) continue; - int childRightEdge = currBox->x() + currBox->width() + currBox->marginRight(); + IntRect transformedChild = currBox->transformedFrameRect(); + int childRightEdge = transformedChild.x() + transformedChild.width() + currBox->marginRight(); right = max(right, childRightEdge + paddingRight() + relativeOffset); } } } + + if (applyTransform == IncludeTransform && includeSelf && layer() && layer()->hasTransform()) { + int top = topmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int bottom = lowestPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int left = leftmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + IntRect transformRect = applyLayerTransformToRect(IntRect(left, top, right - left, bottom - top)); + return transformRect.width(); + } return right; } -int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const +int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const { - int left = includeSelf && height() > 0 ? 0 : width(); + IntRect transformedRect = transformedFrameRect(); + int transformedLeft = includeSelf && transformedRect.height() > 0 ? 0 : transformedRect.width(); if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) - return left; + return transformedLeft; - if (!firstChild() && (!width() || !height())) - return left; + if (!firstChild() && (!transformedRect.width() || !transformedRect.height())) + return transformedLeft; + + int left = includeSelf && height() > 0 ? 0 : width(); if (!hasColumns()) { // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. @@ -3536,7 +3689,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { if (!c->isFloatingOrPositioned() && c->isBox()) { RenderBox* childBox = toRenderBox(c); - left = min(left, childBox->x() + childBox->leftmostPosition(false)); + left = min(left, childBox->transformedFrameRect().x() + childBox->leftmostPosition(false)); } } } @@ -3563,8 +3716,9 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf // FIXME: Should work for overflow sections too. // If a positioned object lies completely above the root it will be unreachable via scrolling. // Therefore we should not allow it to contribute to the leftmost position. - if (!isRenderView() || r->y() + r->height() > 0 || r->y() + r->lowestPosition(false) > 0) { - int lp = r->x() + r->leftmostPosition(false); + IntRect transformedR = r->transformedFrameRect(); + if (!isRenderView() || transformedR.y() + transformedR.height() > 0 || transformedR.y() + r->lowestPosition(false) > 0) { + int lp = transformedR.x() + r->leftmostPosition(false); left = min(left, lp + relativeOffset); } } @@ -3573,7 +3727,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf if (hasColumns()) { // This only matters for RTL - if (style()->direction() == RTL) { + if (!style()->isLeftToRightDirection()) { ColumnInfo* colInfo = columnInfo(); unsigned count = columnCount(colInfo); if (count) @@ -3587,7 +3741,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it ) { if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { - int lp = r->m_left + r->m_renderer->marginLeft() + r->m_renderer->leftmostPosition(false); + int lp = r->left() + r->m_renderer->marginLeft() + r->m_renderer->leftmostPosition(false); left = min(left, lp + relativeOffset); } } @@ -3597,51 +3751,31 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf for (InlineFlowBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) left = min(left, (int)currBox->x() + relativeOffset); } + + if (applyTransform == IncludeTransform && includeSelf && layer() && layer()->hasTransform()) { + int top = topmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int bottom = lowestPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int right = rightmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + IntRect transformRect = applyLayerTransformToRect(IntRect(left, top, right - left, bottom - top)); + return transformRect.x(); + } return left; } -int -RenderBlock::leftBottom() -{ - if (!m_floatingObjects) return 0; - int bottom = 0; - FloatingObject* r; - DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); - for ( ; (r = it.current()); ++it ) - if (r->m_bottom > bottom && r->type() == FloatingObject::FloatLeft) - bottom = r->m_bottom; - - return bottom; -} - -int -RenderBlock::rightBottom() -{ - if (!m_floatingObjects) return 0; - int bottom = 0; - FloatingObject* r; - DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); - for ( ; (r = it.current()); ++it ) - if (r->m_bottom>bottom && r->type() == FloatingObject::FloatRight) - bottom = r->m_bottom; - - return bottom; -} - -void RenderBlock::markLinesDirtyInVerticalRange(int top, int bottom, RootInlineBox* highest) +void RenderBlock::markLinesDirtyInBlockRange(int logicalTop, int logicalBottom, RootInlineBox* highest) { - if (top >= bottom) + if (logicalTop >= logicalBottom) return; RootInlineBox* lowestDirtyLine = lastRootBox(); RootInlineBox* afterLowest = lowestDirtyLine; - while (lowestDirtyLine && lowestDirtyLine->blockHeight() >= bottom) { + while (lowestDirtyLine && lowestDirtyLine->blockLogicalHeight() >= logicalBottom) { afterLowest = lowestDirtyLine; lowestDirtyLine = lowestDirtyLine->prevRootBox(); } - while (afterLowest && afterLowest != highest && afterLowest->blockHeight() >= top) { + while (afterLowest && afterLowest != highest && afterLowest->blockLogicalHeight() >= logicalTop) { afterLowest->markDirty(); afterLowest = afterLowest->prevRootBox(); } @@ -3678,6 +3812,7 @@ void RenderBlock::clearFloats() // out of flow (like floating/positioned elements), and we also skip over any objects that may have shifted // to avoid floats. bool parentHasFloats = false; + RenderBlock* parentBlock = toRenderBlock(parent()); RenderObject* prev = previousSibling(); while (prev && (prev->isFloatingOrPositioned() || !prev->isBox() || !prev->isRenderBlock() || toRenderBlock(prev)->avoidsFloats())) { if (prev->isFloating()) @@ -3686,18 +3821,16 @@ void RenderBlock::clearFloats() } // First add in floats from the parent. - int offset = y(); - if (parentHasFloats) { - RenderBlock* parentBlock = toRenderBlock(parent()); - addIntrudingFloats(parentBlock, parentBlock->borderLeft() + parentBlock->paddingLeft(), offset); - } + int logicalTopOffset = logicalTop(); + if (parentHasFloats) + addIntrudingFloats(parentBlock, parentBlock->logicalLeftOffsetForContent(), logicalTopOffset); - int xoffset = 0; + int logicalLeftOffset = 0; if (prev) - offset -= toRenderBox(prev)->y(); - else if (parent()->isBox()) { - prev = parent(); - xoffset += toRenderBox(prev)->borderLeft() + toRenderBox(prev)->paddingLeft(); + logicalTopOffset -= toRenderBox(prev)->logicalTop(); + else { + prev = parentBlock; + logicalLeftOffset += parentBlock->logicalLeftOffsetForContent(); } // Add overhanging floats from the previous RenderBlock, but only if it has a float that intrudes into our space. @@ -3705,29 +3838,31 @@ void RenderBlock::clearFloats() return; RenderBlock* block = toRenderBlock(prev); - if (block->m_floatingObjects && block->floatBottom() > offset) - addIntrudingFloats(block, xoffset, offset); + if (block->m_floatingObjects && block->lowestFloatLogicalBottom() > logicalTopOffset) + addIntrudingFloats(block, logicalLeftOffset, logicalTopOffset); if (childrenInline()) { - int changeTop = numeric_limits<int>::max(); - int changeBottom = numeric_limits<int>::min(); + int changeLogicalTop = numeric_limits<int>::max(); + int changeLogicalBottom = numeric_limits<int>::min(); if (m_floatingObjects) { for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) { FloatingObject* oldFloatingObject = floatMap.get(f->m_renderer); + int logicalBottom = logicalBottomForFloat(f); if (oldFloatingObject) { - if (f->m_width != oldFloatingObject->m_width || f->m_left != oldFloatingObject->m_left) { - changeTop = 0; - changeBottom = max(changeBottom, max(f->m_bottom, oldFloatingObject->m_bottom)); - } else if (f->m_bottom != oldFloatingObject->m_bottom) { - changeTop = min(changeTop, min(f->m_bottom, oldFloatingObject->m_bottom)); - changeBottom = max(changeBottom, max(f->m_bottom, oldFloatingObject->m_bottom)); + int oldLogicalBottom = logicalBottomForFloat(oldFloatingObject); + if (logicalWidthForFloat(f) != logicalWidthForFloat(oldFloatingObject) || logicalLeftForFloat(f) != logicalLeftForFloat(oldFloatingObject)) { + changeLogicalTop = 0; + changeLogicalBottom = max(changeLogicalBottom, max(logicalBottom, oldLogicalBottom)); + } else if (logicalBottom != oldLogicalBottom) { + changeLogicalTop = min(changeLogicalTop, min(logicalBottom, oldLogicalBottom)); + changeLogicalBottom = max(changeLogicalBottom, max(logicalBottom, oldLogicalBottom)); } floatMap.remove(f->m_renderer); delete oldFloatingObject; } else { - changeTop = 0; - changeBottom = max(changeBottom, f->m_bottom); + changeLogicalTop = 0; + changeLogicalBottom = max(changeLogicalBottom, logicalBottom); } } } @@ -3736,39 +3871,37 @@ void RenderBlock::clearFloats() for (RendererToFloatInfoMap::iterator it = floatMap.begin(); it != end; ++it) { FloatingObject* floatingObject = (*it).second; if (!floatingObject->m_isDescendant) { - changeTop = 0; - changeBottom = max(changeBottom, floatingObject->m_bottom); + changeLogicalTop = 0; + changeLogicalBottom = max(changeLogicalBottom, logicalBottomForFloat(floatingObject)); } } deleteAllValues(floatMap); - markLinesDirtyInVerticalRange(changeTop, changeBottom); + markLinesDirtyInBlockRange(changeLogicalTop, changeLogicalBottom); } } -int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bool makeChildPaintOtherFloats) +int RenderBlock::addOverhangingFloats(RenderBlock* child, int logicalLeftOffset, int logicalTopOffset, bool makeChildPaintOtherFloats) { // Prevent floats from being added to the canvas by the root element, e.g., <html>. - if (child->hasOverflowClip() || !child->containsFloats() || child->isRoot() || child->hasColumns() || child->isBlockFlowRoot()) + if (child->hasOverflowClip() || !child->containsFloats() || child->isRoot() || child->hasColumns() || child->isWritingModeRoot()) return 0; - int lowestFloatBottom = 0; + int lowestFloatLogicalBottom = 0; // Floats that will remain the child's responsibility to paint should factor into its // overflow. DeprecatedPtrListIterator<FloatingObject> it(*child->m_floatingObjects); for (FloatingObject* r; (r = it.current()); ++it) { - int bottom = child->y() + r->m_bottom; - lowestFloatBottom = max(lowestFloatBottom, bottom); + int logicalBottom = child->logicalTop() + logicalBottomForFloat(r); + lowestFloatLogicalBottom = max(lowestFloatLogicalBottom, logicalBottom); - if (bottom > height()) { + if (logicalBottom > logicalHeight()) { // If the object is not in the list, we add it now. if (!containsFloat(r->m_renderer)) { - FloatingObject *floatingObj = new FloatingObject(r->type()); - floatingObj->m_top = r->m_top - yoff; - floatingObj->m_bottom = r->m_bottom - yoff; - floatingObj->m_left = r->m_left - xoff; - floatingObj->m_width = r->m_width; + int leftOffset = style()->isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset; + int topOffset = style()->isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset; + FloatingObject* floatingObj = new FloatingObject(r->type(), IntRect(r->left() - leftOffset, r->top() - topOffset, r->width(), r->height())); floatingObj->m_renderer = r->m_renderer; // The nearest enclosing layer always paints the float (so that zindex and stacking @@ -3797,45 +3930,48 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bo r->m_shouldPaint = true; if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) - child->addOverflowFromChild(r->m_renderer, IntSize(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop())); + child->addOverflowFromChild(r->m_renderer, IntSize(r->left() + r->m_renderer->marginLeft(), r->top() + r->m_renderer->marginTop())); } - return lowestFloatBottom; + return lowestFloatLogicalBottom; } -void RenderBlock::addIntrudingFloats(RenderBlock* prev, int xoff, int yoff) +void RenderBlock::addIntrudingFloats(RenderBlock* prev, int logicalLeftOffset, int logicalTopOffset) { // If the parent or previous sibling doesn't have any floats to add, don't bother. if (!prev->m_floatingObjects) return; + logicalLeftOffset += (style()->isHorizontalWritingMode() ? marginLeft() : marginTop()); + DeprecatedPtrListIterator<FloatingObject> it(*prev->m_floatingObjects); for (FloatingObject *r; (r = it.current()); ++it) { - if (r->m_bottom > yoff) { + if (logicalBottomForFloat(r) > logicalTopOffset) { // The object may already be in our list. Check for it up front to avoid // creating duplicate entries. FloatingObject* f = 0; if (m_floatingObjects) { DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); while ((f = it.current())) { - if (f->m_renderer == r->m_renderer) break; + if (f->m_renderer == r->m_renderer) + break; ++it; } } if (!f) { - FloatingObject *floatingObj = new FloatingObject(r->type()); - floatingObj->m_top = r->m_top - yoff; - floatingObj->m_bottom = r->m_bottom - yoff; - floatingObj->m_left = r->m_left - xoff; + int leftOffset = style()->isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset; + int topOffset = style()->isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset; + + FloatingObject* floatingObj = new FloatingObject(r->type(), IntRect(r->left() - leftOffset, r->top() - topOffset, r->width(), r->height())); + // Applying the child's margin makes no sense in the case where the child was passed in. - // since his own margin was added already through the subtraction of the |xoff| variable - // above. |xoff| will equal -flow->marginLeft() in this case, so it's already been taken - // into account. Only apply this code if |child| is false, since otherwise the left margin + // since this margin was added already through the modification of the |logicalLeftOffset| variable + // above. |logicalLeftOffset| will equal the margin in this case, so it's already been taken + // into account. Only apply this code if prev is the parent, since otherwise the left margin // will get applied twice. if (prev != parent()) - floatingObj->m_left += prev->marginLeft(); - floatingObj->m_left -= marginLeft(); + floatingObj->setLeft(floatingObj->left() + (style()->isHorizontalWritingMode() ? prev->marginLeft() : prev->marginTop())); + floatingObj->m_shouldPaint = false; // We are not in the direct inheritance chain for this float. We will never paint it. - floatingObj->m_width = r->m_width; floatingObj->m_renderer = r->m_renderer; // We create the floating object list lazily. @@ -3890,41 +4026,6 @@ void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove } } -void RenderBlock::markDescendantBlocksAndLinesForLayout(bool inLayout) -{ - if (!m_everHadLayout) - return; - - setChildNeedsLayout(true, !inLayout); - - // Iterate over our children and mark them as needed. - if (!childrenInline()) { - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (child->isFloatingOrPositioned()) - continue; - child->markDescendantBlocksAndLinesForLayout(inLayout); - } - } - - // Walk our floating objects and mark them too. - if (m_floatingObjects) { - DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); - while (it.current()) { - if (it.current()->m_renderer->isRenderBlock()) - it.current()->m_renderer->markDescendantBlocksAndLinesForLayout(inLayout); - ++it; - } - } - - if (m_positionedObjects) { - // FIXME: Technically we don't have to mark the positioned objects if we're the block - // that established the columns, but we don't really have that information here. - Iterator end = m_positionedObjects->end(); - for (Iterator it = m_positionedObjects->begin(); it != end; ++it) - (*it)->markDescendantBlocksAndLinesForLayout(); - } -} - int RenderBlock::getClearDelta(RenderBox* child, int yPos) { // There is no need to compute clearance if we have no floats. @@ -3938,13 +4039,13 @@ int RenderBlock::getClearDelta(RenderBox* child, int yPos) case CNONE: break; case CLEFT: - bottom = leftBottom(); + bottom = lowestFloatLogicalBottom(FloatingObject::FloatLeft); break; case CRIGHT: - bottom = rightBottom(); + bottom = lowestFloatLogicalBottom(FloatingObject::FloatRight); break; case CBOTH: - bottom = floatBottom(); + bottom = lowestFloatLogicalBottom(); break; } @@ -3972,7 +4073,7 @@ int RenderBlock::getClearDelta(RenderBox* child, int yPos) if (childWidthAtY <= widthAtY) return y - yPos; - y = nextFloatBottomBelow(y); + y = nextFloatLogicalBottomBelow(y); ASSERT(y >= yPos); if (y < yPos) break; @@ -3999,7 +4100,7 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu // Check if we need to do anything at all. IntRect overflowBox = visibleOverflowRect(); overflowBox.move(tx, ty); - if (!overflowBox.intersects(result.rectFromPoint(_x, _y))) + if (!overflowBox.intersects(result.rectForPoint(_x, _y))) return false; } @@ -4013,7 +4114,7 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu // If we have clipping, then we can't have any spillout. bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); bool useClip = (hasControlClip() || useOverflowClip); - IntRect hitTestArea(result.rectFromPoint(_x, _y)); + IntRect hitTestArea(result.rectForPoint(_x, _y)); bool checkChildren = !useClip || (hasControlClip() ? controlClipRect(tx, ty).intersects(hitTestArea) : overflowClipRect(tx, ty).intersects(hitTestArea)); if (checkChildren) { // Hit test descendants first. @@ -4042,7 +4143,7 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu // Now hit test our background if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) { IntRect boundsRect(tx, ty, width(), height()); - if (visibleToHitTesting() && boundsRect.intersects(result.rectFromPoint(_x, _y))) { + if (visibleToHitTesting() && boundsRect.intersects(result.rectForPoint(_x, _y))) { updateHitTestResult(result, IntPoint(_x - tx, _y - ty)); if (!result.addNodeToRectBasedTestResult(node(), _x, _y, boundsRect)) return true; @@ -4066,8 +4167,8 @@ bool RenderBlock::hitTestFloats(const HitTestRequest& request, HitTestResult& re DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for (it.toLast(); (floatingObject = it.current()); --it) { if (floatingObject->m_shouldPaint && !floatingObject->m_renderer->hasSelfPaintingLayer()) { - int xOffset = tx + floatingObject->m_left + floatingObject->m_renderer->marginLeft() - floatingObject->m_renderer->x(); - int yOffset = ty + floatingObject->m_top + floatingObject->m_renderer->marginTop() - floatingObject->m_renderer->y(); + int xOffset = tx + floatingObject->left() + floatingObject->m_renderer->marginLeft() - floatingObject->m_renderer->x(); + int yOffset = ty + floatingObject->top() + floatingObject->m_renderer->marginTop() - floatingObject->m_renderer->y(); if (floatingObject->m_renderer->hitTest(request, result, IntPoint(x, y), xOffset, yOffset)) { updateHitTestResult(result, IntPoint(x - xOffset, y - yOffset)); return true; @@ -4096,13 +4197,13 @@ bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& r currYOffset += colRect.height(); colRect.move(tx, ty); - if (colRect.intersects(result.rectFromPoint(x, y))) { + if (colRect.intersects(result.rectForPoint(x, y))) { // The point is inside this column. // Adjust tx and ty to change where we hit test. int finalX = tx + currXOffset; int finalY = ty + currYOffset; - if (result.isRectBasedTest() && !colRect.contains(result.rectFromPoint(x, y))) + if (result.isRectBasedTest() && !colRect.contains(result.rectForPoint(x, y))) hitTestContents(request, result, x, y, finalX, finalY, hitTestAction); else return hitTestContents(request, result, x, y, finalX, finalY, hitTestAction) || (hitTestAction == HitTestFloat && hitTestFloats(request, result, x, y, finalX, finalY)); @@ -4443,7 +4544,7 @@ IntRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const int colHeight = colInfo->columnHeight(); int colTop = borderTop() + paddingTop(); int colGap = columnGap(); - int colLeft = style()->direction() == LTR ? + int colLeft = style()->isLeftToRightDirection() ? borderLeft() + paddingLeft() + (index * (colWidth + colGap)) : borderLeft() + paddingLeft() + contentWidth() - colWidth - (index * (colWidth + colGap)); return IntRect(colLeft, colTop, colWidth, colHeight); @@ -4485,8 +4586,8 @@ bool RenderBlock::layoutColumns(bool hasSpecifiedPageHeight, int pageHeight, Lay if (columnCount(colInfo)) { IntRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1); - int overflowLeft = style()->direction() == RTL ? min(0, lastRect.x()) : 0; - int overflowRight = style()->direction() == LTR ? max(width(), lastRect.x() + lastRect.width()) : 0; + int overflowLeft = !style()->isLeftToRightDirection() ? min(0, lastRect.x()) : 0; + int overflowRight = style()->isLeftToRightDirection() ? max(width(), lastRect.x() + lastRect.width()) : 0; int overflowHeight = borderTop() + paddingTop() + colInfo->columnHeight(); setLogicalHeight(overflowHeight + borderBottom() + paddingBottom() + horizontalScrollbarHeight()); @@ -4607,8 +4708,8 @@ void RenderBlock::computePreferredLogicalWidths() updateFirstLetter(); - if (!isTableCell() && style()->width().isFixed() && style()->width().value() > 0) - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value()); + if (!isTableCell() && style()->logicalWidth().isFixed() && style()->logicalWidth().value() > 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->logicalWidth().value()); else { m_minPreferredLogicalWidth = 0; m_maxPreferredLogicalWidth = 0; @@ -4635,18 +4736,18 @@ void RenderBlock::computePreferredLogicalWidths() } } - if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { - m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); - m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); + if (style()->logicalMinWidth().isFixed() && style()->logicalMinWidth().value() > 0) { + m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMinWidth().value())); + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMinWidth().value())); } - if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { - m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); - m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); + if (style()->logicalMaxWidth().isFixed() && style()->logicalMaxWidth().value() != undefinedLength) { + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMaxWidth().value())); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMaxWidth().value())); } int toAdd = 0; - toAdd = borderAndPaddingWidth(); + toAdd = borderAndPaddingLogicalWidth(); if (hasOverflowClip() && style()->overflowY() == OSCROLL) toAdd += verticalScrollbarWidth(); @@ -4732,7 +4833,7 @@ static int getBorderPaddingMargin(const RenderBoxModelObject* child, bool endOfI { RenderStyle* cstyle = child->style(); int result = 0; - bool leftSide = (cstyle->direction() == LTR) ? !endOfInline : endOfInline; + bool leftSide = (cstyle->isLeftToRightDirection()) ? !endOfInline : endOfInline; result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()), (leftSide ? cstyle->marginLeft() : cstyle->marginRight())); @@ -5135,15 +5236,15 @@ bool RenderBlock::hasLineIfEmpty() const return false; } -int RenderBlock::lineHeight(bool firstLine, bool isRootLineBox) const +int RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const { // Inline blocks are replaced elements. Otherwise, just pass off to // the base class. If we're being queried as though we're the root line // box, then the fact that we're an inline-block is irrelevant, and we behave // just like a block. - if (isReplaced() && !isRootLineBox) - return height() + marginTop() + marginBottom(); - + if (isReplaced() && linePositionMode == PositionOnContainingLine) + return RenderBox::lineHeight(firstLine, direction, linePositionMode); + if (firstLine && document()->usesFirstLineRules()) { RenderStyle* s = style(firstLine); if (s != style()) @@ -5156,16 +5257,17 @@ int RenderBlock::lineHeight(bool firstLine, bool isRootLineBox) const return m_lineHeight; } -int RenderBlock::baselinePosition(bool b, bool isRootLineBox) const +int RenderBlock::baselinePosition(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const { // Inline blocks are replaced elements. Otherwise, just pass off to // the base class. If we're being queried as though we're the root line // box, then the fact that we're an inline-block is irrelevant, and we behave // just like a block. - if (isReplaced() && !isRootLineBox) { + if (isReplaced() && linePositionMode == PositionOnContainingLine) { // For "leaf" theme objects, let the theme decide what the baseline position is. // FIXME: Might be better to have a custom CSS property instead, so that if the theme // is turned off, checkboxes/radios will still have decent baselines. + // FIXME: Need to patch form controls to deal with vertical lines. if (style()->hasAppearance() && !theme()->isControlContainer(style()->appearance())) return theme()->baselinePosition(this); @@ -5175,22 +5277,29 @@ int RenderBlock::baselinePosition(bool b, bool isRootLineBox) const // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled // vertically (e.g., an overflow:hidden block that has had scrollTop moved) or if the baseline is outside // of our content box. - int baselinePos = (layer() && (layer()->marquee() || layer()->verticalScrollbar() || layer()->scrollYOffset() != 0)) ? -1 : lastLineBoxBaseline(); - if (baselinePos != -1 && baselinePos <= borderTop() + paddingTop() + contentHeight()) - return marginTop() + baselinePos; - return height() + marginTop() + marginBottom(); + bool ignoreBaseline = (layer() && (layer()->marquee() || (direction == HorizontalLine ? (layer()->verticalScrollbar() || layer()->scrollYOffset() != 0) + : (layer()->horizontalScrollbar() || layer()->scrollXOffset() != 0)))) || isWritingModeRoot(); + int baselinePos = ignoreBaseline ? -1 : lastLineBoxBaseline(); + + int bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth(); + if (baselinePos != -1 && baselinePos <= bottomOfContent) + return direction == HorizontalLine ? marginTop() + baselinePos : marginRight() + baselinePos; + + return RenderBox::baselinePosition(firstLine, direction, linePositionMode); } - return RenderBox::baselinePosition(b, isRootLineBox); + + const Font& f = style(firstLine)->font(); + return f.ascent() + (lineHeight(firstLine, direction, linePositionMode) - f.height()) / 2; } int RenderBlock::firstLineBoxBaseline() const { - if (!isBlockFlow()) + if (!isBlockFlow() || isWritingModeRoot()) return -1; if (childrenInline()) { if (firstLineBox()) - return firstLineBox()->y() + style(true)->font().ascent(); + return firstLineBox()->logicalTop() + style(true)->font().ascent(); else return -1; } @@ -5199,7 +5308,7 @@ int RenderBlock::firstLineBoxBaseline() const if (!curr->isFloatingOrPositioned()) { int result = curr->firstLineBoxBaseline(); if (result != -1) - return curr->y() + result; // Translate to our coordinate space. + return curr->logicalTop() + result; // Translate to our coordinate space. } } } @@ -5209,28 +5318,33 @@ int RenderBlock::firstLineBoxBaseline() const int RenderBlock::lastLineBoxBaseline() const { - if (!isBlockFlow()) + if (!isBlockFlow() || isWritingModeRoot()) return -1; + LineDirectionMode lineDirection = style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine; + if (childrenInline()) { - if (!firstLineBox() && hasLineIfEmpty()) - return RenderBox::baselinePosition(true, true) + borderTop() + paddingTop(); + if (!firstLineBox() && hasLineIfEmpty()) { + const Font& f = firstLineStyle()->font(); + return f.ascent() + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - f.height()) / 2 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); + } if (lastLineBox()) - return lastLineBox()->y() + style(lastLineBox() == firstLineBox())->font().ascent(); + return lastLineBox()->logicalTop() + style(lastLineBox() == firstLineBox())->font().ascent(); return -1; - } - else { + } else { bool haveNormalFlowChild = false; for (RenderBox* curr = lastChildBox(); curr; curr = curr->previousSiblingBox()) { if (!curr->isFloatingOrPositioned()) { haveNormalFlowChild = true; int result = curr->lastLineBoxBaseline(); if (result != -1) - return curr->y() + result; // Translate to our coordinate space. + return curr->logicalTop() + result; // Translate to our coordinate space. } } - if (!haveNormalFlowChild && hasLineIfEmpty()) - return RenderBox::baselinePosition(true, true) + borderTop() + paddingTop(); + if (!haveNormalFlowChild && hasLineIfEmpty()) { + const Font& f = firstLineStyle()->font(); + return f.ascent() + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - f.height()) / 2 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); + } } return -1; @@ -5279,6 +5393,24 @@ static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderOb return pseudoStyle; } +// CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter +// "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" (Pe), +// "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included" +static inline bool isPunctuationForFirstLetter(UChar c) +{ + CharCategory charCategory = category(c); + return charCategory == Punctuation_Open + || charCategory == Punctuation_Close + || charCategory == Punctuation_InitialQuote + || charCategory == Punctuation_FinalQuote + || charCategory == Punctuation_Other; +} + +static inline bool shouldSkipForFirstLetter(UChar c) +{ + return isSpaceOrNewline(c) || c == noBreakSpace || isPunctuationForFirstLetter(c); +} + void RenderBlock::updateFirstLetter() { if (!document()->usesFirstLetterRules()) @@ -5403,15 +5535,27 @@ void RenderBlock::updateFirstLetter() if (oldText && oldText->length() > 0) { unsigned length = 0; - // account for leading spaces and punctuation - while (length < oldText->length() && (isSpaceOrNewline((*oldText)[length]) || Unicode::isPunct((*oldText)[length]))) + // Account for leading spaces and punctuation. + while (length < oldText->length() && shouldSkipForFirstLetter((*oldText)[length])) length++; - // account for first letter + // Account for first letter. length++; + + // Keep looking for whitespace and allowed punctuation, but avoid + // accumulating just whitespace into the :first-letter. + for (unsigned scanLength = length; scanLength < oldText->length(); ++scanLength) { + UChar c = (*oldText)[scanLength]; + + if (!shouldSkipForFirstLetter(c)) + break; - // construct text fragment for the text after the first letter - // NOTE: this might empty + if (isPunctuationForFirstLetter(c)) + length = scanLength + 1; + } + + // Construct a text fragment for the text after the first letter. + // This text fragment might be empty. RenderTextFragment* remainingText = new (renderArena()) RenderTextFragment(textObj->node() ? textObj->node() : textObj->document(), oldText.get(), length, oldText->length() - length); remainingText->setStyle(textObj->style()); @@ -5553,7 +5697,7 @@ void RenderBlock::adjustForBorderFit(int x, int& left, int& right) const for (; (r = it.current()); ++it) { // Only examine the object if our m_shouldPaint flag is set. if (r->m_shouldPaint) { - int floatLeft = r->m_left - r->m_renderer->x() + r->m_renderer->marginLeft(); + int floatLeft = r->left() - r->m_renderer->x() + r->m_renderer->marginLeft(); int floatRight = floatLeft + r->m_renderer->width(); left = min(left, floatLeft); right = max(right, floatRight); @@ -5605,23 +5749,23 @@ void RenderBlock::clearTruncation() void RenderBlock::setMaxMarginBeforeValues(int pos, int neg) { if (!m_rareData) { - if (pos == RenderBlockRareData::beforePosDefault(this) && neg == RenderBlockRareData::beforeNegDefault(this)) + if (pos == RenderBlockRareData::positiveMarginBeforeDefault(this) && neg == RenderBlockRareData::negativeMarginBeforeDefault(this)) return; m_rareData = new RenderBlockRareData(this); } - m_rareData->m_beforePos = pos; - m_rareData->m_beforeNeg = neg; + m_rareData->m_margins.setPositiveMarginBefore(pos); + m_rareData->m_margins.setNegativeMarginBefore(neg); } void RenderBlock::setMaxMarginAfterValues(int pos, int neg) { if (!m_rareData) { - if (pos == RenderBlockRareData::afterPosDefault(this) && neg == RenderBlockRareData::afterNegDefault(this)) + if (pos == RenderBlockRareData::positiveMarginAfterDefault(this) && neg == RenderBlockRareData::negativeMarginAfterDefault(this)) return; m_rareData = new RenderBlockRareData(this); } - m_rareData->m_afterPos = pos; - m_rareData->m_afterNeg = neg; + m_rareData->m_margins.setPositiveMarginAfter(pos); + m_rareData->m_margins.setNegativeMarginAfter(neg); } void RenderBlock::setPaginationStrut(int strut) @@ -5745,7 +5889,7 @@ IntRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, int* // constructed and this kludge is not called any more. So only the caret size // of an empty :first-line'd block is wrong. I think we can live with that. RenderStyle* currentStyle = firstLineStyle(); - int height = lineHeight(true); + int height = lineHeight(true, currentStyle->isHorizontalWritingMode() ? HorizontalLine : VerticalLine); enum CaretAlignment { alignLeft, alignRight, alignCenter }; @@ -5754,7 +5898,7 @@ IntRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, int* switch (currentStyle->textAlign()) { case TAAUTO: case JUSTIFY: - if (currentStyle->direction() == RTL) + if (!currentStyle->isLeftToRightDirection()) alignment = alignRight; break; case LEFT: @@ -6020,7 +6164,213 @@ void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, int& d } } } - + +int RenderBlock::collapsedMarginBeforeForChild(RenderBox* child) const +{ + // If the child has the same directionality as we do, then we can just return its + // collapsed margin. + if (!child->isWritingModeRoot()) + return child->collapsedMarginBefore(); + + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the collapsed margin for the opposite edge. + if (child->style()->isHorizontalWritingMode() == style()->isHorizontalWritingMode()) + return child->collapsedMarginAfter(); + + // The child is perpendicular to us, which means its margins don't collapse but are on the + // "logical left/right" sides of the child box. We can just return the raw margin in this case. + return marginBeforeForChild(child); +} + +int RenderBlock::collapsedMarginAfterForChild(RenderBox* child) const +{ + // If the child has the same directionality as we do, then we can just return its + // collapsed margin. + if (!child->isWritingModeRoot()) + return child->collapsedMarginAfter(); + + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the collapsed margin for the opposite edge. + if (child->style()->isHorizontalWritingMode() == style()->isHorizontalWritingMode()) + return child->collapsedMarginBefore(); + + // The child is perpendicular to us, which means its margins don't collapse but are on the + // "logical left/right" side of the child box. We can just return the raw margin in this case. + return marginAfterForChild(child); +} + +int RenderBlock::marginBeforeForChild(RenderBoxModelObject* child) const +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return child->marginTop(); + case BottomToTopWritingMode: + return child->marginBottom(); + case LeftToRightWritingMode: + return child->marginLeft(); + case RightToLeftWritingMode: + return child->marginRight(); + } + ASSERT_NOT_REACHED(); + return child->marginTop(); +} + +int RenderBlock::marginAfterForChild(RenderBoxModelObject* child) const +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return child->marginBottom(); + case BottomToTopWritingMode: + return child->marginTop(); + case LeftToRightWritingMode: + return child->marginRight(); + case RightToLeftWritingMode: + return child->marginLeft(); + } + ASSERT_NOT_REACHED(); + return child->marginBottom(); +} + +int RenderBlock::marginStartForChild(RenderBoxModelObject* child) const +{ + if (style()->isHorizontalWritingMode()) + return style()->isLeftToRightDirection() ? child->marginLeft() : child->marginRight(); + return style()->isLeftToRightDirection() ? child->marginTop() : child->marginBottom(); +} + +int RenderBlock::marginEndForChild(RenderBoxModelObject* child) const +{ + if (style()->isHorizontalWritingMode()) + return style()->isLeftToRightDirection() ? child->marginRight() : child->marginLeft(); + return style()->isLeftToRightDirection() ? child->marginBottom() : child->marginTop(); +} + +void RenderBlock::setMarginStartForChild(RenderBox* child, int margin) +{ + if (style()->isHorizontalWritingMode()) { + if (style()->isLeftToRightDirection()) + child->setMarginLeft(margin); + else + child->setMarginRight(margin); + } else { + if (style()->isLeftToRightDirection()) + child->setMarginTop(margin); + else + child->setMarginBottom(margin); + } +} + +void RenderBlock::setMarginEndForChild(RenderBox* child, int margin) +{ + if (style()->isHorizontalWritingMode()) { + if (style()->isLeftToRightDirection()) + child->setMarginRight(margin); + else + child->setMarginLeft(margin); + } else { + if (style()->isLeftToRightDirection()) + child->setMarginBottom(margin); + else + child->setMarginTop(margin); + } +} + +void RenderBlock::setMarginBeforeForChild(RenderBox* child, int margin) +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + child->setMarginTop(margin); + break; + case BottomToTopWritingMode: + child->setMarginBottom(margin); + break; + case LeftToRightWritingMode: + child->setMarginLeft(margin); + break; + case RightToLeftWritingMode: + child->setMarginRight(margin); + break; + } +} + +void RenderBlock::setMarginAfterForChild(RenderBox* child, int margin) +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + child->setMarginBottom(margin); + break; + case BottomToTopWritingMode: + child->setMarginTop(margin); + break; + case LeftToRightWritingMode: + child->setMarginRight(margin); + break; + case RightToLeftWritingMode: + child->setMarginLeft(margin); + break; + } +} + +RenderBlock::MarginValues RenderBlock::marginValuesForChild(RenderBox* child) +{ + int childBeforePositive = 0; + int childBeforeNegative = 0; + int childAfterPositive = 0; + int childAfterNegative = 0; + + int beforeMargin = 0; + int afterMargin = 0; + + RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0; + + // If the child has the same directionality as we do, then we can just return its + // margins in the same direction. + if (!child->isWritingModeRoot()) { + if (childRenderBlock) { + childBeforePositive = childRenderBlock->maxPositiveMarginBefore(); + childBeforeNegative = childRenderBlock->maxNegativeMarginBefore(); + childAfterPositive = childRenderBlock->maxPositiveMarginAfter(); + childAfterNegative = childRenderBlock->maxNegativeMarginAfter(); + } else { + beforeMargin = child->marginBefore(); + afterMargin = child->marginAfter(); + } + } else if (child->style()->isHorizontalWritingMode() == style()->isHorizontalWritingMode()) { + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the margins for the opposite edges. + if (childRenderBlock) { + childBeforePositive = childRenderBlock->maxPositiveMarginAfter(); + childBeforeNegative = childRenderBlock->maxNegativeMarginAfter(); + childAfterPositive = childRenderBlock->maxPositiveMarginBefore(); + childAfterNegative = childRenderBlock->maxNegativeMarginBefore(); + } else { + beforeMargin = child->marginAfter(); + afterMargin = child->marginBefore(); + } + } else { + // The child is perpendicular to us, which means its margins don't collapse but are on the + // "logical left/right" sides of the child box. We can just return the raw margin in this case. + beforeMargin = marginBeforeForChild(child); + afterMargin = marginAfterForChild(child); + } + + // Resolve uncollapsing margins into their positive/negative buckets. + if (beforeMargin) { + if (beforeMargin > 0) + childBeforePositive = beforeMargin; + else + childBeforeNegative = -beforeMargin; + } + if (afterMargin) { + if (afterMargin > 0) + childAfterPositive = afterMargin; + else + childAfterNegative = -afterMargin; + } + + return MarginValues(childBeforePositive, childBeforeNegative, childAfterPositive, childAfterNegative); +} + const char* RenderBlock::renderName() const { if (isBody()) diff --git a/WebCore/rendering/RenderBlock.h b/WebCore/rendering/RenderBlock.h index 0682039..66c8659 100644 --- a/WebCore/rendering/RenderBlock.h +++ b/WebCore/rendering/RenderBlock.h @@ -57,8 +57,8 @@ public: virtual void destroy(); // These two functions are overridden for inline-block. - virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; - virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; + virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + virtual int baselinePosition(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } const RenderLineBoxList* lineBoxes() const { return &m_lineBoxes; } @@ -90,14 +90,7 @@ public: void markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove = 0, bool inLayout = true); void markPositionedObjectsForLayout(); - void markForPaginationRelayout() - { - if (isTable()) - markDescendantBlocksAndLinesForLayout(); - else - setChildNeedsLayout(true, false); - } - virtual void markDescendantBlocksAndLinesForLayout(bool inLayout = true); + virtual void markForPaginationRelayoutIfNeeded(); bool containsFloats() { return m_floatingObjects && !m_floatingObjects->isEmpty(); } bool containsFloat(RenderObject*); @@ -105,10 +98,11 @@ public: int availableLogicalWidthForLine(int position, bool firstLine) const; int logicalRightOffsetForLine(int position, bool firstLine) const { return logicalRightOffsetForLine(position, logicalRightOffsetForContent(), firstLine); } int logicalLeftOffsetForLine(int position, bool firstLine) const { return logicalLeftOffsetForLine(position, logicalLeftOffsetForContent(), firstLine); } - - virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; - virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; - virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + + virtual int topmostPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; + virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; + virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; + virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; virtual VisiblePosition positionForPoint(const IntPoint&); @@ -169,6 +163,51 @@ public: void setPaginationStrut(int strut); void setPageY(int y); + // Accessors for logical width/height and margins in the containing block's block-flow direction. + enum ApplyLayoutDeltaMode { ApplyLayoutDelta, DoNotApplyLayoutDelta }; + int logicalWidthForChild(RenderBox* child) { return style()->isHorizontalWritingMode() ? child->width() : child->height(); } + int logicalHeightForChild(RenderBox* child) { return style()->isHorizontalWritingMode() ? child->height() : child->width(); } + int logicalTopForChild(RenderBox* child) { return style()->isHorizontalWritingMode() ? child->y() : child->x(); } + void setLogicalLeftForChild(RenderBox* child, int logicalLeft, ApplyLayoutDeltaMode = DoNotApplyLayoutDelta); + void setLogicalTopForChild(RenderBox* child, int logicalTop, ApplyLayoutDeltaMode = DoNotApplyLayoutDelta); + int marginBeforeForChild(RenderBoxModelObject* child) const; + int marginAfterForChild(RenderBoxModelObject* child) const; + int marginStartForChild(RenderBoxModelObject* child) const; + int marginEndForChild(RenderBoxModelObject* child) const; + void setMarginStartForChild(RenderBox* child, int); + void setMarginEndForChild(RenderBox* child, int); + void setMarginBeforeForChild(RenderBox* child, int); + void setMarginAfterForChild(RenderBox* child, int); + int collapsedMarginBeforeForChild(RenderBox* child) const; + int collapsedMarginAfterForChild(RenderBox* child) const; + + class MarginValues { + public: + MarginValues(int beforePos, int beforeNeg, int afterPos, int afterNeg) + : m_positiveMarginBefore(beforePos) + , m_negativeMarginBefore(beforeNeg) + , m_positiveMarginAfter(afterPos) + , m_negativeMarginAfter(afterNeg) + { } + + int positiveMarginBefore() const { return m_positiveMarginBefore; } + int negativeMarginBefore() const { return m_negativeMarginBefore; } + int positiveMarginAfter() const { return m_positiveMarginAfter; } + int negativeMarginAfter() const { return m_negativeMarginAfter; } + + void setPositiveMarginBefore(int pos) { m_positiveMarginBefore = pos; } + void setNegativeMarginBefore(int neg) { m_negativeMarginBefore = neg; } + void setPositiveMarginAfter(int pos) { m_positiveMarginAfter = pos; } + void setNegativeMarginAfter(int neg) { m_negativeMarginAfter = neg; } + + private: + int m_positiveMarginBefore; + int m_negativeMarginBefore; + int m_positiveMarginAfter; + int m_negativeMarginAfter; + }; + MarginValues marginValuesForChild(RenderBox* child); + protected: // These functions are only used internally to manipulate the render tree structure via remove/insert/appendChildNode. // Since they are typically called only to move objects around within anonymous blocks (which only have layers in @@ -194,10 +233,10 @@ protected: } void moveChildrenTo(RenderBlock* to, RenderObject* startChild, RenderObject* endChild, RenderObject* beforeChild, bool fullRemoveInsert = false); - int maxPosMarginBefore() const { return m_rareData ? m_rareData->m_beforePos : RenderBlockRareData::beforePosDefault(this); } - int maxNegMarginBefore() const { return m_rareData ? m_rareData->m_beforeNeg : RenderBlockRareData::beforeNegDefault(this); } - int maxPosMarginAfter() const { return m_rareData ? m_rareData->m_afterPos : RenderBlockRareData::afterPosDefault(this); } - int maxNegMarginAfter() const { return m_rareData ? m_rareData->m_afterNeg : RenderBlockRareData::afterNegDefault(this); } + int maxPositiveMarginBefore() const { return m_rareData ? m_rareData->m_margins.positiveMarginBefore() : RenderBlockRareData::positiveMarginBeforeDefault(this); } + int maxNegativeMarginBefore() const { return m_rareData ? m_rareData->m_margins.negativeMarginBefore() : RenderBlockRareData::negativeMarginBeforeDefault(this); } + int maxPositiveMarginAfter() const { return m_rareData ? m_rareData->m_margins.positiveMarginAfter() : RenderBlockRareData::positiveMarginAfterDefault(this); } + int maxNegativeMarginAfter() const { return m_rareData ? m_rareData->m_margins.negativeMarginAfter() : RenderBlockRareData::negativeMarginAfterDefault(this); } void setMaxMarginBeforeValues(int pos, int neg); void setMaxMarginAfterValues(int pos, int neg); @@ -205,10 +244,8 @@ protected: void initMaxMarginValues() { if (m_rareData) { - m_rareData->m_beforePos = RenderBlockRareData::beforePosDefault(this); - m_rareData->m_beforeNeg = RenderBlockRareData::beforeNegDefault(this); - m_rareData->m_afterPos = RenderBlockRareData::afterPosDefault(this); - m_rareData->m_afterNeg = RenderBlockRareData::afterNegDefault(this); + m_rareData->m_margins = MarginValues(RenderBlockRareData::positiveMarginBeforeDefault(this) , RenderBlockRareData::negativeMarginBeforeDefault(this), + RenderBlockRareData::positiveMarginAfterDefault(this), RenderBlockRareData::negativeMarginAfterDefault(this)); m_rareData->m_paginationStrut = 0; } } @@ -220,8 +257,8 @@ protected: virtual void paint(PaintInfo&, int tx, int ty); virtual void paintObject(PaintInfo&, int tx, int ty); - int logicalRightOffsetForContent() const; - int logicalLeftOffsetForContent() const; + int logicalRightOffsetForContent() const { return style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() + availableLogicalWidth() : borderTop() + paddingTop() + availableLogicalWidth(); } + int logicalLeftOffsetForContent() const { return style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); } int logicalRightOffsetForLine(int position, int fixedOffset, bool applyTextIndent = true, int* logicalHeightRemaining = 0) const; int logicalLeftOffsetForLine(int position, int fixedOffset, bool applyTextIndent = true, int* logicalHeightRemaining = 0) const; @@ -256,9 +293,9 @@ protected: // Only used by RenderSVGText, which explicitely overrides RenderBlock::layoutBlock(), do NOT use for anything else. void forceLayoutInlineChildren() { - int repaintTop = 0; - int repaintBottom = 0; - layoutInlineChildren(true, repaintTop, repaintBottom); + int repaintLogicalTop = 0; + int repaintLogicalBottom = 0; + layoutInlineChildren(true, repaintLogicalTop, repaintLogicalBottom); } #endif @@ -284,19 +321,13 @@ private: virtual bool isSelfCollapsingBlock() const; - virtual int maxMarginBefore(MarginSign sign) const - { - return (sign == PositiveMargin) ? maxPosMarginBefore() : maxNegMarginBefore(); - } - virtual int maxMarginAfter(MarginSign sign) const - { - return (sign == PositiveMargin) ? maxPosMarginAfter() : maxNegMarginAfter(); - } + virtual int collapsedMarginBefore() const { return maxPositiveMarginBefore() - maxNegativeMarginBefore(); } + virtual int collapsedMarginAfter() const { return maxPositiveMarginAfter() - maxNegativeMarginAfter(); } virtual void repaintOverhangingFloats(bool paintAllDescendants); - void layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom); - void layoutInlineChildren(bool relayoutChildren, int& repaintTop, int& repaintBottom); + void layoutBlockChildren(bool relayoutChildren, int& maxFloatLogicalBottom); + void layoutInlineChildren(bool relayoutChildren, int& repaintLogicalTop, int& repaintLogicalBottom); virtual void positionListMarker() { } @@ -323,37 +354,94 @@ private: }; struct FloatingObject : Noncopyable { - enum Type { - FloatLeft, - FloatRight - }; + // Note that Type uses bits so you can use FloatBoth as a mask to query for both left and right. + enum Type { FloatLeft = 1, FloatRight = 2, FloatBoth = 3 }; FloatingObject(Type type) : m_renderer(0) - , m_top(0) - , m_bottom(0) - , m_left(0) - , m_width(0) , m_paginationStrut(0) , m_type(type) , m_shouldPaint(true) , m_isDescendant(false) + , m_isPlaced(false) { } - Type type() { return static_cast<Type>(m_type); } + FloatingObject(Type type, const IntRect& frameRect) + : m_renderer(0) + , m_frameRect(frameRect) + , m_paginationStrut(0) + , m_type(type) + , m_shouldPaint(true) + , m_isDescendant(false) + , m_isPlaced(true) + { + } + + Type type() const { return static_cast<Type>(m_type); } + RenderBox* renderer() const { return m_renderer; } + + bool isPlaced() const { return m_isPlaced; } + void setIsPlaced(bool placed = true) { m_isPlaced = placed; } + + int left() const { ASSERT(isPlaced()); return m_frameRect.x(); } + int right() const { ASSERT(isPlaced()); return m_frameRect.right(); } + int top() const { ASSERT(isPlaced()); return m_frameRect.y(); } + int bottom() const { ASSERT(isPlaced()); return m_frameRect.bottom(); } + int width() const { return m_frameRect.width(); } + int height() const { return m_frameRect.height(); } + + void setLeft(int left) { m_frameRect.setX(left); } + void setTop(int top) { m_frameRect.setY(top); } + void setWidth(int width) { m_frameRect.setWidth(width); } + void setHeight(int height) { m_frameRect.setHeight(height); } + + const IntRect& frameRect() const { ASSERT(isPlaced()); return m_frameRect; } + void setFrameRect(const IntRect& frameRect) { m_frameRect = frameRect; } RenderBox* m_renderer; - int m_top; - int m_bottom; - int m_left; - int m_width; + IntRect m_frameRect; int m_paginationStrut; - unsigned m_type : 1; // Type (left or right aligned) + unsigned m_type : 2; // Type (left or right aligned) bool m_shouldPaint : 1; bool m_isDescendant : 1; + bool m_isPlaced : 1; }; + int logicalTopForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->top() : child->left(); } + int logicalBottomForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->bottom() : child->right(); } + int logicalLeftForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->left() : child->top(); } + int logicalRightForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->right() : child->bottom(); } + int logicalWidthForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->width() : child->height(); } + void setLogicalTopForFloat(FloatingObject* child, int logicalTop) + { + if (style()->isHorizontalWritingMode()) + child->setTop(logicalTop); + else + child->setLeft(logicalTop); + } + void setLogicalLeftForFloat(FloatingObject* child, int logicalLeft) + { + if (style()->isHorizontalWritingMode()) + child->setLeft(logicalLeft); + else + child->setTop(logicalLeft); + } + void setLogicalHeightForFloat(FloatingObject* child, int logicalHeight) + { + if (style()->isHorizontalWritingMode()) + child->setHeight(logicalHeight); + else + child->setWidth(logicalHeight); + } + void setLogicalWidthForFloat(FloatingObject* child, int logicalWidth) + { + if (style()->isHorizontalWritingMode()) + child->setWidth(logicalWidth); + else + child->setHeight(logicalWidth); + } + // The following functions' implementations are in RenderBlockLineLayout.cpp. RootInlineBox* determineStartPosition(bool& firstLine, bool& fullLayout, bool& previousLineBrokeCleanly, InlineBidiResolver&, Vector<FloatWithRect>& floats, unsigned& numCleanFloats, @@ -375,6 +463,10 @@ private: void deleteEllipsisLineBoxes(); void checkLinesForTextOverflow(); void addOverflowFromInlineChildren(); + int beforeSideVisibleOverflowForLine(RootInlineBox*) const; + int afterSideVisibleOverflowForLine(RootInlineBox*) const; + int beforeSideLayoutOverflowForLine(RootInlineBox*) const; + int afterSideLayoutOverflowForLine(RootInlineBox*) const; // End of functions defined in RenderBlockLineLayout.cpp. void addOverflowFromBlockChildren(); @@ -406,15 +498,13 @@ private: virtual bool avoidsFloats() const; - bool hasOverhangingFloats() { return parent() && !hasColumns() && floatBottom() > height(); } + bool hasOverhangingFloats() { return parent() && !hasColumns() && lowestFloatLogicalBottom() > logicalHeight(); } void addIntrudingFloats(RenderBlock* prev, int xoffset, int yoffset); int addOverhangingFloats(RenderBlock* child, int xoffset, int yoffset, bool makeChildPaintOtherFloats); - int nextFloatBottomBelow(int) const; - int floatBottom() const; - inline int leftBottom(); - inline int rightBottom(); - + int lowestFloatLogicalBottom(FloatingObject::Type = FloatingObject::FloatBoth) const; + int nextFloatLogicalBottomBelow(int) const; + virtual bool hitTestColumns(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); virtual bool hitTestContents(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); bool hitTestFloats(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); @@ -468,7 +558,7 @@ private: void adjustPointToColumnContents(IntPoint&) const; void adjustForBorderFit(int x, int& left, int& right) const; // Helper function for borderFitAdjust - void markLinesDirtyInVerticalRange(int top, int bottom, RootInlineBox* highest = 0); + void markLinesDirtyInBlockRange(int logicalTop, int logicalBottom, RootInlineBox* highest = 0); void newLine(EClear); @@ -524,24 +614,24 @@ private: bool m_determinedMarginBeforeQuirk : 1; // These flags track the previous maximal positive and negative margins. - int m_posMargin; - int m_negMargin; + int m_positiveMargin; + int m_negativeMargin; public: MarginInfo(RenderBlock* b, int beforeBorderPadding, int afterBorderPadding); void setAtBeforeSideOfBlock(bool b) { m_atBeforeSideOfBlock = b; } void setAtAfterSideOfBlock(bool b) { m_atAfterSideOfBlock = b; } - void clearMargin() { m_posMargin = m_negMargin = 0; } + void clearMargin() { m_positiveMargin = m_negativeMargin = 0; } void setMarginBeforeQuirk(bool b) { m_marginBeforeQuirk = b; } void setMarginAfterQuirk(bool b) { m_marginAfterQuirk = b; } void setDeterminedMarginBeforeQuirk(bool b) { m_determinedMarginBeforeQuirk = b; } - void setPosMargin(int p) { m_posMargin = p; } - void setNegMargin(int n) { m_negMargin = n; } - void setPosMarginIfLarger(int p) { if (p > m_posMargin) m_posMargin = p; } - void setNegMarginIfLarger(int n) { if (n > m_negMargin) m_negMargin = n; } + void setPositiveMargin(int p) { m_positiveMargin = p; } + void setNegativeMargin(int n) { m_negativeMargin = n; } + void setPositiveMarginIfLarger(int p) { if (p > m_positiveMargin) m_positiveMargin = p; } + void setNegativeMarginIfLarger(int n) { if (n > m_negativeMargin) m_negativeMargin = n; } - void setMargin(int p, int n) { m_posMargin = p; m_negMargin = n; } + void setMargin(int p, int n) { m_positiveMargin = p; m_negativeMargin = n; } bool atBeforeSideOfBlock() const { return m_atBeforeSideOfBlock; } bool canCollapseWithMarginBefore() const { return m_atBeforeSideOfBlock && m_canCollapseMarginBeforeWithChildren; } @@ -552,12 +642,12 @@ private: bool determinedMarginBeforeQuirk() const { return m_determinedMarginBeforeQuirk; } bool marginBeforeQuirk() const { return m_marginBeforeQuirk; } bool marginAfterQuirk() const { return m_marginAfterQuirk; } - int posMargin() const { return m_posMargin; } - int negMargin() const { return m_negMargin; } - int margin() const { return m_posMargin - m_negMargin; } + int positiveMargin() const { return m_positiveMargin; } + int negativeMargin() const { return m_negativeMargin; } + int margin() const { return m_positiveMargin - m_negativeMargin; } }; - void layoutBlockChild(RenderBox* child, MarginInfo&, int& previousFloatBottom, int& maxFloatBottom); + void layoutBlockChild(RenderBox* child, MarginInfo&, int& previousFloatLogicalBottom, int& maxFloatLogicalBottom); void adjustPositionedBlock(RenderBox* child, const MarginInfo&); void adjustFloatingBlock(const MarginInfo&); bool handleSpecialChild(RenderBox* child, const MarginInfo&); @@ -566,9 +656,9 @@ private: bool handleRunInChild(RenderBox* child); int collapseMargins(RenderBox* child, MarginInfo&); int clearFloatsIfNeeded(RenderBox* child, MarginInfo&, int oldTopPosMargin, int oldTopNegMargin, int yPos); - int estimateVerticalPosition(RenderBox* child, const MarginInfo&); - void determineHorizontalPosition(RenderBox* child); - void handleBottomOfBlock(int top, int bottom, MarginInfo&); + int estimateLogicalTopPosition(RenderBox* child, const MarginInfo&); + void determineLogicalLeftPositionForChild(RenderBox* child); + void handleAfterSideOfBlock(int top, int bottom, MarginInfo&); void setCollapsedBottomMargin(const MarginInfo&); // End helper functions and structs used by layoutBlockChildren. @@ -589,41 +679,35 @@ private: // split into a sequence of inlines and blocks. The continuation will either be // an anonymous block (that houses other blocks) or it will be an inline flow. RenderBoxModelObject* m_continuation; - + // Allocated only when some of these fields have non-default values struct RenderBlockRareData : Noncopyable { RenderBlockRareData(const RenderBlock* block) - : m_beforePos(beforePosDefault(block)) - , m_beforeNeg(beforeNegDefault(block)) - , m_afterPos(afterPosDefault(block)) - , m_afterNeg(afterNegDefault(block)) + : m_margins(positiveMarginBeforeDefault(block), negativeMarginBeforeDefault(block), positiveMarginAfterDefault(block), negativeMarginAfterDefault(block)) , m_paginationStrut(0) , m_pageY(0) { } - static int beforePosDefault(const RenderBlock* block) + static int positiveMarginBeforeDefault(const RenderBlock* block) { return std::max(block->marginBefore(), 0); } - static int beforeNegDefault(const RenderBlock* block) + static int negativeMarginBeforeDefault(const RenderBlock* block) { return std::max(-block->marginBefore(), 0); } - static int afterPosDefault(const RenderBlock* block) + static int positiveMarginAfterDefault(const RenderBlock* block) { return std::max(block->marginAfter(), 0); } - static int afterNegDefault(const RenderBlock* block) + static int negativeMarginAfterDefault(const RenderBlock* block) { return std::max(-block->marginAfter(), 0); } - int m_beforePos; - int m_beforeNeg; - int m_afterPos; - int m_afterNeg; + MarginValues m_margins; int m_paginationStrut; int m_pageY; }; diff --git a/WebCore/rendering/RenderBlockLineLayout.cpp b/WebCore/rendering/RenderBlockLineLayout.cpp index 1df4bbc..ce84d31 100644 --- a/WebCore/rendering/RenderBlockLineLayout.cpp +++ b/WebCore/rendering/RenderBlockLineLayout.cpp @@ -64,13 +64,12 @@ const unsigned cMaxLineDepth = 200; static int getBorderPaddingMargin(RenderBoxModelObject* child, bool endOfInline) { - bool leftSide = (child->style()->direction() == LTR) ? !endOfInline : endOfInline; - if (leftSide) - return child->marginLeft() + child->paddingLeft() + child->borderLeft(); - return child->marginRight() + child->paddingRight() + child->borderRight(); + if (endOfInline) + return child->marginEnd() + child->paddingEnd() + child->borderEnd(); + return child->marginStart() + child->paddingStart() + child->borderStart(); } -static int inlineWidth(RenderObject* child, bool start = true, bool end = true) +static int inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true) { unsigned lineDepth = 1; int extraWidth = 0; @@ -246,12 +245,13 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, { ASSERT(firstRun); + bool rootHasSelectedChildren = false; InlineFlowBox* parentBox = 0; for (BidiRun* r = firstRun; r; r = r->next()) { // Create a box for our object. bool isOnlyRun = (runCount == 1); if (runCount == 2 && !r->m_object->isListMarker()) - isOnlyRun = ((style()->direction() == RTL) ? lastRun : firstRun)->m_object->isListMarker(); + isOnlyRun = (!style()->isLeftToRightDirection() ? lastRun : firstRun)->m_object->isListMarker(); InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun); r->m_box = box; @@ -260,6 +260,9 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, if (!box) continue; + if (!rootHasSelectedChildren && box->renderer()->selectionState() != RenderObject::SelectionNone) + rootHasSelectedChildren = true; + // 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. @@ -287,6 +290,11 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, // be the last continuation of our line list. ASSERT(lastLineBox() && !lastLineBox()->isConstructed()); + // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box + // from the bidi runs walk above has a selection state. + if (rootHasSelectedChildren) + lastLineBox()->root()->setHasSelectedChildren(true); + // Set bits on our inline flow boxes that indicate which sides should // paint borders/margins/padding. This knowledge will ultimately be used when // we determine the horizontal positions and widths of all the inline boxes on @@ -302,9 +310,9 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) { - // First determine our total width. - int availableWidth = availableLogicalWidthForLine(height(), firstLine); - int totWidth = lineBox->getFlowSpacingLogicalWidth(); + // First determine our total logical width. + int availableLogicalWidth = availableLogicalWidthForLine(logicalHeight(), firstLine); + int totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth(); bool needsWordSpacing = false; unsigned numSpaces = 0; ETextAlign textAlign = style()->textAlign(); @@ -328,7 +336,7 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, if (int length = rt->textLength()) { if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characters()[r->m_start])) - totWidth += rt->style(firstLine)->font().wordSpacing(); + totalLogicalWidth += rt->style(firstLine)->font().wordSpacing(); needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length; } HashSet<const SimpleFontData*> fallbackFonts; @@ -338,7 +346,7 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const AtomicString& hyphenString = rt->style()->hyphenString(); hyphenWidth = rt->style(firstLine)->font().width(TextRun(hyphenString.characters(), hyphenString.length())); } - r->m_box->setLogicalWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, firstLine, &fallbackFonts, &glyphOverflow) + hyphenWidth); + r->m_box->setLogicalWidth(rt->width(r->m_start, r->m_stop - r->m_start, totalLogicalWidth, firstLine, &fallbackFonts, &glyphOverflow) + hyphenWidth); if (!fallbackFonts.isEmpty()) { ASSERT(r->m_box->isText()); GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast<InlineTextBox*>(r->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).first; @@ -353,37 +361,37 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, } else if (!r->m_object->isRenderInline()) { RenderBox* renderBox = toRenderBox(r->m_object); renderBox->computeLogicalWidth(); - r->m_box->setLogicalWidth(renderBox->width()); - totWidth += renderBox->marginLeft() + renderBox->marginRight(); + r->m_box->setLogicalWidth(logicalWidthForChild(renderBox)); + totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox); } - totWidth += r->m_box->logicalWidth(); + totalLogicalWidth += r->m_box->logicalWidth(); } // Armed with the total width of the line (without justification), // we now examine our text-align property in order to determine where to position the // objects horizontally. The total width of the line can be increased if we end up // justifying text. - int x = logicalLeftOffsetForLine(height(), firstLine); + int logicalLeft = logicalLeftOffsetForLine(logicalHeight(), firstLine); switch (textAlign) { case LEFT: case WEBKIT_LEFT: // The direction of the block should determine what happens with wide lines. In // particular with RTL blocks, wide lines should still spill out to the left. - if (style()->direction() == LTR) { - if (totWidth > availableWidth && trailingSpaceRun) - trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceRun->m_box->logicalWidth() - totWidth + availableWidth)); + if (style()->isLeftToRightDirection()) { + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) + trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); } else { if (trailingSpaceRun) trailingSpaceRun->m_box->setLogicalWidth(0); - else if (totWidth > availableWidth) - x -= (totWidth - availableWidth); + else if (totalLogicalWidth > availableLogicalWidth) + logicalLeft -= (totalLogicalWidth - availableLogicalWidth); } break; case JUSTIFY: if (numSpaces && !reachedEnd && !lineBox->endsWithBreak()) { if (trailingSpaceRun) { - totWidth -= trailingSpaceRun->m_box->logicalWidth(); + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); trailingSpaceRun->m_box->setLogicalWidth(0); } break; @@ -392,9 +400,9 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, case TAAUTO: numSpaces = 0; // for right to left fall through to right aligned - if (style()->direction() == LTR) { - if (totWidth > availableWidth && trailingSpaceRun) - trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceRun->m_box->logicalWidth() - totWidth + availableWidth)); + if (style()->isLeftToRightDirection()) { + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) + trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); break; } case RIGHT: @@ -402,33 +410,33 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, // Wide lines spill out of the block based off direction. // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right // side of the block. - if (style()->direction() == LTR) { + if (style()->isLeftToRightDirection()) { if (trailingSpaceRun) { - totWidth -= trailingSpaceRun->m_box->logicalWidth(); + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); trailingSpaceRun->m_box->setLogicalWidth(0); } - if (totWidth < availableWidth) - x += availableWidth - totWidth; + if (totalLogicalWidth < availableLogicalWidth) + logicalLeft += availableLogicalWidth - totalLogicalWidth; } else { - if (totWidth > availableWidth && trailingSpaceRun) { - trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceRun->m_box->logicalWidth() - totWidth + availableWidth)); - totWidth -= trailingSpaceRun->m_box->logicalWidth(); + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) { + trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); } else - x += availableWidth - totWidth; + logicalLeft += availableLogicalWidth - totalLogicalWidth; } break; case CENTER: case WEBKIT_CENTER: int trailingSpaceWidth = 0; if (trailingSpaceRun) { - totWidth -= trailingSpaceRun->m_box->logicalWidth(); - trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableWidth - totWidth + 1) / 2); + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2); trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceWidth)); } - if (style()->direction() == LTR) - x += max((availableWidth - totWidth) / 2, 0); + if (style()->isLeftToRightDirection()) + logicalLeft += max((availableLogicalWidth - totalLogicalWidth) / 2, 0); else - x += totWidth > availableWidth ? (availableWidth - totWidth) : (availableWidth - totWidth) / 2 - trailingSpaceWidth; + logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth; break; } @@ -451,9 +459,9 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, // Only justify text if whitespace is collapsed. if (r->m_object->style()->collapseWhiteSpace()) { - spaceAdd = (availableWidth - totWidth) * spaces / numSpaces; + spaceAdd = (availableLogicalWidth - totalLogicalWidth) * spaces / numSpaces; static_cast<InlineTextBox*>(r->m_box)->setSpaceAdd(spaceAdd); - totWidth += spaceAdd; + totalLogicalWidth += spaceAdd; } numSpaces -= spaces; if (!numSpaces) @@ -465,13 +473,13 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, // 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(x, needsWordSpacing, textBoxDataMap); + lineBox->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing, textBoxDataMap); } void RenderBlock::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) { - setLogicalHeight(lineBox->alignBoxesInBlockDirection(height(), textBoxDataMap)); - lineBox->setBlockHeight(height()); + setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap)); + lineBox->setBlockLogicalHeight(logicalHeight()); // Now make sure we place replaced render objects correctly. for (BidiRun* r = firstRun; r; r = r->next()) { @@ -482,7 +490,7 @@ void RenderBlock::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, // Align positioned boxes with the top of the line box. This is // a reasonable approximation of an appropriate y position. if (r->m_object->isPositioned()) - r->m_box->setY(height()); + r->m_box->setLogicalTop(logicalHeight()); // Position is used to properly position both replaced elements and // to update the static normal flow x/y of positioned elements. @@ -507,14 +515,13 @@ static inline bool isCollapsibleSpace(UChar character, RenderText* renderer) return false; } -void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, int& repaintBottom) +void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogicalTop, int& repaintLogicalBottom) { bool useRepaintBounds = false; m_overflow.clear(); - setLogicalHeight(borderTop() + paddingTop()); - int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + setLogicalHeight(borderBefore() + paddingBefore()); // Figure out if we should clear out our line boxes. // FIXME: Handle resize eventually! @@ -582,7 +589,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i o->setChildNeedsLayout(true, false); // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. - if (relayoutChildren && (o->style()->paddingLeft().isPercent() || o->style()->paddingRight().isPercent())) + if (relayoutChildren && (o->style()->paddingStart().isPercent() || o->style()->paddingEnd().isPercent())) o->setPreferredLogicalWidthsDirty(true, false); if (o->isPositioned()) @@ -706,7 +713,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i bool firstLine = true; bool previousLineBrokeCleanly = true; RootInlineBox* startLine = determineStartPosition(firstLine, fullLayout, previousLineBrokeCleanly, resolver, floats, floatIndex, - useRepaintBounds, repaintTop, repaintBottom); + useRepaintBounds, repaintLogicalTop, repaintLogicalBottom); if (fullLayout && hasInlineChild && !selfNeedsLayout()) { setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like @@ -729,21 +736,21 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // if we determine that we're able to synchronize after handling all our dirty lines. InlineIterator cleanLineStart; BidiStatus cleanLineBidiStatus; - int endLineYPos = 0; + int endLineLogicalTop = 0; RootInlineBox* endLine = (fullLayout || !startLine) ? - 0 : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, endLineYPos); + 0 : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, endLineLogicalTop); if (startLine) { if (!useRepaintBounds) { useRepaintBounds = true; - repaintTop = height(); - repaintBottom = height(); + repaintLogicalTop = logicalHeight(); + repaintLogicalBottom = logicalHeight(); } RenderArena* arena = renderArena(); RootInlineBox* box = startLine; while (box) { - repaintTop = min(repaintTop, box->topVisibleOverflow()); - repaintBottom = max(repaintBottom, box->bottomVisibleOverflow()); + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisibleOverflowForLine(box)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisibleOverflowForLine(box)); RootInlineBox* next = box->nextRootBox(); box->deleteLine(arena); box = next; @@ -777,7 +784,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i while (!end.atEnd()) { // FIXME: Is this check necessary before the first iteration or can it be moved to the end? - if (checkForEndLineMatch && (endLineMatched = matchedEndLine(resolver, cleanLineStart, cleanLineBidiStatus, endLine, endLineYPos, repaintBottom, repaintTop))) + if (checkForEndLineMatch && (endLineMatched = matchedEndLine(resolver, cleanLineStart, cleanLineBidiStatus, endLine, endLineLogicalTop, repaintLogicalBottom, repaintLogicalTop))) break; lineMidpointState.reset(); @@ -854,7 +861,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // inline flow boxes. RootInlineBox* lineBox = 0; - int oldHeight = height(); + int oldLogicalHeight = logicalHeight(); if (resolver.runCount()) { if (hyphenated) resolver.logicallyLastRun()->m_hasHyphen = true; @@ -902,30 +909,30 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i if (lineBox) { lineBox->setLineBreakInfo(end.obj, end.pos, resolver.status()); if (useRepaintBounds) { - repaintTop = min(repaintTop, lineBox->topVisibleOverflow()); - repaintBottom = max(repaintBottom, lineBox->bottomVisibleOverflow()); + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisibleOverflowForLine(lineBox)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisibleOverflowForLine(lineBox)); } if (paginated) { int adjustment = 0; adjustLinePositionForPagination(lineBox, adjustment); if (adjustment) { - int oldLineWidth = availableLogicalWidthForLine(oldHeight, firstLine); + int oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, firstLine); lineBox->adjustPosition(0, adjustment); if (useRepaintBounds) // This can only be a positive adjustment, so no need to update repaintTop. - repaintBottom = max(repaintBottom, lineBox->bottomVisibleOverflow()); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisibleOverflowForLine(lineBox)); - if (availableLogicalWidthForLine(oldHeight + adjustment, firstLine) != oldLineWidth) { + if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, firstLine) != oldLineWidth) { // We have to delete this line, remove all floats that got added, and let line layout re-run. lineBox->deleteLine(renderArena()); - removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldHeight); - setLogicalHeight(oldHeight + adjustment); + removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight); + setLogicalHeight(oldLogicalHeight + adjustment); resolver.setPosition(oldEnd); end = oldEnd; continue; } - setLogicalHeight(lineBox->blockHeight()); + setLogicalHeight(lineBox->blockLogicalHeight()); } } } @@ -945,7 +952,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i lastRootBox()->floats().append(f->m_renderer); ASSERT(f->m_renderer == floats[floatIndex].object); // If a float's geometry has changed, give up on syncing with clean lines. - if (floats[floatIndex].rect != IntRect(f->m_left, f->m_top, f->m_width, f->m_bottom - f->m_top)) + if (floats[floatIndex].rect != f->frameRect()) checkForEndLineMatch = false; floatIndex++; } @@ -959,7 +966,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i if (endLine) { if (endLineMatched) { // Attach all the remaining lines, and then adjust their y-positions as needed. - int delta = height() - endLineYPos; + int delta = logicalHeight() - endLineLogicalTop; for (RootInlineBox* line = endLine; line; line = line->nextRootBox()) { line->attachLine(); if (paginated) { @@ -967,28 +974,27 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i adjustLinePositionForPagination(line, delta); } if (delta) { - repaintTop = min(repaintTop, line->topVisibleOverflow() + min(delta, 0)); - repaintBottom = max(repaintBottom, line->bottomVisibleOverflow() + max(delta, 0)); + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisibleOverflowForLine(line) + min(delta, 0)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisibleOverflowForLine(line) + max(delta, 0)); line->adjustPosition(0, delta); } if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) { Vector<RenderBox*>::iterator end = cleanLineFloats->end(); for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) { - int floatTop = (*f)->y() - (*f)->marginTop(); insertFloatingObject(*f); - setLogicalHeight(floatTop + delta); + setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f) + delta); positionNewFloats(); } } } - setLogicalHeight(lastRootBox()->blockHeight()); + setLogicalHeight(lastRootBox()->blockLogicalHeight()); } else { // Delete all the remaining lines. RootInlineBox* line = endLine; RenderArena* arena = renderArena(); while (line) { - repaintTop = min(repaintTop, line->topVisibleOverflow()); - repaintBottom = max(repaintBottom, line->bottomVisibleOverflow()); + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisibleOverflowForLine(line)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisibleOverflowForLine(line)); RootInlineBox* next = line->nextRootBox(); line->deleteLine(arena); line = next; @@ -1000,15 +1006,15 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // This has to be done before adding in the bottom border/padding, or the float will // include the padding incorrectly. -dwh if (checkForFloatsFromLastLine) { - int bottomVisualOverflow = lastRootBox()->bottomVisualOverflow(); - int bottomLayoutOverflow = lastRootBox()->bottomLayoutOverflow(); + int bottomVisualOverflow = afterSideVisibleOverflowForLine(lastRootBox()); + int bottomLayoutOverflow = afterSideLayoutOverflowForLine(lastRootBox()); TrailingFloatsRootInlineBox* trailingFloatsLineBox = new (renderArena()) TrailingFloatsRootInlineBox(this); m_lineBoxes.appendLineBox(trailingFloatsLineBox); trailingFloatsLineBox->setConstructed(); GlyphOverflowAndFallbackFontsMap textBoxDataMap; - trailingFloatsLineBox->alignBoxesInBlockDirection(height(), textBoxDataMap); - trailingFloatsLineBox->setBlockDirectionOverflowPositions(height(), bottomLayoutOverflow, height(), bottomVisualOverflow, 0); - trailingFloatsLineBox->setBlockHeight(height()); + trailingFloatsLineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap); + trailingFloatsLineBox->setBlockDirectionOverflowPositions(logicalHeight(), bottomLayoutOverflow, logicalHeight(), bottomVisualOverflow, 0); + trailingFloatsLineBox->setBlockLogicalHeight(logicalHeight()); } if (lastFloat) { for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) { @@ -1034,10 +1040,10 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i } // Now add in the bottom border/padding. - setLogicalHeight(height() + toAdd); + setLogicalHeight(logicalHeight() + borderAfter() + paddingAfter() + scrollbarLogicalHeight()); if (!firstLineBox() && hasLineIfEmpty()) - setLogicalHeight(height() + lineHeight(true, true)); + setLogicalHeight(logicalHeight() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); // See if we have any lines that spill out of our block. If we do, then we will possibly need to // truncate text. @@ -1047,7 +1053,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLayout, bool& previousLineBrokeCleanly, InlineBidiResolver& resolver, Vector<FloatWithRect>& floats, unsigned& numCleanFloats, - bool& useRepaintBounds, int& repaintTop, int& repaintBottom) + bool& useRepaintBounds, int& repaintLogicalTop, int& repaintLogicalBottom) { RootInlineBox* curr = 0; RootInlineBox* last = 0; @@ -1072,8 +1078,8 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa if (!useRepaintBounds) useRepaintBounds = true; - repaintTop = min(repaintTop, curr->topVisibleOverflow() + min(paginationDelta, 0)); - repaintBottom = max(repaintBottom, curr->bottomVisibleOverflow() + max(paginationDelta, 0)); + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisibleOverflowForLine(curr) + min(paginationDelta, 0)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisibleOverflowForLine(curr) + max(paginationDelta, 0)); curr->adjustPosition(0, paginationDelta); } } @@ -1092,9 +1098,11 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa break; } if (floats[floatIndex].rect.size() != newSize) { - int floatTop = floats[floatIndex].rect.y(); + int floatTop = style()->isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x(); + int floatHeight = style()->isHorizontalWritingMode() ? max(floats[floatIndex].rect.height(), newSize.height()) + : max(floats[floatIndex].rect.width(), newSize.width()); curr->markDirty(); - markLinesDirtyInVerticalRange(curr->blockHeight(), floatTop + max(floats[floatIndex].rect.height(), newSize.height()), curr); + markLinesDirtyInBlockRange(curr->blockLogicalHeight(), floatTop + floatHeight, curr); floats[floatIndex].rect.setSize(newSize); dirtiedByFloat = true; } @@ -1144,7 +1152,7 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa numCleanFloats = 0; if (!floats.isEmpty()) { - int savedHeight = height(); + int savedLogicalHeight = logicalHeight(); // Restore floats from clean lines. RootInlineBox* line = firstRootBox(); while (line != curr) { @@ -1152,7 +1160,7 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa Vector<RenderBox*>::iterator end = cleanLineFloats->end(); for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) { insertFloatingObject(*f); - setLogicalHeight((*f)->y() - (*f)->marginTop()); + setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f)); positionNewFloats(); ASSERT(floats[numCleanFloats].object == *f); numCleanFloats++; @@ -1160,7 +1168,7 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa } line = line->nextRootBox(); } - setLogicalHeight(savedHeight); + setLogicalHeight(savedLogicalHeight); } firstLine = !last; @@ -1169,12 +1177,12 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa RenderObject* startObj; int pos = 0; if (last) { - setLogicalHeight(last->blockHeight()); + setLogicalHeight(last->blockLogicalHeight()); startObj = last->lineBreakObj(); pos = last->lineBreakPos(); resolver.setStatus(last->lineBreakBidiStatus()); } else { - bool ltr = style()->direction() == LTR + bool ltr = style()->isLeftToRightDirection() #if ENABLE(SVG) || (style()->unicodeBidi() == UBNormal && isSVGText()) #endif @@ -1194,7 +1202,7 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa return curr; } -RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& yPos) +RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& logicalTop) { RootInlineBox* last = 0; if (!startLine) @@ -1214,7 +1222,7 @@ RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, Inlin RootInlineBox* prev = last->prevRootBox(); cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos()); cleanLineBidiStatus = prev->lineBreakBidiStatus(); - yPos = prev->blockHeight(); + logicalTop = prev->blockLogicalHeight(); for (RootInlineBox* line = last; line; line = line->nextRootBox()) line->extractLine(); // Disconnect all line boxes from their render objects while preserving @@ -1223,27 +1231,28 @@ RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, Inlin return last; } -bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, RootInlineBox*& endLine, int& endYPos, int& repaintBottom, int& repaintTop) +bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, RootInlineBox*& endLine, + int& endLogicalTop, int& repaintLogicalBottom, int& repaintLogicalTop) { if (resolver.position() == endLineStart) { if (resolver.status() != endLineStatus) return false; - int delta = height() - endYPos; + int delta = logicalHeight() - endLogicalTop; if (!delta || !m_floatingObjects) return true; // See if any floats end in the range along which we want to shift the lines vertically. - int top = min(height(), endYPos); + int logicalTop = min(logicalHeight(), endLogicalTop); RootInlineBox* lastLine = endLine; while (RootInlineBox* nextLine = lastLine->nextRootBox()) lastLine = nextLine; - int bottom = lastLine->blockHeight() + abs(delta); + int logicalBottom = lastLine->blockLogicalHeight() + abs(delta); for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) { - if (f->m_bottom >= top && f->m_bottom < bottom) + if (logicalBottomForFloat(f) >= logicalTop && logicalBottomForFloat(f) < logicalBottom) return false; } @@ -1261,23 +1270,23 @@ bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const Inlin return false; // ...but the bidi state doesn't match. RootInlineBox* result = line->nextRootBox(); - // Set our yPos to be the block height of endLine. + // Set our logical top to be the block height of endLine. if (result) - endYPos = line->blockHeight(); + endLogicalTop = line->blockLogicalHeight(); - int delta = height() - endYPos; + int delta = logicalHeight() - endLogicalTop; if (delta && m_floatingObjects) { // See if any floats end in the range along which we want to shift the lines vertically. - int top = min(height(), endYPos); + int logicalTop = min(logicalHeight(), endLogicalTop); RootInlineBox* lastLine = endLine; while (RootInlineBox* nextLine = lastLine->nextRootBox()) lastLine = nextLine; - int bottom = lastLine->blockHeight() + abs(delta); + int logicalBottom = lastLine->blockLogicalHeight() + abs(delta); for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) { - if (f->m_bottom >= top && f->m_bottom < bottom) + if (logicalBottomForFloat(f) >= logicalTop && logicalBottomForFloat(f) < logicalBottom) return false; } } @@ -1286,8 +1295,8 @@ bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const Inlin RootInlineBox* boxToDelete = endLine; RenderArena* arena = renderArena(); while (boxToDelete && boxToDelete != result) { - repaintTop = min(repaintTop, boxToDelete->topVisibleOverflow()); - repaintBottom = max(repaintBottom, boxToDelete->bottomVisibleOverflow()); + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisibleOverflowForLine(boxToDelete)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisibleOverflowForLine(boxToDelete)); RootInlineBox* next = boxToDelete->nextRootBox(); boxToDelete->deleteLine(arena); boxToDelete = next; @@ -1337,7 +1346,7 @@ static bool inlineFlowRequiresLineBox(RenderInline* 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->hasHorizontalBordersPaddingOrMargin(); + return !flow->firstChild() && flow->hasInlineDirectionBordersPaddingOrMargin(); } bool RenderBlock::requiresLineBox(const InlineIterator& it, bool isLineEmpty, bool previousLineBrokeCleanly) @@ -1387,16 +1396,16 @@ void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator, bool isLineEm // A relative positioned inline encloses us. In this case, we also have to determine our // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned // inline so that we can obtain the value later. - toRenderInline(c)->layer()->setStaticX(style()->direction() == LTR ? logicalLeftOffsetForLine(height(), false) : logicalRightOffsetForLine(height(), false)); + toRenderInline(c)->layer()->setStaticX(style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(height(), false) : logicalRightOffsetForLine(height(), false)); toRenderInline(c)->layer()->setStaticY(height()); } RenderBox* box = toRenderBox(object); if (box->style()->hasStaticX()) { if (box->style()->isOriginalDisplayInlineType()) - box->layer()->setStaticX(style()->direction() == LTR ? logicalLeftOffsetForLine(height(), false) : width() - logicalRightOffsetForLine(height(), false)); + box->layer()->setStaticX(style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(height(), false) : width() - logicalRightOffsetForLine(height(), false)); else - box->layer()->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); + box->layer()->setStaticX(style()->isLeftToRightDirection() ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); } if (box->style()->hasStaticY()) @@ -1409,12 +1418,12 @@ void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator, bool isLineEm int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver, bool firstLine, bool isLineEmpty, bool previousLineBrokeCleanly, FloatingObject* lastFloatFromPreviousLine) { - int availableWidth = availableLogicalWidthForLine(height(), firstLine); + int availableWidth = availableLogicalWidthForLine(logicalHeight(), firstLine); while (!resolver.position().atEnd() && !requiresLineBox(resolver.position(), isLineEmpty, previousLineBrokeCleanly)) { RenderObject* object = resolver.position().obj; if (object->isFloating()) { positionNewFloatOnLine(insertFloatingObject(toRenderBox(object)), lastFloatFromPreviousLine); - availableWidth = availableLogicalWidthForLine(height(), firstLine); + availableWidth = availableLogicalWidthForLine(logicalHeight(), firstLine); } else if (object->isPositioned()) { // FIXME: The math here is actually not really right. It's a best-guess approximation that // will work for the common cases @@ -1423,16 +1432,16 @@ int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver, bool firstL // A relative positioned inline encloses us. In this case, we also have to determine our // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned // inline so that we can obtain the value later. - toRenderInline(c)->layer()->setStaticX(style()->direction() == LTR ? logicalLeftOffsetForLine(height(), firstLine) : logicalRightOffsetForLine(height(), firstLine)); + toRenderInline(c)->layer()->setStaticX(style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(height(), firstLine) : logicalRightOffsetForLine(height(), firstLine)); toRenderInline(c)->layer()->setStaticY(height()); } RenderBox* box = toRenderBox(object); if (box->style()->hasStaticX()) { if (box->style()->isOriginalDisplayInlineType()) - box->layer()->setStaticX(style()->direction() == LTR ? logicalLeftOffsetForLine(height(), firstLine) : width() - logicalRightOffsetForLine(height(), firstLine)); + box->layer()->setStaticX(style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(height(), firstLine) : width() - logicalRightOffsetForLine(height(), firstLine)); else - box->layer()->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); + box->layer()->setStaticX(style()->isLeftToRightDirection() ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); } if (box->style()->hasStaticY()) @@ -1465,22 +1474,22 @@ void RenderBlock::fitBelowFloats(int widthToFit, bool firstLine, int& availableW { ASSERT(widthToFit > availableWidth); - int floatBottom; - int lastFloatBottom = height(); + int floatLogicalBottom; + int lastFloatLogicalBottom = logicalHeight(); int newLineWidth = availableWidth; while (true) { - floatBottom = nextFloatBottomBelow(lastFloatBottom); - if (!floatBottom) + floatLogicalBottom = nextFloatLogicalBottomBelow(lastFloatLogicalBottom); + if (!floatLogicalBottom) break; - newLineWidth = availableLogicalWidthForLine(floatBottom, firstLine); - lastFloatBottom = floatBottom; + newLineWidth = availableLogicalWidthForLine(floatLogicalBottom, firstLine); + lastFloatLogicalBottom = floatLogicalBottom; if (newLineWidth >= widthToFit) break; } if (newLineWidth > availableWidth) { - setLogicalHeight(lastFloatBottom); + setLogicalHeight(lastFloatLogicalBottom); availableWidth = newLineWidth; } } @@ -1571,7 +1580,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // Firefox and Opera will allow a table cell to grow to fit an image inside it under // very specific circumstances (in order to match common WinIE renderings). // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) - bool allowImagesToBreak = !document()->inQuirksMode() || !isTableCell() || !style()->width().isIntrinsicOrAuto(); + bool allowImagesToBreak = !document()->inQuirksMode() || !isTableCell() || !style()->logicalWidth().isIntrinsicOrAuto(); EWhiteSpace currWS = style()->whiteSpace(); EWhiteSpace lastWS = currWS; @@ -1621,9 +1630,9 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // 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 && floatBox->width() + floatBox->marginLeft() + floatBox->marginRight() + w + tmpW <= width) { + if (floatsFitOnLine && logicalWidthForFloat(f) + w + tmpW <= width) { positionNewFloatOnLine(f, lastFloatFromPreviousLine); - width = availableLogicalWidthForLine(height(), firstLine); + width = availableLogicalWidthForLine(logicalHeight(), firstLine); } else floatsFitOnLine = false; } else if (o->isPositioned()) { @@ -1633,7 +1642,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool bool isInlineType = box->style()->isOriginalDisplayInlineType(); bool needToSetStaticX = box->style()->hasStaticX(); if (box->style()->hasStaticX() && !isInlineType) { - box->layer()->setStaticX(o->parent()->style()->direction() == LTR ? + box->layer()->setStaticX(o->parent()->style()->isLeftToRightDirection() ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); needToSetStaticX = false; @@ -1691,8 +1700,8 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } } - tmpW += flowBox->marginLeft() + flowBox->borderLeft() + flowBox->paddingLeft() + - flowBox->marginRight() + flowBox->borderRight() + flowBox->paddingRight(); + tmpW += flowBox->marginStart() + flowBox->borderStart() + flowBox->paddingStart() + + flowBox->marginEnd() + flowBox->borderEnd() + flowBox->paddingEnd(); } else if (o->isReplaced()) { RenderBox* replacedBox = toRenderBox(o); @@ -1715,7 +1724,8 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool trailingSpaceObject = 0; // Optimize for a common case. If we can't find whitespace after the list - // item, then this is all moot. -dwh + // item, then this is all moot. + int replacedLogicalWidth = logicalWidthForChild(replacedBox) + marginStartForChild(replacedBox) + marginEndForChild(replacedBox) + inlineLogicalWidth(o); if (o->isListMarker()) { if (style()->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(this, o, lineMidpointState)) { // Like with inline flows, we start ignoring spaces to make sure that any @@ -1725,9 +1735,9 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool ignoringSpaces = true; } if (toRenderListMarker(o)->isInside()) - tmpW += replacedBox->width() + replacedBox->marginLeft() + replacedBox->marginRight() + inlineWidth(o); + tmpW += replacedLogicalWidth; } else - tmpW += replacedBox->width() + replacedBox->marginLeft() + replacedBox->marginRight() + inlineWidth(o); + tmpW += replacedLogicalWidth; } else if (o->isText()) { if (!pos) appliedStartWidth = false; @@ -1755,7 +1765,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // space, then subtract its width. int wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.width(TextRun(&space, 1)) + wordSpacing : 0; - int wrapW = tmpW + inlineWidth(o, !appliedStartWidth, true); + int wrapW = tmpW + inlineLogicalWidth(o, !appliedStartWidth, true); int charWidth = 0; bool breakNBSP = autoWrap && o->style()->nbspMode() == SPACE; // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word, @@ -1839,7 +1849,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool additionalTmpW = textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; tmpW += additionalTmpW; if (!appliedStartWidth) { - tmpW += inlineWidth(o, true, false); + tmpW += inlineLogicalWidth(o, true, false); appliedStartWidth = true; } @@ -1984,7 +1994,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // IMPORTANT: pos is > length here! int additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; tmpW += additionalTmpW; - tmpW += inlineWidth(o, !appliedStartWidth, true); + tmpW += inlineLogicalWidth(o, !appliedStartWidth, true); if (canHyphenate && w + tmpW > width) { tryHyphenating(t, f, style->hyphenationLocale(), lastSpace, pos, w + tmpW - additionalTmpW, width, isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, nextBreakable, hyphenated); @@ -2149,6 +2159,70 @@ void RenderBlock::addOverflowFromInlineChildren() } } +int RenderBlock::beforeSideVisibleOverflowForLine(RootInlineBox* line) const +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return line->topVisibleOverflow(); + case LeftToRightWritingMode: + return line->leftVisibleOverflow(); + case RightToLeftWritingMode: + return line->rightVisibleOverflow(); + case BottomToTopWritingMode: + return line->bottomVisibleOverflow(); + } + ASSERT_NOT_REACHED(); + return line->topVisibleOverflow(); +} + +int RenderBlock::afterSideVisibleOverflowForLine(RootInlineBox* line) const +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return line->bottomVisibleOverflow(); + case LeftToRightWritingMode: + return line->rightVisibleOverflow(); + case RightToLeftWritingMode: + return line->leftVisibleOverflow(); + case BottomToTopWritingMode: + return line->topVisibleOverflow(); + } + ASSERT_NOT_REACHED(); + return line->bottomVisibleOverflow(); +} + +int RenderBlock::beforeSideLayoutOverflowForLine(RootInlineBox* line) const +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return line->topLayoutOverflow(); + case LeftToRightWritingMode: + return line->leftLayoutOverflow(); + case RightToLeftWritingMode: + return line->rightLayoutOverflow(); + case BottomToTopWritingMode: + return line->bottomLayoutOverflow(); + } + ASSERT_NOT_REACHED(); + return line->topLayoutOverflow(); +} + +int RenderBlock::afterSideLayoutOverflowForLine(RootInlineBox* line) const +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return line->bottomLayoutOverflow(); + case LeftToRightWritingMode: + return line->rightLayoutOverflow(); + case RightToLeftWritingMode: + return line->leftLayoutOverflow(); + case BottomToTopWritingMode: + return line->topLayoutOverflow(); + } + ASSERT_NOT_REACHED(); + return line->bottomLayoutOverflow(); +} + void RenderBlock::deleteEllipsisLineBoxes() { for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) @@ -2170,7 +2244,7 @@ void RenderBlock::checkLinesForTextOverflow() // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and // check the left edge of the line box to see if it is less // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()" - bool ltr = style()->direction() == LTR; + bool ltr = style()->isLeftToRightDirection(); for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { int blockRightEdge = logicalRightOffsetForLine(curr->y(), curr == firstRootBox()); int blockLeftEdge = logicalLeftOffsetForLine(curr->y(), curr == firstRootBox()); diff --git a/WebCore/rendering/RenderBox.cpp b/WebCore/rendering/RenderBox.cpp index ecb6ccc..ac40ee9 100644 --- a/WebCore/rendering/RenderBox.cpp +++ b/WebCore/rendering/RenderBox.cpp @@ -92,19 +92,14 @@ RenderBox::~RenderBox() int RenderBox::marginBefore() const { - return marginBeforeUsing(style()); -} - -int RenderBox::marginBeforeUsing(const RenderStyle* s) const -{ - switch (s->blockFlow()) { - case TopToBottomBlockFlow: + switch (style()->writingMode()) { + case TopToBottomWritingMode: return m_marginTop; - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return m_marginBottom; - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return m_marginLeft; - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return m_marginRight; } ASSERT_NOT_REACHED(); @@ -113,19 +108,14 @@ int RenderBox::marginBeforeUsing(const RenderStyle* s) const int RenderBox::marginAfter() const { - return marginAfterUsing(style()); -} - -int RenderBox::marginAfterUsing(const RenderStyle* s) const -{ - switch (s->blockFlow()) { - case TopToBottomBlockFlow: + switch (style()->writingMode()) { + case TopToBottomWritingMode: return m_marginBottom; - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return m_marginTop; - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return m_marginRight; - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return m_marginLeft; } ASSERT_NOT_REACHED(); @@ -134,62 +124,42 @@ int RenderBox::marginAfterUsing(const RenderStyle* s) const int RenderBox::marginStart() const { - return marginStartUsing(style()); -} - -int RenderBox::marginStartUsing(const RenderStyle* s) const -{ - if (s->isVerticalBlockFlow()) - return s->direction() == LTR ? m_marginLeft : m_marginRight; - return s->direction() == LTR ? m_marginTop : m_marginBottom; + if (style()->isHorizontalWritingMode()) + return style()->isLeftToRightDirection() ? m_marginLeft : m_marginRight; + return style()->isLeftToRightDirection() ? m_marginTop : m_marginBottom; } int RenderBox::marginEnd() const { - return marginEndUsing(style()); -} - -int RenderBox::marginEndUsing(const RenderStyle* s) const -{ - if (s->isVerticalBlockFlow()) - return s->direction() == LTR ? m_marginRight : m_marginLeft; - return s->direction() == LTR ? m_marginBottom : m_marginTop; + if (style()->isHorizontalWritingMode()) + return style()->isLeftToRightDirection() ? m_marginRight : m_marginLeft; + return style()->isLeftToRightDirection() ? m_marginBottom : m_marginTop; } void RenderBox::setMarginStart(int margin) { - setMarginStartUsing(style(), margin); -} - -void RenderBox::setMarginEnd(int margin) -{ - setMarginEndUsing(style(), margin); -} - -void RenderBox::setMarginStartUsing(const RenderStyle* s, int margin) -{ - if (s->isVerticalBlockFlow()) { - if (s->direction() == LTR) + if (style()->isHorizontalWritingMode()) { + if (style()->isLeftToRightDirection()) m_marginLeft = margin; else m_marginRight = margin; } else { - if (s->direction() == LTR) + if (style()->isLeftToRightDirection()) m_marginTop = margin; else m_marginBottom = margin; } } -void RenderBox::setMarginEndUsing(const RenderStyle* s, int margin) +void RenderBox::setMarginEnd(int margin) { - if (s->isVerticalBlockFlow()) { - if (s->direction() == LTR) + if (style()->isHorizontalWritingMode()) { + if (style()->isLeftToRightDirection()) m_marginRight = margin; else m_marginLeft = margin; } else { - if (s->direction() == LTR) + if (style()->isLeftToRightDirection()) m_marginBottom = margin; else m_marginTop = margin; @@ -198,45 +168,35 @@ void RenderBox::setMarginEndUsing(const RenderStyle* s, int margin) void RenderBox::setMarginBefore(int margin) { - setMarginBeforeUsing(style(), margin); -} - -void RenderBox::setMarginAfter(int margin) -{ - setMarginAfterUsing(style(), margin); -} - -void RenderBox::setMarginBeforeUsing(const RenderStyle* s, int margin) -{ - switch (s->blockFlow()) { - case TopToBottomBlockFlow: + switch (style()->writingMode()) { + case TopToBottomWritingMode: m_marginTop = margin; break; - case BottomToTopBlockFlow: + case BottomToTopWritingMode: m_marginBottom = margin; break; - case LeftToRightBlockFlow: + case LeftToRightWritingMode: m_marginLeft = margin; break; - case RightToLeftBlockFlow: + case RightToLeftWritingMode: m_marginRight = margin; break; } } -void RenderBox::setMarginAfterUsing(const RenderStyle* s, int margin) +void RenderBox::setMarginAfter(int margin) { - switch (s->blockFlow()) { - case TopToBottomBlockFlow: + switch (style()->writingMode()) { + case TopToBottomWritingMode: m_marginBottom = margin; break; - case BottomToTopBlockFlow: + case BottomToTopWritingMode: m_marginTop = margin; break; - case LeftToRightBlockFlow: + case LeftToRightWritingMode: m_marginRight = margin; break; - case RightToLeftBlockFlow: + case RightToLeftWritingMode: m_marginLeft = margin; break; } @@ -270,8 +230,13 @@ void RenderBox::removeFloatingOrPositionedChildFromBlockLists() outermostBlock = p; } - if (outermostBlock) + if (outermostBlock) { + RenderObject* parent = outermostBlock->parent(); + if (parent && parent->isFlexibleBox()) + outermostBlock = toRenderBlock(parent); + outermostBlock->markAllDescendantsWithFloatsForLayout(this, false); + } } if (isPositioned()) { @@ -347,11 +312,16 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle // Set the text color if we're the body. if (isBody()) document()->setTextColor(style()->visitedDependentColor(CSSPropertyColor)); - else if (oldStyle && isRoot() && oldStyle->blockFlow() != style()->blockFlow()) { - // Propagate the new block flow up to the RenderView. + else if (isRoot() && (!oldStyle || oldStyle->writingMode() != style()->writingMode() || oldStyle->direction() != style()->direction())) { + // Propagate the new block flow and direction up to the RenderView. + // FIXME: WinIE seems to propagate from the <body> as well. We may want to consider doing that at some point. RenderView* viewRenderer = view(); - viewRenderer->style()->setBlockFlow(style()->blockFlow()); - viewRenderer->setNeedsLayoutAndPrefWidthsRecalc(); + RenderStyle* viewStyle = viewRenderer->style(); + if (viewStyle->writingMode() != style()->writingMode() || viewStyle->direction() != style()->direction()) { + viewStyle->setWritingMode(style()->writingMode()); + viewStyle->setDirection(style()->direction()); + viewRenderer->setNeedsLayoutAndPrefWidthsRecalc(); + } } } @@ -434,7 +404,7 @@ int RenderBox::scrollWidth() const if (hasOverflowClip()) return layer()->scrollWidth(); // For objects with visible overflow, this matches IE. - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) return max(clientWidth(), rightmostPosition(true, false) - borderLeft()); return clientWidth() - min(0, leftmostPosition(true, false) - borderLeft()); } @@ -479,6 +449,24 @@ void RenderBox::absoluteQuads(Vector<FloatQuad>& quads) quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height()))); } +IntRect RenderBox::applyLayerTransformToRect(const IntRect& rect) const +{ + if (layer() && layer()->hasTransform()) { + TransformationMatrix transform; + transform.makeIdentity(); + transform.translate(rect.x(), rect.y()); + layer()->updateTransform(); + transform.multLeft(layer()->currentTransform()); + return transform.mapRect(IntRect(0, 0, rect.width(), rect.height())); + } + return rect; +} + +IntRect RenderBox::transformedFrameRect() const +{ + return applyLayerTransformToRect(frameRect()); +} + IntRect RenderBox::absoluteContentBox() const { IntRect rect = contentBoxRect(); @@ -721,7 +709,7 @@ bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result // 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). IntRect boundsRect = IntRect(tx, ty, width(), height()); - if (visibleToHitTesting() && action == HitTestForeground && boundsRect.intersects(result.rectFromPoint(xPos, yPos))) { + if (visibleToHitTesting() && action == HitTestForeground && boundsRect.intersects(result.rectForPoint(xPos, yPos))) { updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); if (!result.addNodeToRectBasedTestResult(node(), xPos, yPos, boundsRect)) return true; @@ -833,7 +821,7 @@ void RenderBox::paintBoxDecorationsWithSize(PaintInfo& paintInfo, int tx, int ty void RenderBox::paintMask(PaintInfo& paintInfo, int tx, int ty) { - if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled()) return; int w = width(); @@ -856,6 +844,11 @@ void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int bool allMaskImagesLoaded = true; if (!compositedMask) { + // If the context has a rotation, scale or skew, then use a transparency layer to avoid + // pixel cruft around the edge of the mask. + const AffineTransform& currentCTM = paintInfo.context->getCTM(); + pushTransparencyLayer = !currentCTM.isIdentityOrTranslationOrFlipped(); + StyleImage* maskBoxImage = style()->maskBoxImage().image(); const FillLayer* maskLayers = style()->maskLayers(); @@ -1141,7 +1134,7 @@ int RenderBox::perpendicularContainingBlockLogicalHeight() const // 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->isVerticalBlockFlow() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth(); + return containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth(); } // Use the content box logical height as specified by the style. @@ -1507,11 +1500,11 @@ void RenderBox::computeLogicalWidth() bool stretching = (parent()->style()->boxAlign() == BSTRETCH); bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching); - Length logicalWidthLength = (treatAsReplaced) ? Length(computeReplacedWidth(), Fixed) : style()->logicalWidth(); + Length logicalWidthLength = (treatAsReplaced) ? Length(computeReplacedLogicalWidth(), Fixed) : style()->logicalWidth(); RenderBlock* cb = containingBlock(); int containerLogicalWidth = max(0, containingBlockLogicalWidthForContent()); - bool hasPerpendicularContainingBlock = cb->style()->isVerticalBlockFlow() != style()->isVerticalBlockFlow(); + bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode(); int containerWidthInInlineDirection = containerLogicalWidth; if (hasPerpendicularContainingBlock) containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight(); @@ -1596,7 +1589,7 @@ void RenderBox::computeLogicalWidth() if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (logicalWidth() + marginStart() + marginEnd()) && !isFloating() && !isInline() && !cb->isFlexibleBox()) - setMarginEndUsing(cb->style(), containerLogicalWidth - logicalWidth() - marginStartUsing(cb->style())); + cb->setMarginEndForChild(this, containerLogicalWidth - logicalWidth() - cb->marginStartForChild(this)); } int RenderBox::computeLogicalWidthUsing(LogicalWidthType widthType, int availableLogicalWidth) @@ -1678,31 +1671,31 @@ void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, int // Case One: The object is being centered in the containing block's available logical width. if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth) || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock->style()->textAlign() == WEBKIT_CENTER)) { - setMarginStartUsing(containingBlockStyle, max(0, (containerWidth - childWidth) / 2)); - setMarginEndUsing(containingBlockStyle, containerWidth - childWidth - marginStartUsing(containingBlockStyle)); + containingBlock->setMarginStartForChild(this, max(0, (containerWidth - childWidth) / 2)); + containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this)); return; } // Case Two: The object is being pushed to the start of the containing block's available logical width. if (marginEndLength.isAuto() && childWidth < containerWidth) { - setMarginStartUsing(containingBlockStyle, marginStartLength.calcValue(containerWidth)); - setMarginEndUsing(containingBlockStyle, containerWidth - childWidth - marginStartUsing(containingBlockStyle)); + containingBlock->setMarginStartForChild(this, marginStartLength.calcValue(containerWidth)); + containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this)); return; } // Case Three: The object is being pushed to the end of the containing block's available logical width. - bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((containingBlockStyle->direction() == RTL && containingBlockStyle->textAlign() == WEBKIT_LEFT) - || (containingBlockStyle->direction() == LTR && containingBlockStyle->textAlign() == WEBKIT_RIGHT)); + bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_LEFT) + || (containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_RIGHT)); if ((marginStartLength.isAuto() && childWidth < containerWidth) || pushToEndFromTextAlign) { - setMarginEndUsing(containingBlockStyle, marginEndLength.calcValue(containerWidth)); - setMarginStartUsing(containingBlockStyle, containerWidth - childWidth - marginEndUsing(containingBlockStyle)); + containingBlock->setMarginEndForChild(this, marginEndLength.calcValue(containerWidth)); + containingBlock->setMarginStartForChild(this, containerWidth - childWidth - containingBlock->marginEndForChild(this)); return; } // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3). In that case // auto margins will just turn into 0. - setMarginStartUsing(containingBlockStyle, marginStartLength.calcMinValue(containerWidth)); - setMarginEndUsing(containingBlockStyle, marginEndLength.calcMinValue(containerWidth)); + containingBlock->setMarginStartForChild(this, marginStartLength.calcMinValue(containerWidth)); + containingBlock->setMarginEndForChild(this, marginEndLength.calcMinValue(containerWidth)); } void RenderBox::computeLogicalHeight() @@ -1718,7 +1711,7 @@ void RenderBox::computeLogicalHeight() computePositionedLogicalHeight(); } else { RenderBlock* cb = containingBlock(); - bool hasPerpendicularContainingBlock = cb->style()->isVerticalBlockFlow() != style()->isVerticalBlockFlow(); + bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode(); if (!hasPerpendicularContainingBlock) computeBlockDirectionMargins(cb); @@ -1745,7 +1738,7 @@ void RenderBox::computeLogicalHeight() && parent()->isFlexingChildren()) h = Length(overrideSize() - borderAndPaddingLogicalHeight(), Fixed); else if (treatAsReplaced) - h = Length(computeReplacedHeight(), Fixed); + h = Length(computeReplacedLogicalHeight(), Fixed); else { h = style()->logicalHeight(); checkMinMaxHeight = true; @@ -1797,7 +1790,7 @@ void RenderBox::computeLogicalHeight() // 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())); - if (stretchesToViewHeight() || paginatedContentNeedsBaseHeight) { + if (stretchesToViewport() || paginatedContentNeedsBaseHeight) { // FIXME: Finish accounting for block flow here. // https://bugs.webkit.org/show_bug.cgi?id=46603 int margins = collapsedMarginBefore() + collapsedMarginAfter(); @@ -1805,7 +1798,7 @@ void RenderBox::computeLogicalHeight() if (document()->printing()) visHeight = static_cast<int>(view()->pageHeight()); else { - if (style()->isVerticalBlockFlow()) + if (style()->isHorizontalWritingMode()) visHeight = view()->viewHeight(); else visHeight = view()->viewWidth(); @@ -1916,48 +1909,48 @@ int RenderBox::computePercentageLogicalHeight(const Length& height) return result; } -int RenderBox::computeReplacedWidth(bool includeMaxWidth) const +int RenderBox::computeReplacedLogicalWidth(bool includeMaxWidth) const { - int width = computeReplacedWidthUsing(style()->width()); - int minW = computeReplacedWidthUsing(style()->minWidth()); - int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : computeReplacedWidthUsing(style()->maxWidth()); + int logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth()); + int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); + int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); - return max(minW, min(width, maxW)); + return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); } -int RenderBox::computeReplacedWidthUsing(Length width) const +int RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const { - switch (width.type()) { + switch (logicalWidth.type()) { case Fixed: - return computeContentBoxLogicalWidth(width.value()); + return computeContentBoxLogicalWidth(logicalWidth.value()); case Percent: { // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the // containing block's block-flow. // https://bugs.webkit.org/show_bug.cgi?id=46496 const int cw = isPositioned() ? containingBlockWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent(); if (cw > 0) - return computeContentBoxLogicalWidth(width.calcMinValue(cw)); + return computeContentBoxLogicalWidth(logicalWidth.calcMinValue(cw)); } // fall through default: - return intrinsicSize().width(); + return intrinsicLogicalWidth(); } } -int RenderBox::computeReplacedHeight() const +int RenderBox::computeReplacedLogicalHeight() const { - int height = computeReplacedHeightUsing(style()->height()); - int minH = computeReplacedHeightUsing(style()->minHeight()); - int maxH = style()->maxHeight().isUndefined() ? height : computeReplacedHeightUsing(style()->maxHeight()); + int logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight()); + int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); + int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); - return max(minH, min(height, maxH)); + return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); } -int RenderBox::computeReplacedHeightUsing(Length height) const +int RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const { - switch (height.type()) { + switch (logicalHeight.type()) { case Fixed: - return computeContentBoxLogicalHeight(height.value()); + return computeContentBoxLogicalHeight(logicalHeight.value()); case Percent: { RenderObject* cb = isPositioned() ? container() : containingBlock(); @@ -1966,6 +1959,8 @@ int RenderBox::computeReplacedHeightUsing(Length height) const toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); } + // FIXME: This calculation is not patched for block-flow yet. + // https://bugs.webkit.org/show_bug.cgi?id=46500 if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { ASSERT(cb->isRenderBlock()); RenderBlock* block = toRenderBlock(cb); @@ -1973,25 +1968,30 @@ int RenderBox::computeReplacedHeightUsing(Length height) const block->computeLogicalHeight(); int newHeight = block->computeContentBoxLogicalHeight(block->contentHeight()); block->setHeight(oldHeight); - return computeContentBoxLogicalHeight(height.calcValue(newHeight)); + return computeContentBoxLogicalHeight(logicalHeight.calcValue(newHeight)); } + // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the + // containing block's block-flow. + // https://bugs.webkit.org/show_bug.cgi?id=46496 int availableHeight = isPositioned() ? containingBlockHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableLogicalHeight(); // 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. - if (cb->isTableCell() && (cb->style()->height().isAuto() || cb->style()->height().isPercent())) { + // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right. + // https://bugs.webkit.org/show_bug.cgi?id=46997 + if (cb->isTableCell() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) { // Don't let table cells squeeze percent-height replaced elements // <http://bugs.webkit.org/show_bug.cgi?id=15359> - availableHeight = max(availableHeight, intrinsicSize().height()); - return height.calcValue(availableHeight - borderAndPaddingHeight()); + availableHeight = max(availableHeight, intrinsicLogicalHeight()); + return logicalHeight.calcValue(availableHeight - borderAndPaddingLogicalHeight()); } - return computeContentBoxLogicalHeight(height.calcValue(availableHeight)); + return computeContentBoxLogicalHeight(logicalHeight.calcValue(availableHeight)); } default: - return intrinsicSize().height(); + return intrinsicLogicalHeight(); } } @@ -2006,7 +2006,7 @@ int RenderBox::availableLogicalHeightUsing(const Length& h) const return computeContentBoxLogicalHeight(h.value()); if (isRenderView()) - return style()->isVerticalBlockFlow() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth(); + return style()->isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth(); // We need to stop here, since we don't want to increase the height of the table // artificially. We're going to rely on this cell getting expanded to some new @@ -2046,8 +2046,8 @@ void RenderBox::computeBlockDirectionMargins(RenderBlock* containingBlock) int cw = containingBlockLogicalWidthForContent(); RenderStyle* containingBlockStyle = containingBlock->style(); - setMarginBeforeUsing(containingBlockStyle, style()->marginBeforeUsing(containingBlockStyle).calcMinValue(cw)); - setMarginAfterUsing(containingBlockStyle, style()->marginAfterUsing(containingBlockStyle).calcMinValue(cw)); + containingBlock->setMarginBeforeForChild(this, style()->marginBeforeUsing(containingBlockStyle).calcMinValue(cw)); + containingBlock->setMarginAfterForChild(this, style()->marginAfterUsing(containingBlockStyle).calcMinValue(cw)); } int RenderBox::containingBlockWidthForPositioned(const RenderBoxModelObject* containingBlock) const @@ -2069,7 +2069,7 @@ int RenderBox::containingBlockWidthForPositioned(const RenderBoxModelObject* con int fromLeft; int fromRight; - if (containingBlock->style()->direction() == LTR) { + if (containingBlock->style()->isLeftToRightDirection()) { fromLeft = first->logicalLeft() + first->borderLogicalLeft(); fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight(); } else { @@ -2411,7 +2411,7 @@ void RenderBox::computePositionedLogicalWidthUsing(Length width, const RenderBox // positioned, inline because right now, it is using the xPos // of the first line box when really it should use the last line box. When // this is fixed elsewhere, this block should be removed. - if (containerBlock->isRenderInline() && containerBlock->style()->direction() == RTL) { + if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { const RenderInline* flow = toRenderInline(containerBlock); InlineFlowBox* firstLine = flow->firstLineBox(); InlineFlowBox* lastLine = flow->lastLineBox(); @@ -2684,7 +2684,7 @@ void RenderBox::computePositionedLogicalWidthReplaced() // NOTE: This value of width is FINAL in that the min/max width calculations // are dealt with in computeReplacedWidth(). This means that the steps to produce // correct max/min in the non-replaced version, are not necessary. - setWidth(computeReplacedWidth() + borderAndPaddingWidth()); + setWidth(computeReplacedLogicalWidth() + borderAndPaddingWidth()); const int availableSpace = containerWidth - width(); /*-----------------------------------------------------------------------*\ @@ -2817,7 +2817,7 @@ void RenderBox::computePositionedLogicalWidthReplaced() // positioned, inline containing block because right now, it is using the xPos // of the first line box when really it should use the last line box. When // this is fixed elsewhere, this block should be removed. - if (containerBlock->isRenderInline() && containerBlock->style()->direction() == RTL) { + if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { const RenderInline* flow = toRenderInline(containerBlock); InlineFlowBox* firstLine = flow->firstLineBox(); InlineFlowBox* lastLine = flow->lastLineBox(); @@ -2857,7 +2857,7 @@ void RenderBox::computePositionedLogicalHeightReplaced() // NOTE: This value of height is FINAL in that the min/max height calculations // are dealt with in computeReplacedHeight(). This means that the steps to produce // correct max/min in the non-replaced version, are not necessary. - setHeight(computeReplacedHeight() + borderAndPaddingHeight()); + setHeight(computeReplacedLogicalHeight() + borderAndPaddingHeight()); const int availableSpace = containerHeight - height(); /*-----------------------------------------------------------------------*\ @@ -2971,9 +2971,9 @@ IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWid // FIXME: What about border and padding? IntRect rect(x(), y(), caretWidth, height()); - TextDirection direction = box ? box->direction() : style()->direction(); + bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection(); - if ((!caretOffset) ^ (direction == LTR)) + if ((!caretOffset) ^ ltr) rect.move(IntSize(width() - caretWidth, 0)); if (box) { @@ -3003,30 +3003,44 @@ IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWid return rect; } -int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +int RenderBox::topmostPosition(bool /*includeOverflowInterior*/, bool includeSelf, ApplyTransform applyTransform) const +{ + IntRect transformedRect = applyTransform == IncludeTransform && includeSelf ? transformedFrameRect() : frameRect(); + if (!includeSelf || !transformedRect.width()) + return 0; + int top = 0; + if (isRelPositioned()) + top += relativePositionOffsetY(); + return top; +} + +int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf, ApplyTransform applyTransform) const { - if (!includeSelf || !width()) + IntRect transformedRect = applyTransform == IncludeTransform && includeSelf ? transformedFrameRect() : frameRect(); + if (!includeSelf || !transformedRect.width()) return 0; - int bottom = height(); + int bottom = transformedRect.height(); if (isRelPositioned()) bottom += relativePositionOffsetY(); return bottom; } -int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf, ApplyTransform applyTransform) const { - if (!includeSelf || !height()) + IntRect transformedRect = applyTransform == IncludeTransform && includeSelf ? transformedFrameRect() : frameRect(); + if (!includeSelf || !transformedRect.height()) return 0; - int right = width(); + int right = transformedRect.width(); if (isRelPositioned()) right += relativePositionOffsetX(); return right; } -int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf, ApplyTransform applyTransform) const { - if (!includeSelf || !height()) - return width(); + IntRect transformedRect = applyTransform == IncludeTransform && includeSelf ? transformedFrameRect() : frameRect(); + if (!includeSelf || !transformedRect.height()) + return transformedRect.width(); int left = 0; if (isRelPositioned()) left += relativePositionOffsetX(); @@ -3137,7 +3151,7 @@ bool RenderBox::shrinkToAvoidFloats() const bool RenderBox::avoidsFloats() const { - return isReplaced() || hasOverflowClip() || isHR() || isBlockFlowRoot(); + return isReplaced() || hasOverflowClip() || isHR() || isWritingModeRoot(); } void RenderBox::addShadowOverflow() @@ -3215,19 +3229,18 @@ void RenderBox::clearLayoutOverflow() m_overflow->resetLayoutOverflow(borderBoxRect()); } -void RenderBox::markDescendantBlocksAndLinesForLayout(bool inLayout) +int RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const { - if (!m_everHadLayout || isReplaced()) - return; - - setChildNeedsLayout(true, !inLayout); + if (isReplaced()) + return direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft; + return 0; +} - // Iterate over our children and mark them as needed. - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (child->isFloatingOrPositioned()) - continue; - child->markDescendantBlocksAndLinesForLayout(inLayout); - } +int RenderBox::baselinePosition(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const +{ + if (isReplaced()) + return direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft; + return 0; } } // namespace WebCore diff --git a/WebCore/rendering/RenderBox.h b/WebCore/rendering/RenderBox.h index 1b70fad..2e37657 100644 --- a/WebCore/rendering/RenderBox.h +++ b/WebCore/rendering/RenderBox.h @@ -50,41 +50,41 @@ public: void setWidth(int width) { m_frameRect.setWidth(width); } void setHeight(int height) { m_frameRect.setHeight(height); } - int logicalLeft() const { return style()->isVerticalBlockFlow() ? x() : y(); } - int logicalTop() const { return style()->isVerticalBlockFlow() ? y() : x(); } - int logicalWidth() const { return style()->isVerticalBlockFlow() ? width() : height(); } - int logicalHeight() const { return style()->isVerticalBlockFlow() ? height() : width(); } + int logicalLeft() const { return style()->isHorizontalWritingMode() ? x() : y(); } + int logicalTop() const { return style()->isHorizontalWritingMode() ? y() : x(); } + int logicalWidth() const { return style()->isHorizontalWritingMode() ? width() : height(); } + int logicalHeight() const { return style()->isHorizontalWritingMode() ? height() : width(); } void setLogicalLeft(int left) { - if (style()->isVerticalBlockFlow()) + if (style()->isHorizontalWritingMode()) setX(left); else setY(left); } void setLogicalTop(int top) { - if (style()->isVerticalBlockFlow()) + if (style()->isHorizontalWritingMode()) setY(top); else setX(top); } void setLogicalWidth(int size) { - if (style()->isVerticalBlockFlow()) + if (style()->isHorizontalWritingMode()) setWidth(size); else setHeight(size); } void setLogicalHeight(int size) { - if (style()->isVerticalBlockFlow()) + if (style()->isHorizontalWritingMode()) setHeight(size); else setWidth(size); } void setLogicalLocation(int left, int top) { - if (style()->isVerticalBlockFlow()) + if (style()->isHorizontalWritingMode()) setLocation(left, top); else setLocation(top, left); @@ -103,6 +103,9 @@ public: IntRect frameRect() const { return m_frameRect; } void setFrameRect(const IntRect& rect) { m_frameRect = rect; } + IntRect transformedFrameRect() const; + IntRect applyLayerTransformToRect(const IntRect&) const; + IntRect borderBoxRect() const { return IntRect(0, 0, width(), height()); } virtual IntRect borderBoundingBox() const { return borderBoxRect(); } @@ -127,19 +130,23 @@ public: int bottomVisibleOverflow() const { return hasOverflowClip() ? bottomVisualOverflow() : std::max(bottomLayoutOverflow(), bottomVisualOverflow()); } int leftVisibleOverflow() const { return hasOverflowClip() ? leftVisualOverflow() : std::min(leftLayoutOverflow(), leftVisualOverflow()); } int rightVisibleOverflow() const { return hasOverflowClip() ? rightVisualOverflow() : std::max(rightLayoutOverflow(), rightVisualOverflow()); } - + IntRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : borderBoxRect(); } int topLayoutOverflow() const { return m_overflow? m_overflow->topLayoutOverflow() : 0; } int bottomLayoutOverflow() const { return m_overflow ? m_overflow->bottomLayoutOverflow() : height(); } int leftLayoutOverflow() const { return m_overflow ? m_overflow->leftLayoutOverflow() : 0; } int rightLayoutOverflow() const { return m_overflow ? m_overflow->rightLayoutOverflow() : width(); } + int logicalLeftLayoutOverflow() const { return style()->isHorizontalWritingMode() ? leftLayoutOverflow() : topLayoutOverflow(); } + int logicalRightLayoutOverflow() const { return style()->isHorizontalWritingMode() ? rightLayoutOverflow() : bottomLayoutOverflow(); } IntRect visualOverflowRect() const { return m_overflow ? m_overflow->visualOverflowRect() : borderBoxRect(); } int topVisualOverflow() const { return m_overflow? m_overflow->topVisualOverflow() : 0; } int bottomVisualOverflow() const { return m_overflow ? m_overflow->bottomVisualOverflow() : height(); } int leftVisualOverflow() const { return m_overflow ? m_overflow->leftVisualOverflow() : 0; } int rightVisualOverflow() const { return m_overflow ? m_overflow->rightVisualOverflow() : width(); } - + int logicalLeftVisualOverflow() const { return style()->isHorizontalWritingMode() ? leftVisualOverflow() : topVisualOverflow(); } + int logicalRightVisualOverflow() const { return style()->isHorizontalWritingMode() ? rightVisualOverflow() : bottomVisualOverflow(); } + void addLayoutOverflow(const IntRect&); void addVisualOverflow(const IntRect&); @@ -150,8 +157,8 @@ public: int contentWidth() const { return clientWidth() - paddingLeft() - paddingRight(); } int contentHeight() const { return clientHeight() - paddingTop() - paddingBottom(); } - int contentLogicalWidth() const { return style()->isVerticalBlockFlow() ? contentWidth() : contentHeight(); } - int contentLogicalHeight() const { return style()->isVerticalBlockFlow() ? contentHeight() : contentWidth(); } + int contentLogicalWidth() const { return style()->isHorizontalWritingMode() ? contentWidth() : contentHeight(); } + int contentLogicalHeight() const { return style()->isHorizontalWritingMode() ? contentHeight() : contentWidth(); } // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines (RenderFlow) // to return the remaining width on a given line (and the height of a single line). @@ -181,6 +188,10 @@ public: virtual int marginBottom() const { return m_marginBottom; } virtual int marginLeft() const { return m_marginLeft; } virtual int marginRight() const { return m_marginRight; } + void setMarginTop(int margin) { m_marginTop = margin; } + void setMarginBottom(int margin) { m_marginBottom = margin; } + void setMarginLeft(int margin) { m_marginLeft = margin; } + void setMarginRight(int margin) { m_marginRight = margin; } virtual int marginBefore() const; virtual int marginAfter() const; virtual int marginStart() const; @@ -189,7 +200,7 @@ public: void setMarginEnd(int); void setMarginBefore(int); void setMarginAfter(int); - + // The following five functions are used to implement collapsing margins. // All objects know their maximal positive and negative margins. The // formula for computing a collapsed margin is |maxPosMargin| - |maxNegmargin|. @@ -198,24 +209,8 @@ public: // methods. enum MarginSign { PositiveMargin, NegativeMargin }; virtual bool isSelfCollapsingBlock() const { return false; } - int collapsedMarginBefore() const - { - return maxMarginBefore(PositiveMargin) - maxMarginBefore(NegativeMargin); - } - int collapsedMarginAfter() const - { - return maxMarginAfter(PositiveMargin) - maxMarginAfter(NegativeMargin); -} - virtual int maxMarginBefore(MarginSign sign) const - { - int beforeMargin = marginBefore(); - return (sign == PositiveMargin) ? std::max(0, beforeMargin) : -std::min(0, beforeMargin); - } - virtual int maxMarginAfter(MarginSign sign) const - { - int afterMargin = marginAfter(); - return (sign == PositiveMargin) ? std::max(0, afterMargin) : -std::min(0, afterMargin); - } + virtual int collapsedMarginBefore() const { return marginBefore(); } + virtual int collapsedMarginAfter() const { return marginAfter(); } virtual void absoluteRects(Vector<IntRect>&, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); @@ -267,9 +262,11 @@ public: void setInlineBoxWrapper(InlineBox* boxWrapper) { m_inlineBoxWrapper = boxWrapper; } void deleteLineBoxWrapper(); - virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; - virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; - virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + enum ApplyTransform { IncludeTransform, ExcludeTransform }; + virtual int topmostPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; + virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; + virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; + virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); @@ -282,12 +279,14 @@ public: virtual void computeLogicalWidth(); virtual void computeLogicalHeight(); - bool stretchesToViewHeight() const + bool stretchesToViewport() const { - return document()->inQuirksMode() && style()->height().isAuto() && !isFloatingOrPositioned() && (isRoot() || isBody()) && !isBlockFlowRoot(); + return document()->inQuirksMode() && style()->logicalHeight().isAuto() && !isFloatingOrPositioned() && (isRoot() || isBody()) && !isWritingModeRoot(); } virtual IntSize intrinsicSize() const { return IntSize(); } + int intrinsicLogicalWidth() const { return style()->isHorizontalWritingMode() ? intrinsicSize().width() : intrinsicSize().height(); } + int intrinsicLogicalHeight() const { return style()->isHorizontalWritingMode() ? intrinsicSize().height() : intrinsicSize().width(); } // 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. @@ -296,11 +295,11 @@ public: int computeLogicalWidthUsing(LogicalWidthType, int availableLogicalWidth); int computeLogicalHeightUsing(const Length& height); - int computeReplacedWidthUsing(Length width) const; - int computeReplacedHeightUsing(Length height) const; + int computeReplacedLogicalWidthUsing(Length width) const; + int computeReplacedLogicalHeightUsing(Length height) const; - virtual int computeReplacedWidth(bool includeMaxWidth = true) const; - virtual int computeReplacedHeight() const; + virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual int computeReplacedLogicalHeight() const; int computePercentageLogicalHeight(const Length& height); @@ -311,11 +310,12 @@ public: // 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. - int availableWidth() const { return style()->isVerticalBlockFlow() ? availableLogicalWidth() : availableLogicalHeight(); } - int availableHeight() const { return style()->isVerticalBlockFlow() ? availableLogicalHeight() : availableLogicalWidth(); } + int availableWidth() const { return style()->isHorizontalWritingMode() ? availableLogicalWidth() : availableLogicalHeight(); } + int availableHeight() const { return style()->isHorizontalWritingMode() ? availableLogicalHeight() : availableLogicalWidth(); } virtual int verticalScrollbarWidth() const; int horizontalScrollbarHeight() const; + int scrollbarLogicalHeight() const { return style()->isHorizontalWritingMode() ? horizontalScrollbarHeight() : verticalScrollbarWidth(); } virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0); bool canBeScrolledAndHasScrollableArea() const; virtual bool canBeProgramaticallyScrolled(bool) const; @@ -367,9 +367,12 @@ public: bool shrinkToAvoidFloats() const; virtual bool avoidsFloats() const; - virtual void markDescendantBlocksAndLinesForLayout(bool inLayout = true); + virtual void markForPaginationRelayoutIfNeeded() { } + + bool isWritingModeRoot() const { return !parent() || parent()->style()->writingMode() != style()->writingMode(); } - bool isBlockFlowRoot() const { return !parent() || parent()->style()->blockFlow() != style()->blockFlow(); } + virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + virtual int baselinePosition(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; #ifdef ANDROID_LAYOUT int getVisibleWidth() const { return m_visibleWidth; } @@ -426,16 +429,6 @@ private: // These include tables, positioned objects, floats and flexible boxes. virtual void computePreferredLogicalWidths() { setPreferredLogicalWidthsDirty(false); } - void setMarginStartUsing(const RenderStyle*, int); - void setMarginEndUsing(const RenderStyle*, int); - void setMarginBeforeUsing(const RenderStyle*, int); - void setMarginAfterUsing(const RenderStyle*, int); - - int marginStartUsing(const RenderStyle*) const; - int marginEndUsing(const RenderStyle*) const; - int marginBeforeUsing(const RenderStyle*) const; - int marginAfterUsing(const RenderStyle*) const; - private: // The width/height of the contents + borders + padding. The x/y location is relative to our container (which is not always our parent). IntRect m_frameRect; diff --git a/WebCore/rendering/RenderBoxModelObject.cpp b/WebCore/rendering/RenderBoxModelObject.cpp index bbb9c2c..ec065a6 100644 --- a/WebCore/rendering/RenderBoxModelObject.cpp +++ b/WebCore/rendering/RenderBoxModelObject.cpp @@ -324,7 +324,7 @@ int RenderBoxModelObject::relativePositionOffsetX() const // call availableWidth on our containing block. if (!style()->left().isAuto()) { RenderBlock* cb = containingBlock(); - if (!style()->right().isAuto() && containingBlock()->style()->direction() == RTL) + if (!style()->right().isAuto() && !containingBlock()->style()->isLeftToRightDirection()) return -style()->right().calcValue(cb->availableWidth()); return style()->left().calcValue(cb->availableWidth()); } @@ -348,13 +348,13 @@ int RenderBoxModelObject::relativePositionOffsetY() const if (!style()->top().isAuto() && (!containingBlock->style()->height().isAuto() || !style()->top().isPercent() - || containingBlock->stretchesToViewHeight())) + || containingBlock->stretchesToViewport())) return style()->top().calcValue(containingBlock->availableHeight()); if (!style()->bottom().isAuto() && (!containingBlock->style()->height().isAuto() || !style()->bottom().isPercent() - || containingBlock->stretchesToViewHeight())) + || containingBlock->stretchesToViewport())) return -style()->bottom().calcValue(containingBlock->availableHeight()); return 0; @@ -855,23 +855,25 @@ int RenderBoxModelObject::verticalPosition(bool firstLine) const const Font& f = parent()->style(firstLine)->font(); int fontsize = f.pixelSize(); + LineDirectionMode lineDirection = parent()->style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine; + if (va == SUB) vpos += fontsize / 5 + 1; else if (va == SUPER) vpos -= fontsize / 3 + 1; else if (va == TEXT_TOP) - vpos += baselinePosition(firstLine) - f.ascent(); + vpos += baselinePosition(firstLine, lineDirection) - f.ascent(); else if (va == MIDDLE) - vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine) / 2 + baselinePosition(firstLine); + vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine, lineDirection) / 2 + baselinePosition(firstLine, lineDirection); else if (va == TEXT_BOTTOM) { vpos += f.descent(); // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case. if (!isReplaced() || style()->display() == INLINE_BLOCK) - vpos -= (lineHeight(firstLine) - baselinePosition(firstLine)); + vpos -= (lineHeight(firstLine, lineDirection) - baselinePosition(firstLine, lineDirection)); } else if (va == BASELINE_MIDDLE) - vpos += -lineHeight(firstLine) / 2 + baselinePosition(firstLine); + vpos += -lineHeight(firstLine, lineDirection) / 2 + baselinePosition(firstLine, lineDirection); else if (va == LENGTH) - vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine)); + vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine, lineDirection)); } return vpos; @@ -1061,7 +1063,7 @@ void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); graphicsContext->clipOutRoundedRect(innerBorderRect, innerTopLeftRadius, innerTopRightRadius, innerBottomLeftRadius, innerBottomRightRadius); - roundedPath = Path::createRoundedRectangle(borderRect, topLeft, topRight, bottomLeft, bottomRight); + roundedPath.addRoundedRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); graphicsContext->addPath(roundedPath); } @@ -1752,24 +1754,29 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int context->save(); - if (hasBorderRadius) - context->clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight)); - else + Path path; + if (hasBorderRadius) { + path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); + context->clip(path); + path.clear(); + } else context->clip(rect); IntSize extraOffset(2 * w + max(0, shadowOffset.width()) + shadowBlur - 2 * shadowSpread + 1, 0); context->translate(extraOffset.width(), extraOffset.height()); shadowOffset -= extraOffset; - context->beginPath(); - context->addPath(Path::createRectangle(outerRect)); + path.addRect(outerRect); if (hasBorderRadius) { if (shadowSpread > 0) uniformlyExpandBorderRadii(-shadowSpread, topLeft, topRight, bottomLeft, bottomRight); - context->addPath(Path::createRoundedRectangle(holeRect, topLeft, topRight, bottomLeft, bottomRight)); + path.addRoundedRect(holeRect, topLeft, topRight, bottomLeft, bottomRight); } else - context->addPath(Path::createRectangle(holeRect)); + path.addRect(holeRect); + + context->beginPath(); + context->addPath(path); context->setFillRule(RULE_EVENODD); context->setFillColor(fillColor, s->colorSpace()); diff --git a/WebCore/rendering/RenderBoxModelObject.h b/WebCore/rendering/RenderBoxModelObject.h index 83c9367..33d7ca2 100644 --- a/WebCore/rendering/RenderBoxModelObject.h +++ b/WebCore/rendering/RenderBoxModelObject.h @@ -33,6 +33,10 @@ const int PositionTop = -0x7fffffff; const int PositionBottom = 0x7fffffff; const int PositionUndefined = 0x80000000; +// Modes for some of the line-related functions. +enum LinePositionMode { PositionOnContainingLine, PositionOfInteriorLineBoxes }; +enum LineDirectionMode { HorizontalLine, VerticalLine }; + // This class is the base for all objects that adhere to the CSS box model as described // at http://www.w3.org/TR/CSS21/box.html @@ -98,8 +102,8 @@ public: virtual int marginStart() const = 0; virtual int marginEnd() const = 0; - bool hasHorizontalBordersPaddingOrMargin() const { return hasHorizontalBordersOrPadding() || marginLeft() != 0 || marginRight() != 0; } - bool hasHorizontalBordersOrPadding() const { return borderLeft() != 0 || borderRight() != 0 || paddingLeft() != 0 || paddingRight() != 0; } + bool hasInlineDirectionBordersPaddingOrMargin() const { return hasInlineDirectionBordersOrPadding() || marginStart()|| marginEnd(); } + bool hasInlineDirectionBordersOrPadding() const { return borderStart() || borderEnd() || paddingStart()|| paddingEnd(); } virtual int containingBlockLogicalWidthForContent() const; @@ -112,6 +116,10 @@ public: // The difference between this inline's baseline position and the line's baseline position. int verticalPosition(bool firstLine) const; + + // Overridden by subclasses to determine line height and baseline position. + virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const = 0; + virtual int baselinePosition(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const = 0; // Called by RenderObject::destroy() (and RenderWidget::destroy()) and is the only way layers should ever be destroyed void destroyLayer(); diff --git a/WebCore/rendering/RenderButton.cpp b/WebCore/rendering/RenderButton.cpp index 8357bd4..2642f23 100644 --- a/WebCore/rendering/RenderButton.cpp +++ b/WebCore/rendering/RenderButton.cpp @@ -45,6 +45,10 @@ RenderButton::RenderButton(Node* node) { } +RenderButton::~RenderButton() +{ +} + void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild) { if (!m_inner) { diff --git a/WebCore/rendering/RenderButton.h b/WebCore/rendering/RenderButton.h index 1fc5eb6..252edb4 100644 --- a/WebCore/rendering/RenderButton.h +++ b/WebCore/rendering/RenderButton.h @@ -34,7 +34,8 @@ class RenderTextFragment; // to date as the button changes. class RenderButton : public RenderFlexibleBox { public: - RenderButton(Node*); + explicit RenderButton(Node*); + virtual ~RenderButton(); virtual const char* renderName() const { return "RenderButton"; } virtual bool isRenderButton() const { return true; } diff --git a/WebCore/rendering/RenderCounter.cpp b/WebCore/rendering/RenderCounter.cpp index 639221d..c357be1 100644 --- a/WebCore/rendering/RenderCounter.cpp +++ b/WebCore/rendering/RenderCounter.cpp @@ -35,7 +35,7 @@ namespace WebCore { using namespace HTMLNames; -typedef HashMap<RefPtr<AtomicStringImpl>, CounterNode*> CounterMap; +typedef HashMap<RefPtr<AtomicStringImpl>, RefPtr<CounterNode> > CounterMap; typedef HashMap<const RenderObject*, CounterMap*> CounterMaps; static CounterNode* makeCounterNode(RenderObject*, const AtomicString& identifier, bool alwaysCreateCounter); @@ -235,10 +235,12 @@ static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& id { ASSERT(object); - if (object->m_hasCounterNodeMap) - if (CounterMap* nodeMap = counterMaps().get(object)) - if (CounterNode* node = nodeMap->get(identifier.impl())) + if (object->m_hasCounterNodeMap) { + if (CounterMap* nodeMap = counterMaps().get(object)) { + if (CounterNode* node = nodeMap->get(identifier.impl()).get()) return node; + } + } bool isReset = false; int value = 0; @@ -247,9 +249,9 @@ static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& id CounterNode* newParent = 0; CounterNode* newPreviousSibling = 0; - CounterNode* newNode = new CounterNode(object, isReset, value); + RefPtr<CounterNode> newNode = CounterNode::create(object, isReset, value); if (findPlaceForCounter(object, identifier, isReset, newParent, newPreviousSibling)) - newParent->insertAfter(newNode, newPreviousSibling, identifier); + newParent->insertAfter(newNode.get(), newPreviousSibling, identifier); CounterMap* nodeMap; if (object->m_hasCounterNodeMap) nodeMap = counterMaps().get(object); @@ -260,7 +262,7 @@ static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& id } nodeMap->set(identifier.impl(), newNode); if (newNode->parent() || !object->nextInPreOrder(object->parent())) - return newNode; + return newNode.get(); // Checking if some nodes that were previously counter tree root nodes // should become children of this node now. CounterMaps& maps = counterMaps(); @@ -268,7 +270,7 @@ static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& id for (RenderObject* currentRenderer = object->nextInPreOrder(stayWithin); currentRenderer; currentRenderer = currentRenderer->nextInPreOrder(stayWithin)) { if (!currentRenderer->m_hasCounterNodeMap) continue; - CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()); + CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()).get(); if (!currentCounter) continue; if (currentCounter->parent()) { @@ -282,7 +284,7 @@ static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& id if (currentRenderer->lastChild()) currentRenderer = currentRenderer->lastChild(); } - return newNode; + return newNode.get(); } RenderCounter::RenderCounter(Document* node, const CounterContent& counter) @@ -292,6 +294,10 @@ RenderCounter::RenderCounter(Document* node, const CounterContent& counter) { } +RenderCounter::~RenderCounter() +{ +} + const char* RenderCounter::renderName() const { return "RenderCounter"; @@ -345,9 +351,9 @@ void RenderCounter::invalidate(const AtomicString& identifier) static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node) { CounterNode* previous; - for (CounterNode* child = node->lastDescendant(); child && child != node; child = previous) { + for (RefPtr<CounterNode> child = node->lastDescendant(); child && child != node; child = previous) { previous = child->previousInPreOrder(); - child->parent()->removeChild(child, identifier); + child->parent()->removeChild(child.get(), identifier); ASSERT(counterMaps().get(child->renderer())->get(identifier.impl()) == child); counterMaps().get(child->renderer())->remove(identifier.impl()); if (!child->renderer()->documentBeingDestroyed()) { @@ -355,7 +361,6 @@ static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, if (children) children->invalidateCounters(child->renderer(), identifier); } - delete child; } RenderObject* renderer = node->renderer(); if (!renderer->documentBeingDestroyed()) { @@ -364,7 +369,6 @@ static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, } if (CounterNode* parent = node->parent()) parent->removeChild(node, identifier); - delete node; } void RenderCounter::destroyCounterNodes(RenderObject* renderer) @@ -377,7 +381,7 @@ void RenderCounter::destroyCounterNodes(RenderObject* renderer) CounterMap::const_iterator end = map->end(); for (CounterMap::const_iterator it = map->begin(); it != end; ++it) { AtomicString identifier(it->first.get()); - destroyCounterNodeWithoutMapRemoval(identifier, it->second); + destroyCounterNodeWithoutMapRemoval(identifier, it->second.get()); } maps.remove(mapsIterator); delete map; @@ -392,7 +396,7 @@ void RenderCounter::destroyCounterNode(RenderObject* renderer, const AtomicStrin CounterMap::iterator mapIterator = map->find(identifier.impl()); if (mapIterator == map->end()) return; - destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second); + destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second.get()); map->remove(mapIterator); // We do not delete "map" here even if empty because we expect to reuse // it soon. In order for a renderer to lose all its counters permanently, @@ -422,21 +426,24 @@ static void updateCounters(RenderObject* renderer) CounterMap* counterMap = counterMaps().get(renderer); ASSERT(counterMap); for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) { - CounterNode* node = counterMap->get(it->first.get()); + RefPtr<CounterNode> node = counterMap->get(it->first.get()); if (!node) { makeCounterNode(renderer, AtomicString(it->first.get()), false); continue; } CounterNode* newParent = 0; CounterNode* newPreviousSibling; + findPlaceForCounter(renderer, AtomicString(it->first.get()), node->hasResetType(), newParent, newPreviousSibling); + if (node != counterMap->get(it->first.get())) + continue; CounterNode* parent = node->parent(); if (newParent == parent && newPreviousSibling == node->previousSibling()) continue; if (parent) - parent->removeChild(node, it->first.get()); + parent->removeChild(node.get(), it->first.get()); if (newParent) - newParent->insertAfter(node, newPreviousSibling, it->first.get()); + newParent->insertAfter(node.get(), newPreviousSibling, it->first.get()); } } diff --git a/WebCore/rendering/RenderCounter.h b/WebCore/rendering/RenderCounter.h index 8d981df..9373193 100644 --- a/WebCore/rendering/RenderCounter.h +++ b/WebCore/rendering/RenderCounter.h @@ -32,6 +32,7 @@ class CounterNode; class RenderCounter : public RenderText { public: RenderCounter(Document*, const CounterContent&); + virtual ~RenderCounter(); // Removes the reference to the CounterNode associated with this renderer // if its identifier matches the argument. diff --git a/WebCore/rendering/RenderEmbeddedObject.cpp b/WebCore/rendering/RenderEmbeddedObject.cpp index aa301e2..16613a5 100644 --- a/WebCore/rendering/RenderEmbeddedObject.cpp +++ b/WebCore/rendering/RenderEmbeddedObject.cpp @@ -206,7 +206,7 @@ bool RenderEmbeddedObject::getReplacementTextGeometry(int tx, int ty, FloatRect& float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y(); replacementTextRect.setLocation(FloatPoint(x, y)); - path = Path::createRoundedRectangle(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius)); + path.addRoundedRect(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius)); return true; } diff --git a/WebCore/rendering/RenderFieldset.cpp b/WebCore/rendering/RenderFieldset.cpp index d3b88c1..12386e9 100644 --- a/WebCore/rendering/RenderFieldset.cpp +++ b/WebCore/rendering/RenderFieldset.cpp @@ -71,34 +71,43 @@ RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren) legend->setNeedsLayout(true); legend->layoutIfNeeded(); - int xPos; - if (style()->direction() == RTL) { + int logicalLeft; + if (style()->isLeftToRightDirection()) { switch (legend->style()->textAlign()) { - case LEFT: - xPos = borderLeft() + paddingLeft(); - break; - case CENTER: - xPos = (width() - legend->width()) / 2; - break; - default: - xPos = width() - paddingRight() - borderRight() - legend->width() - legend->marginRight(); + case CENTER: + logicalLeft = (logicalWidth() - logicalWidthForChild(legend)) / 2; + break; + case RIGHT: + logicalLeft = logicalWidth() - borderEnd() - paddingEnd() - logicalWidthForChild(legend); + break; + default: + logicalLeft = borderStart() + paddingStart() + marginStartForChild(legend); + break; } } else { switch (legend->style()->textAlign()) { - case RIGHT: - xPos = width() - paddingRight() - borderRight() - legend->width(); - break; - case CENTER: - xPos = (width() - legend->width()) / 2; - break; - default: - xPos = borderLeft() + paddingLeft() + legend->marginLeft(); + case LEFT: + logicalLeft = borderStart() + paddingStart(); + break; + case CENTER: { + // Make sure that the extra pixel goes to the end side in RTL (since it went to the end side + // in LTR). + int centeredWidth = logicalWidth() - logicalWidthForChild(legend); + logicalLeft = centeredWidth - centeredWidth / 2; + break; + } + default: + logicalLeft = logicalWidth() - borderStart() - paddingStart() - marginStartForChild(legend) - logicalWidthForChild(legend); + break; } } - int b = borderTop(); - int h = legend->height(); - legend->setLocation(xPos, max((b-h)/2, 0)); - setHeight(max(b, h) + paddingTop()); + + setLogicalLeftForChild(legend, logicalLeft); + + int b = borderBefore(); + int h = logicalHeightForChild(legend); + setLogicalTopForChild(legend, max((b - h) / 2, 0)); + setLogicalHeight(max(b, h) + paddingBefore()); } return legend; } @@ -129,10 +138,18 @@ void RenderFieldset::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) if (!legend) return RenderBlock::paintBoxDecorations(paintInfo, tx, ty); - int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2; - int legendBottom = ty + legend->y() + legend->height(); - h -= yOff; - ty += yOff; + // FIXME: We need to work with "rl" and "bt" block flow directions. In those + // cases the legend is embedded in the right and bottom borders respectively. + // https://bugs.webkit.org/show_bug.cgi?id=47236 + if (style()->isHorizontalWritingMode()) { + int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2; + h -= yOff; + ty += yOff; + } else { + int xOff = (legend->x() > 0) ? 0 : (legend->width() - borderLeft()) / 2; + w -= xOff; + tx += xOff; + } paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal); @@ -141,21 +158,24 @@ void RenderFieldset::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) if (!style()->hasBorder()) return; - - // Save time by not saving and restoring the GraphicsContext in the straight border case - if (!style()->hasBorderRadius()) - return paintBorderMinusLegend(paintInfo.context, tx, ty, w, h, style(), legend->x(), legend->width(), legendBottom); - // We have rounded borders, create a clipping region - // around the legend and paint the border as normal + // Create a clipping region around the legend and paint the border as normal GraphicsContext* graphicsContext = paintInfo.context; graphicsContext->save(); - int clipTop = ty; - int clipHeight = max(static_cast<int>(style()->borderTopWidth()), legend->height()); + // FIXME: We need to work with "rl" and "bt" block flow directions. In those + // cases the legend is embedded in the right and bottom borders respectively. + // https://bugs.webkit.org/show_bug.cgi?id=47236 + if (style()->isHorizontalWritingMode()) { + int clipTop = ty; + int clipHeight = max(static_cast<int>(style()->borderTopWidth()), legend->height()); + graphicsContext->clipOut(IntRect(tx + legend->x(), clipTop, legend->width(), clipHeight)); + } else { + int clipLeft = tx; + int clipWidth = max(static_cast<int>(style()->borderLeftWidth()), legend->width()); + graphicsContext->clipOut(IntRect(clipLeft, ty + legend->y(), clipWidth, legend->height())); + } - graphicsContext->clipOut(IntRect(tx + legend->x(), clipTop, - legend->width(), clipHeight)); paintBorder(paintInfo.context, tx, ty, w, h, style(), true, true); graphicsContext->restore(); @@ -172,95 +192,20 @@ void RenderFieldset::paintMask(PaintInfo& paintInfo, int tx, int ty) if (!legend) return RenderBlock::paintMask(paintInfo, tx, ty); - int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2; - h -= yOff; - ty += yOff; - - paintMaskImages(paintInfo, tx, ty, w, h); -} - -void RenderFieldset::paintBorderMinusLegend(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, - const RenderStyle* style, int lx, int lw, int lb) -{ - const Color& tc = style->visitedDependentColor(CSSPropertyBorderTopColor); - const Color& bc = style->visitedDependentColor(CSSPropertyBorderBottomColor); - - EBorderStyle ts = style->borderTopStyle(); - EBorderStyle bs = style->borderBottomStyle(); - EBorderStyle ls = style->borderLeftStyle(); - EBorderStyle rs = style->borderRightStyle(); - - bool render_t = ts > BHIDDEN; - bool render_l = ls > BHIDDEN; - bool render_r = rs > BHIDDEN; - bool render_b = bs > BHIDDEN; - - int borderLeftWidth = style->borderLeftWidth(); - int borderRightWidth = style->borderRightWidth(); - - if (render_t) { - if (lx >= borderLeftWidth) - drawLineForBoxSide(graphicsContext, tx, ty, tx + min(lx, w), ty + style->borderTopWidth(), BSTop, tc, ts, - (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0), - (lx >= w && render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0)); - if (lx + lw <= w - borderRightWidth) - drawLineForBoxSide(graphicsContext, tx + max(0, lx + lw), ty, tx + w, ty + style->borderTopWidth(), BSTop, tc, ts, - (lx + lw <= 0 && render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0), - (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0)); + // FIXME: We need to work with "rl" and "bt" block flow directions. In those + // cases the legend is embedded in the right and bottom borders respectively. + // https://bugs.webkit.org/show_bug.cgi?id=47236 + if (style()->isHorizontalWritingMode()) { + int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2; + h -= yOff; + ty += yOff; + } else { + int xOff = (legend->x() > 0) ? 0 : (legend->width() - borderLeft()) / 2; + w -= xOff; + tx += xOff; } - if (render_b) - drawLineForBoxSide(graphicsContext, tx, ty + h - style->borderBottomWidth(), tx + w, ty + h, BSBottom, bc, bs, - (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? style->borderLeftWidth() : 0), - (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? style->borderRightWidth() : 0)); - - if (render_l) { - const Color& lc = style->visitedDependentColor(CSSPropertyBorderLeftColor); - int startY = ty; - - bool ignore_top = - (tc == lc) && - (ls >= OUTSET) && - (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET); - - bool ignore_bottom = - (bc == lc) && - (ls >= OUTSET) && - (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET); - - if (lx < borderLeftWidth && lx + lw > 0) { - // The legend intersects the border. - ignore_top = true; - startY = lb; - } - - drawLineForBoxSide(graphicsContext, tx, startY, tx + borderLeftWidth, ty + h, BSLeft, lc, ls, - ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); - } - - if (render_r) { - const Color& rc = style->visitedDependentColor(CSSPropertyBorderRightColor); - int startY = ty; - - bool ignore_top = - (tc == rc) && - (rs >= DOTTED || rs == INSET) && - (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET); - - bool ignore_bottom = - (bc == rc) && - (rs >= DOTTED || rs == INSET) && - (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET); - - if (lx < w && lx + lw > w - borderRightWidth) { - // The legend intersects the border. - ignore_top = true; - startY = lb; - } - - drawLineForBoxSide(graphicsContext, tx + w - borderRightWidth, startY, tx + w, ty + h, BSRight, rc, rs, - ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); - } + paintMaskImages(paintInfo, tx, ty, w, h); } void RenderFieldset::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) diff --git a/WebCore/rendering/RenderFieldset.h b/WebCore/rendering/RenderFieldset.h index bc8e8ae..bada78c 100644 --- a/WebCore/rendering/RenderFieldset.h +++ b/WebCore/rendering/RenderFieldset.h @@ -30,7 +30,7 @@ namespace WebCore { class RenderFieldset : public RenderBlock { public: - RenderFieldset(Node*); + explicit RenderFieldset(Node*); RenderBox* findLegend() const; @@ -48,8 +48,6 @@ private: virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); virtual void paintMask(PaintInfo&, int tx, int ty); - - void paintBorderMinusLegend(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, int lx, int lw, int lb); }; inline RenderFieldset* toRenderFieldset(RenderObject* object) diff --git a/WebCore/rendering/RenderFileUploadControl.cpp b/WebCore/rendering/RenderFileUploadControl.cpp index cdb0470..3da8530 100644 --- a/WebCore/rendering/RenderFileUploadControl.cpp +++ b/WebCore/rendering/RenderFileUploadControl.cpp @@ -124,8 +124,11 @@ void RenderFileUploadControl::chooseIconForFiles(FileChooser* chooser, const Vec void RenderFileUploadControl::click() { + // Requires a user gesture to open the file dialog. + if (!frame() || !frame()->loader()->isProcessingUserGesture()) + return; if (Chrome* chromePointer = chrome()) - chromePointer->runOpenPanel(node()->document()->frame(), m_fileChooser); + chromePointer->runOpenPanel(frame(), m_fileChooser); } Chrome* RenderFileUploadControl::chrome() const @@ -213,14 +216,14 @@ void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty) const String& displayedFilename = fileTextValue(); unsigned length = displayedFilename.length(); const UChar* string = displayedFilename.characters(); - TextRun textRun(string, length, false, 0, 0, style()->direction() == RTL, style()->unicodeBidi() == Override); + TextRun textRun(string, length, false, 0, 0, !style()->isLeftToRightDirection(), style()->unicodeBidi() == Override); // Determine where the filename should be placed int contentLeft = tx + borderLeft() + paddingLeft(); int buttonAndIconWidth = m_button->renderBox()->width() + afterButtonSpacing + (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0); int textX; - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) textX = contentLeft + buttonAndIconWidth; else textX = contentLeft + contentWidth() - buttonAndIconWidth - style()->font().width(textRun); @@ -228,7 +231,7 @@ void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty) RenderButton* buttonRenderer = toRenderButton(m_button->renderer()); int textY = buttonRenderer->absoluteBoundingBoxRect().y() + buttonRenderer->marginTop() + buttonRenderer->borderTop() + buttonRenderer->paddingTop() - + buttonRenderer->baselinePosition(true, false); + + buttonRenderer->baselinePosition(true, HorizontalLine, PositionOnContainingLine); paintInfo.context->setFillColor(style()->visitedDependentColor(CSSPropertyColor), style()->colorSpace()); @@ -239,7 +242,7 @@ void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty) // Determine where the icon should be placed int iconY = ty + borderTop() + paddingTop() + (contentHeight() - iconHeight) / 2; int iconX; - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) iconX = contentLeft + m_button->renderBox()->width() + afterButtonSpacing; else iconX = contentLeft + contentWidth() - m_button->renderBox()->width() - afterButtonSpacing - iconWidth; diff --git a/WebCore/rendering/RenderFlexibleBox.cpp b/WebCore/rendering/RenderFlexibleBox.cpp index 636c873..83ec721 100644 --- a/WebCore/rendering/RenderFlexibleBox.cpp +++ b/WebCore/rendering/RenderFlexibleBox.cpp @@ -44,7 +44,7 @@ public: FlexBoxIterator(RenderFlexibleBox* parent) { box = parent; - if (box->style()->boxOrient() == HORIZONTAL && box->style()->direction() == RTL) + if (box->style()->boxOrient() == HORIZONTAL && !box->style()->isLeftToRightDirection()) forward = box->style()->boxDirection() != BNORMAL; else forward = box->style()->boxDirection() == BNORMAL; @@ -266,12 +266,12 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int /*pageHeight FIXM // bottom margin max values to 0. This way we don't factor in the values // twice when we collapse with our previous vertically adjacent and // following vertically adjacent blocks. - int pos = maxPosMarginBefore(); - int neg = maxNegMarginBefore(); - if (maxPosMarginAfter() > pos) - pos = maxPosMarginAfter(); - if (maxNegMarginAfter() > neg) - neg = maxNegMarginAfter(); + int pos = maxPositiveMarginBefore(); + int neg = maxNegativeMarginBefore(); + if (maxPositiveMarginAfter() > pos) + pos = maxPositiveMarginAfter(); + if (maxNegativeMarginAfter() > neg) + neg = maxNegativeMarginAfter(); setMaxMarginBeforeValues(pos, neg); setMaxMarginAfterValues(0, 0); } @@ -342,8 +342,6 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) bool haveFlex = false; gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); - bool paginated = view()->layoutState()->isPaginated(); - RenderBox* child; RenderBlock::startDelayUpdateScrollInfo(); @@ -374,11 +372,8 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) // Compute the child's vertical margins. child->computeBlockDirectionMargins(this); - if (!child->needsLayout() && paginated && view()->layoutState()->m_pageHeight) { - RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0; - if (childRenderBlock && view()->layoutState()->pageY(child->y()) != childRenderBlock->pageY()) - childRenderBlock->markForPaginationRelayout(); - } + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); // Now do the layout. child->layoutIfNeeded(); @@ -407,7 +402,7 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) } if (!iterator.first() && hasLineIfEmpty()) - setHeight(height() + lineHeight(true, true)); + setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); setHeight(height() + toAdd); @@ -425,7 +420,7 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) if (child->isPositioned()) { child->containingBlock()->insertPositionedObject(child); if (child->style()->hasStaticX()) { - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) child->layer()->setStaticX(xPos); else child->layer()->setStaticX(width() - xPos); } @@ -448,11 +443,8 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) if (oldChildHeight != child->height()) child->setChildNeedsLayout(true, false); - if (!child->needsLayout() && paginated && view()->layoutState()->m_pageHeight) { - RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0; - if (childRenderBlock && view()->layoutState()->pageY(child->y()) != childRenderBlock->pageY()) - childRenderBlock->markForPaginationRelayout(); - } + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); child->layoutIfNeeded(); @@ -583,8 +575,8 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) RenderBlock::finishDelayUpdateScrollInfo(); - if (remainingSpace > 0 && ((style()->direction() == LTR && style()->boxPack() != BSTART) || - (style()->direction() == RTL && style()->boxPack() != BEND))) { + if (remainingSpace > 0 && ((style()->isLeftToRightDirection() && style()->boxPack() != BSTART) + || (!style()->isLeftToRightDirection() && style()->boxPack() != BEND))) { // Children must be repositioned. int offset = 0; if (style()->boxPack() == BJUSTIFY) { @@ -653,7 +645,7 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) { int xPos = borderLeft() + paddingLeft(); int yPos = borderTop() + paddingTop(); - if (style()->direction() == RTL) + if (!style()->isLeftToRightDirection()) xPos = width() - paddingRight() - borderRight(); int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); bool heightSpecified = false; @@ -667,8 +659,6 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) bool haveFlex = false; gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); - bool paginated = view()->layoutState()->isPaginated(); - RenderBox* child; // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of @@ -696,7 +686,7 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) if (child->isPositioned()) { child->containingBlock()->insertPositionedObject(child); if (child->style()->hasStaticX()) { - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) child->layer()->setStaticX(borderLeft()+paddingLeft()); else child->layer()->setStaticX(borderRight()+paddingRight()); @@ -718,11 +708,8 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) // Add in the child's marginTop to our height. setHeight(height() + child->marginTop()); - if (!child->needsLayout() && paginated && view()->layoutState()->m_pageHeight) { - RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0; - if (childRenderBlock && view()->layoutState()->pageY(child->y()) != childRenderBlock->pageY()) - childRenderBlock->markForPaginationRelayout(); - } + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); // Now do a layout. child->layoutIfNeeded(); @@ -735,13 +722,13 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) childX += child->marginLeft() + max(0, (contentWidth() - (child->width() + child->marginLeft() + child->marginRight()))/2); break; case BEND: - if (style()->direction() == RTL) + if (!style()->isLeftToRightDirection()) childX += child->marginLeft(); else childX += contentWidth() - child->marginRight() - child->width(); break; default: // BSTART/BSTRETCH - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) childX += child->marginLeft(); else childX += contentWidth() - child->marginRight() - child->width(); @@ -758,7 +745,7 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) yPos = height(); if (!iterator.first() && hasLineIfEmpty()) - setHeight(height() + lineHeight(true, true)); + setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); setHeight(height() + toAdd); @@ -934,103 +921,102 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) void RenderFlexibleBox::applyLineClamp(FlexBoxIterator& iterator, bool relayoutChildren) { int maxLineCount = 0; - RenderBox* child = iterator.first(); - while (child) { - if (!child->isPositioned()) { - if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) || - (child->style()->height().isAuto() && child->isBlockFlow() && !child->needsLayout())) { - child->setChildNeedsLayout(true, false); - - // Dirty all the positioned objects. - if (child->isRenderBlock()) { - toRenderBlock(child)->markPositionedObjectsForLayout(); - toRenderBlock(child)->clearTruncation(); - } + for (RenderBox* child = iterator.first(); child; child = iterator.next()) { + if (child->isPositioned()) + continue; + + if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) + || (child->style()->height().isAuto() && child->isBlockFlow() && !child->needsLayout())) { + child->setChildNeedsLayout(true, false); + + // Dirty all the positioned objects. + if (child->isRenderBlock()) { + toRenderBlock(child)->markPositionedObjectsForLayout(); + toRenderBlock(child)->clearTruncation(); } - child->layoutIfNeeded(); - if (child->style()->height().isAuto() && child->isBlockFlow()) - maxLineCount = max(maxLineCount, toRenderBlock(child)->lineCount()); } - child = iterator.next(); + child->layoutIfNeeded(); + if (child->style()->height().isAuto() && child->isBlockFlow()) + maxLineCount = max(maxLineCount, toRenderBlock(child)->lineCount()); } - - // Get the # of lines and then alter all block flow children with auto height to use the + + // Get the number of lines and then alter all block flow children with auto height to use the // specified height. We always try to leave room for at least one line. LineClampValue lineClamp = style()->lineClamp(); int numVisibleLines = lineClamp.isPercentage() ? max(1, (maxLineCount + 1) * lineClamp.value() / 100) : lineClamp.value(); - if (numVisibleLines < maxLineCount) { - for (child = iterator.first(); child; child = iterator.next()) { - if (child->isPositioned() || !child->style()->height().isAuto() || !child->isBlockFlow()) - continue; - - RenderBlock* blockChild = toRenderBlock(child); - int lineCount = blockChild->lineCount(); - if (lineCount <= numVisibleLines) - continue; - - int newHeight = blockChild->heightForLineCount(numVisibleLines); - if (newHeight == child->height()) - continue; - - child->setChildNeedsLayout(true, false); - child->setOverrideSize(newHeight); - m_flexingChildren = true; - child->layoutIfNeeded(); - m_flexingChildren = false; - child->setOverrideSize(-1); - - // FIXME: For now don't support RTL. - if (style()->direction() != LTR) - continue; - - // Get the last line - RootInlineBox* lastLine = blockChild->lineAtIndex(lineCount-1); - if (!lastLine) - continue; - - RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines-1); - if (!lastVisibleLine) - continue; - - const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' }; - DEFINE_STATIC_LOCAL(AtomicString, ellipsisAndSpaceStr, (ellipsisAndSpace, 2)); - DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); - const Font& font = style(numVisibleLines == 1)->font(); - - // Get ellipsis width, and if the last child is an anchor, it will go after the ellipsis, so add in a space and the anchor width too - int totalWidth; - InlineBox* anchorBox = lastLine->lastChild(); - if (anchorBox && anchorBox->renderer()->node() && anchorBox->renderer()->node()->isLink()) - totalWidth = anchorBox->logicalWidth() + font.width(TextRun(ellipsisAndSpace, 2)); - else { - anchorBox = 0; - totalWidth = font.width(TextRun(&horizontalEllipsis, 1)); - } - - // See if this width can be accommodated on the last visible line - RenderBlock* destBlock = toRenderBlock(lastVisibleLine->renderer()); - RenderBlock* srcBlock = toRenderBlock(lastLine->renderer()); - - // FIXME: Directions of src/destBlock could be different from our direction and from one another. - if (srcBlock->style()->direction() != LTR) - continue; - if (destBlock->style()->direction() != LTR) - continue; - int ltr = true; - - int blockRightEdge = destBlock->logicalRightOffsetForLine(lastVisibleLine->y(), false); - int blockLeftEdge = destBlock->logicalLeftOffsetForLine(lastVisibleLine->y(), false); - - int blockEdge = ltr ? blockRightEdge : blockLeftEdge; - if (!lastVisibleLine->canAccommodateEllipsis(ltr, blockEdge, - lastVisibleLine->x() + lastVisibleLine->logicalWidth(), - totalWidth)) - continue; - - // Let the truncation code kick in. - lastVisibleLine->placeEllipsis(anchorBox ? ellipsisAndSpaceStr : ellipsisStr, ltr, blockLeftEdge, blockRightEdge, totalWidth, anchorBox); - destBlock->setHasMarkupTruncation(true); + if (numVisibleLines >= maxLineCount) + return; + + for (RenderBox* child = iterator.first(); child; child = iterator.next()) { + if (child->isPositioned() || !child->style()->height().isAuto() || !child->isBlockFlow()) + continue; + + RenderBlock* blockChild = toRenderBlock(child); + int lineCount = blockChild->lineCount(); + if (lineCount <= numVisibleLines) + continue; + + int newHeight = blockChild->heightForLineCount(numVisibleLines); + if (newHeight == child->height()) + continue; + + child->setChildNeedsLayout(true, false); + child->setOverrideSize(newHeight); + m_flexingChildren = true; + child->layoutIfNeeded(); + m_flexingChildren = false; + child->setOverrideSize(-1); + + // FIXME: For now don't support RTL. + if (style()->direction() != LTR) + continue; + + // Get the last line + RootInlineBox* lastLine = blockChild->lineAtIndex(lineCount-1); + if (!lastLine) + continue; + + RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines-1); + if (!lastVisibleLine) + continue; + + const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' }; + DEFINE_STATIC_LOCAL(AtomicString, ellipsisAndSpaceStr, (ellipsisAndSpace, 2)); + DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); + const Font& font = style(numVisibleLines == 1)->font(); + + // Get ellipsis width, and if the last child is an anchor, it will go after the ellipsis, so add in a space and the anchor width too + int totalWidth; + InlineBox* anchorBox = lastLine->lastChild(); + if (anchorBox && anchorBox->renderer()->node() && anchorBox->renderer()->node()->isLink()) + totalWidth = anchorBox->logicalWidth() + font.width(TextRun(ellipsisAndSpace, 2)); + else { + anchorBox = 0; + totalWidth = font.width(TextRun(&horizontalEllipsis, 1)); } + + // See if this width can be accommodated on the last visible line + RenderBlock* destBlock = toRenderBlock(lastVisibleLine->renderer()); + RenderBlock* srcBlock = toRenderBlock(lastLine->renderer()); + + // FIXME: Directions of src/destBlock could be different from our direction and from one another. + if (!srcBlock->style()->isLeftToRightDirection()) + continue; + + bool leftToRight = destBlock->style()->isLeftToRightDirection(); + if (!leftToRight) + continue; + + int blockRightEdge = destBlock->logicalRightOffsetForLine(lastVisibleLine->y(), false); + int blockLeftEdge = destBlock->logicalLeftOffsetForLine(lastVisibleLine->y(), false); + + int blockEdge = leftToRight ? blockRightEdge : blockLeftEdge; + if (!lastVisibleLine->canAccommodateEllipsis(leftToRight, blockEdge, lastVisibleLine->x() + lastVisibleLine->logicalWidth(), totalWidth)) + continue; + + // Let the truncation code kick in. + lastVisibleLine->placeEllipsis(anchorBox ? ellipsisAndSpaceStr : ellipsisStr, leftToRight, blockLeftEdge, blockRightEdge, totalWidth, anchorBox); + destBlock->setHasMarkupTruncation(true); } } diff --git a/WebCore/rendering/RenderForeignObject.cpp b/WebCore/rendering/RenderForeignObject.cpp index 839e963..f9f6988 100644 --- a/WebCore/rendering/RenderForeignObject.cpp +++ b/WebCore/rendering/RenderForeignObject.cpp @@ -41,6 +41,10 @@ RenderForeignObject::RenderForeignObject(SVGForeignObjectElement* node) { } +RenderForeignObject::~RenderForeignObject() +{ +} + void RenderForeignObject::paint(PaintInfo& paintInfo, int, int) { if (paintInfo.context->paintingDisabled()) diff --git a/WebCore/rendering/RenderForeignObject.h b/WebCore/rendering/RenderForeignObject.h index 87423e6..bdf96ce 100644 --- a/WebCore/rendering/RenderForeignObject.h +++ b/WebCore/rendering/RenderForeignObject.h @@ -33,7 +33,8 @@ class SVGForeignObjectElement; class RenderForeignObject : public RenderSVGBlock { public: - RenderForeignObject(SVGForeignObjectElement*); + explicit RenderForeignObject(SVGForeignObjectElement*); + virtual ~RenderForeignObject(); virtual const char* renderName() const { return "RenderForeignObject"; } diff --git a/WebCore/rendering/RenderFrame.h b/WebCore/rendering/RenderFrame.h index bdcaa4c..a7a859f 100644 --- a/WebCore/rendering/RenderFrame.h +++ b/WebCore/rendering/RenderFrame.h @@ -32,7 +32,7 @@ class HTMLFrameElement; class RenderFrame : public RenderFrameBase { public: - RenderFrame(HTMLFrameElement*); + explicit RenderFrame(HTMLFrameElement*); FrameEdgeInfo edgeInfo() const; diff --git a/WebCore/rendering/RenderFrameBase.h b/WebCore/rendering/RenderFrameBase.h index cd3cf0c..4fad560 100644 --- a/WebCore/rendering/RenderFrameBase.h +++ b/WebCore/rendering/RenderFrameBase.h @@ -33,7 +33,7 @@ namespace WebCore { // Base class for RenderFrame and RenderIFrame class RenderFrameBase : public RenderPart { protected: - RenderFrameBase(Element*); + explicit RenderFrameBase(Element*); public: void layoutWithFlattening(bool fixedWidth, bool fixedHeight); diff --git a/WebCore/rendering/RenderHTMLCanvas.h b/WebCore/rendering/RenderHTMLCanvas.h index 473dad5..2230b39 100644 --- a/WebCore/rendering/RenderHTMLCanvas.h +++ b/WebCore/rendering/RenderHTMLCanvas.h @@ -34,7 +34,7 @@ class HTMLCanvasElement; class RenderHTMLCanvas : public RenderReplaced { public: - RenderHTMLCanvas(HTMLCanvasElement*); + explicit RenderHTMLCanvas(HTMLCanvasElement*); virtual bool isCanvas() const { return true; } virtual bool requiresLayer() const; diff --git a/WebCore/rendering/RenderIFrame.h b/WebCore/rendering/RenderIFrame.h index 325e2b3..0bb3182 100644 --- a/WebCore/rendering/RenderIFrame.h +++ b/WebCore/rendering/RenderIFrame.h @@ -32,7 +32,7 @@ namespace WebCore { class RenderIFrame : public RenderFrameBase { public: - RenderIFrame(Element*); + explicit RenderIFrame(Element*); #if USE(ACCELERATED_COMPOSITING) bool requiresAcceleratedCompositing() const; diff --git a/WebCore/rendering/RenderImage.cpp b/WebCore/rendering/RenderImage.cpp index c8e31e6..e6f4dbe 100644 --- a/WebCore/rendering/RenderImage.cpp +++ b/WebCore/rendering/RenderImage.cpp @@ -131,6 +131,9 @@ void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect) if (hasBoxDecorations() || hasMask()) RenderReplaced::imageChanged(newImage, rect); + + if (!m_imageResource) + return; if (newImage != m_imageResource->imagePtr() || !newImage) return; @@ -195,6 +198,9 @@ void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect) void RenderImage::notifyFinished(CachedResource* newImage) { + if (!m_imageResource) + return; + if (documentBeingDestroyed()) return; @@ -327,6 +333,9 @@ void RenderImage::paintFocusRings(PaintInfo& paintInfo, const RenderStyle* style RefPtr<HTMLCollection> areas = mapElement->areas(); unsigned numAreas = areas->length(); + + if (theme()->supportsFocusRing(style)) + return; // The theme draws the focus ring. // FIXME: Clip the paths to the image bounding box. for (unsigned k = 0; k < numAreas; ++k) { @@ -405,9 +414,9 @@ void RenderImage::updateAltText() #endif } -bool RenderImage::isWidthSpecified() const +bool RenderImage::isLogicalWidthSpecified() const { - switch (style()->width().type()) { + switch (style()->logicalWidth().type()) { case Fixed: case Percent: return true; @@ -422,9 +431,9 @@ bool RenderImage::isWidthSpecified() const return false; } -bool RenderImage::isHeightSpecified() const +bool RenderImage::isLogicalHeightSpecified() const { - switch (style()->height().type()) { + switch (style()->logicalHeight().type()) { case Fixed: case Percent: return true; @@ -439,7 +448,7 @@ bool RenderImage::isHeightSpecified() const return false; } -int RenderImage::computeReplacedWidth(bool includeMaxWidth) const +int RenderImage::computeReplacedLogicalWidth(bool includeMaxWidth) const { if (m_imageResource->imageHasRelativeWidth()) if (RenderObject* cb = isPositioned() ? container() : containingBlock()) { @@ -447,19 +456,21 @@ int RenderImage::computeReplacedWidth(bool includeMaxWidth) const m_imageResource->setImageContainerSize(IntSize(toRenderBox(cb)->availableWidth(), toRenderBox(cb)->availableHeight())); } - int width; - if (isWidthSpecified()) - width = computeReplacedWidthUsing(style()->width()); - else if (m_imageResource->usesImageContainerSize()) - width = m_imageResource->imageSize(style()->effectiveZoom()).width(); - else if (m_imageResource->imageHasRelativeWidth()) - width = 0; // If the image is relatively-sized, set the width to 0 until there is a set container size. + int logicalWidth; + if (isLogicalWidthSpecified()) + logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth()); + else if (m_imageResource->usesImageContainerSize()) { + IntSize size = m_imageResource->imageSize(style()->effectiveZoom()); + logicalWidth = style()->isHorizontalWritingMode() ? size.width() : size.height(); + } else if (m_imageResource->imageHasRelativeWidth()) + logicalWidth = 0; // If the image is relatively-sized, set the width to 0 until there is a set container size. else - width = calcAspectRatioWidth(); + logicalWidth = calcAspectRatioLogicalWidth(); - int minW = computeReplacedWidthUsing(style()->minWidth()); - int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : computeReplacedWidthUsing(style()->maxWidth()); + int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); + int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); +<<<<<<< HEAD #ifdef ANDROID_LAYOUT width = max(minW, min(width, maxW)); // in SSR mode, we will fit the image to its container width @@ -472,23 +483,28 @@ int RenderImage::computeReplacedWidth(bool includeMaxWidth) const #else return max(minW, min(width, maxW)); #endif +======= + return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); +>>>>>>> webkit.org at r70209 } -int RenderImage::computeReplacedHeight() const +int RenderImage::computeReplacedLogicalHeight() const { - int height; - if (isHeightSpecified()) - height = computeReplacedHeightUsing(style()->height()); - else if (m_imageResource->usesImageContainerSize()) - height = m_imageResource->imageSize(style()->effectiveZoom()).height(); - else if (m_imageResource->imageHasRelativeHeight()) - height = 0; // If the image is relatively-sized, set the height to 0 until there is a set container size. + int logicalHeight; + if (isLogicalHeightSpecified()) + logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight()); + else if (m_imageResource->usesImageContainerSize()) { + IntSize size = m_imageResource->imageSize(style()->effectiveZoom()); + logicalHeight = style()->isHorizontalWritingMode() ? size.height() : size.width(); + } else if (m_imageResource->imageHasRelativeHeight()) + logicalHeight = 0; // If the image is relatively-sized, set the height to 0 until there is a set container size. else - height = calcAspectRatioHeight(); + logicalHeight = calcAspectRatioLogicalHeight(); - int minH = computeReplacedHeightUsing(style()->minHeight()); - int maxH = style()->maxHeight().isUndefined() ? height : computeReplacedHeightUsing(style()->maxHeight()); + int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); + int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); +<<<<<<< HEAD #ifdef ANDROID_LAYOUT height = max(minH, min(height, maxH)); // in SSR mode, we will fit the image to its container width @@ -511,26 +527,31 @@ int RenderImage::computeReplacedHeight() const #else return max(minH, min(height, maxH)); #endif +======= + return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); +>>>>>>> webkit.org at r70209 } -int RenderImage::calcAspectRatioWidth() const +int RenderImage::calcAspectRatioLogicalWidth() const { - IntSize size = intrinsicSize(); - if (!size.height()) + int intrinsicWidth = intrinsicLogicalWidth(); + int intrinsicHeight = intrinsicLogicalHeight(); + if (!intrinsicHeight) return 0; if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) - return size.width(); // Don't bother scaling. - return RenderBox::computeReplacedHeight() * size.width() / size.height(); + return intrinsicWidth; // Don't bother scaling. + return RenderBox::computeReplacedLogicalHeight() * intrinsicWidth / intrinsicHeight; } -int RenderImage::calcAspectRatioHeight() const +int RenderImage::calcAspectRatioLogicalHeight() const { - IntSize size = intrinsicSize(); - if (!size.width()) + int intrinsicWidth = intrinsicLogicalWidth(); + int intrinsicHeight = intrinsicLogicalHeight(); + if (!intrinsicWidth) return 0; if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) - return size.height(); // Don't bother scaling. - return RenderBox::computeReplacedWidth() * size.height() / size.width(); + return intrinsicHeight; // Don't bother scaling. + return RenderBox::computeReplacedLogicalWidth() * intrinsicHeight / intrinsicWidth; } } // namespace WebCore diff --git a/WebCore/rendering/RenderImage.h b/WebCore/rendering/RenderImage.h index 022d792..308c863 100644 --- a/WebCore/rendering/RenderImage.h +++ b/WebCore/rendering/RenderImage.h @@ -58,8 +58,8 @@ protected: void paintFocusRings(PaintInfo&, const RenderStyle*); virtual void paint(PaintInfo&, int tx, int ty); - bool isWidthSpecified() const; - bool isHeightSpecified() const; + bool isLogicalWidthSpecified() const; + bool isLogicalHeightSpecified() const; virtual void intrinsicSizeChanged() { @@ -80,11 +80,11 @@ private: virtual void notifyFinished(CachedResource*); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); - virtual int computeReplacedWidth(bool includeMaxWidth = true) const; - virtual int computeReplacedHeight() const; + virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual int computeReplacedLogicalHeight() const; - int calcAspectRatioWidth() const; - int calcAspectRatioHeight() const; + int calcAspectRatioLogicalWidth() const; + int calcAspectRatioLogicalHeight() const; private: // Text to display as long as the image isn't available. diff --git a/WebCore/rendering/RenderInline.cpp b/WebCore/rendering/RenderInline.cpp index b4bfe2f..4b28268 100644 --- a/WebCore/rendering/RenderInline.cpp +++ b/WebCore/rendering/RenderInline.cpp @@ -31,6 +31,7 @@ #include "RenderArena.h" #include "RenderBlock.h" #include "RenderLayer.h" +#include "RenderTheme.h" #include "RenderView.h" #include "TransformState.h" #include "VisiblePosition.h" @@ -471,28 +472,28 @@ static int computeMargin(const RenderInline* renderer, const Length& margin) int RenderInline::marginLeft() const { - if (!style()->isVerticalBlockFlow()) + if (!style()->isHorizontalWritingMode()) return 0; return computeMargin(this, style()->marginLeft()); } int RenderInline::marginRight() const { - if (!style()->isVerticalBlockFlow()) + if (!style()->isHorizontalWritingMode()) return 0; return computeMargin(this, style()->marginRight()); } int RenderInline::marginTop() const { - if (style()->isVerticalBlockFlow()) + if (style()->isHorizontalWritingMode()) return 0; return computeMargin(this, style()->marginTop()); } int RenderInline::marginBottom() const { - if (style()->isVerticalBlockFlow()) + if (style()->isHorizontalWritingMode()) return 0; return computeMargin(this, style()->marginBottom()); } @@ -878,7 +879,7 @@ InlineFlowBox* RenderInline::createAndAppendInlineFlowBox() return flowBox; } -int RenderInline::lineHeight(bool firstLine, bool /*isRootLineBox*/) const +int RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const { if (firstLine && document()->usesFirstLineRules()) { RenderStyle* s = style(firstLine); @@ -892,6 +893,12 @@ int RenderInline::lineHeight(bool firstLine, bool /*isRootLineBox*/) const return m_lineHeight; } +int RenderInline::baselinePosition(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const +{ + const Font& f = style(firstLine)->font(); + return f.ascent() + (lineHeight(firstLine, direction, linePositionMode) - f.height()) / 2; +} + int RenderInline::verticalPositionFromCache(bool firstLine) const { if (firstLine) // We're only really a first-line style if the document actually uses first-line rules. @@ -993,15 +1000,10 @@ void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty RenderStyle* styleToUse = style(); if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) { - int ow = styleToUse->outlineWidth(); - Color oc = styleToUse->visitedDependentColor(CSSPropertyOutlineColor); - - Vector<IntRect> focusRingRects; - addFocusRingRects(focusRingRects, tx, ty); - if (styleToUse->outlineStyleIsAuto()) - graphicsContext->drawFocusRing(focusRingRects, ow, styleToUse->outlineOffset(), oc); - else - addPDFURLRect(graphicsContext, unionRect(focusRingRects)); + if (!theme()->supportsFocusRing(styleToUse)) { + // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. + paintFocusRing(graphicsContext, tx, ty, styleToUse); + } } if (styleToUse->outlineStyleIsAuto() || styleToUse->outlineStyle() == BNONE) diff --git a/WebCore/rendering/RenderInline.h b/WebCore/rendering/RenderInline.h index feb8b8f..1eb32ff 100644 --- a/WebCore/rendering/RenderInline.h +++ b/WebCore/rendering/RenderInline.h @@ -32,7 +32,7 @@ class Position; class RenderInline : public RenderBoxModelObject { public: - RenderInline(Node*); + explicit RenderInline(Node*); virtual void destroy(); @@ -133,7 +133,8 @@ private: virtual void dirtyLinesFromChangedChild(RenderObject* child) { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } - virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; + virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + virtual int baselinePosition(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; virtual void childBecameNonInline(RenderObject* child); diff --git a/WebCore/rendering/RenderLayer.cpp b/WebCore/rendering/RenderLayer.cpp index 63233e5..a160381 100644 --- a/WebCore/rendering/RenderLayer.cpp +++ b/WebCore/rendering/RenderLayer.cpp @@ -978,7 +978,7 @@ void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const RenderLayer* p->clip(clipRect); p->beginTransparencyLayer(renderer()->opacity()); #ifdef REVEAL_TRANSPARENCY_LAYERS - p->setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f), DeviceColorSpace); + p->setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f), ColorSpaceDeviceRGB); p->fillRect(clipRect); #endif } @@ -1986,7 +1986,7 @@ void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar) m_scrollDimensionsDirty = false; - bool ltr = renderer()->style()->direction() == LTR; + bool ltr = renderer()->style()->isLeftToRightDirection(); int clientWidth = box->clientWidth(); int clientHeight = box->clientHeight(); @@ -2234,9 +2234,9 @@ void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const I context->clip(absRect); IntRect largerCorner = absRect; largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1)); - context->setStrokeColor(Color(makeRGB(217, 217, 217)), DeviceColorSpace); + context->setStrokeColor(Color(makeRGB(217, 217, 217)), ColorSpaceDeviceRGB); context->setStrokeThickness(1.0f); - context->setFillColor(Color::transparent, DeviceColorSpace); + context->setFillColor(Color::transparent, ColorSpaceDeviceRGB); context->drawRect(largerCorner); context->restore(); } @@ -2811,7 +2811,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont useTemporaryClipRects = compositor()->inCompositingMode(); #endif - IntRect hitTestArea = result.rectFromPoint(hitTestPoint); + IntRect hitTestArea = result.rectForPoint(hitTestPoint); // Apply a transform if we have one. if (transform() && !appliedTransform) { @@ -3094,7 +3094,7 @@ RenderLayer* RenderLayer::hitTestChildLayerColumns(RenderLayer* childLayer, Rend IntRect localClipRect(hitTestRect); localClipRect.intersect(colRect); - if (!localClipRect.isEmpty() && localClipRect.intersects(result.rectFromPoint(hitTestPoint))) { + if (!localClipRect.isEmpty() && localClipRect.intersects(result.rectForPoint(hitTestPoint))) { RenderLayer* hitLayer = 0; if (!columnIndex) { // Apply a translation transform to change where the layer paints. diff --git a/WebCore/rendering/RenderLayerBacking.cpp b/WebCore/rendering/RenderLayerBacking.cpp index 536cdab..52a814c 100644 --- a/WebCore/rendering/RenderLayerBacking.cpp +++ b/WebCore/rendering/RenderLayerBacking.cpp @@ -43,7 +43,7 @@ #include "HTMLIFrameElement.h" #include "HTMLMediaElement.h" #include "HTMLNames.h" -#include "InspectorTimelineAgent.h" +#include "InspectorInstrumentation.h" #include "KeyframeList.h" #include "PluginViewBase.h" #include "RenderBox.h" @@ -1104,26 +1104,10 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* ASSERT(!m_owningLayer->m_usedTransparency); } -#if ENABLE(INSPECTOR) -static InspectorTimelineAgent* inspectorTimelineAgent(RenderObject* renderer) -{ - Frame* frame = renderer->frame(); - if (!frame) - return 0; - Page* page = frame->page(); - if (!page) - return 0; - return page->inspectorTimelineAgent(); -} -#endif - // Up-call from compositing layer drawing callback. void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase paintingPhase, const IntRect& clip) { -#if ENABLE(INSPECTOR) - if (InspectorTimelineAgent* timelineAgent = inspectorTimelineAgent(m_owningLayer->renderer())) - timelineAgent->willPaint(clip); -#endif + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willPaint(m_owningLayer->renderer()->frame(), clip); // We have to use the same root as for hit testing, because both methods // can compute and cache clipRects. @@ -1149,10 +1133,7 @@ void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& co paintIntoLayer(m_owningLayer, &context, dirtyRect, PaintBehaviorNormal, paintingPhase, renderer()); -#if ENABLE(INSPECTOR) - if (InspectorTimelineAgent* timelineAgent = inspectorTimelineAgent(m_owningLayer->renderer())) - timelineAgent->didPaint(); -#endif + InspectorInstrumentation::didPaint(cookie); } bool RenderLayerBacking::showDebugBorders() const diff --git a/WebCore/rendering/RenderLayerCompositor.cpp b/WebCore/rendering/RenderLayerCompositor.cpp index 835d21e..835d21e 100755..100644 --- a/WebCore/rendering/RenderLayerCompositor.cpp +++ b/WebCore/rendering/RenderLayerCompositor.cpp diff --git a/WebCore/rendering/RenderLayerCompositor.h b/WebCore/rendering/RenderLayerCompositor.h index b4e3afe..b4e3afe 100755..100644 --- a/WebCore/rendering/RenderLayerCompositor.h +++ b/WebCore/rendering/RenderLayerCompositor.h diff --git a/WebCore/rendering/RenderLineBoxList.cpp b/WebCore/rendering/RenderLineBoxList.cpp index 760d2ea..1e58e63 100644 --- a/WebCore/rendering/RenderLineBoxList.cpp +++ b/WebCore/rendering/RenderLineBoxList.cpp @@ -203,6 +203,10 @@ void RenderLineBoxList::paint(RenderBoxModelObject* renderer, PaintInfo& paintIn } if (bottomForPaginationCheck - topForPaginationCheck <= v->printRect().height()) { if (ty + bottomForPaginationCheck > v->printRect().bottom()) { + if (RootInlineBox* nextRootBox = curr->root()->nextRootBox()) + bottomForPaginationCheck = min(bottomForPaginationCheck, min(nextRootBox->topVisibleOverflow(), nextRootBox->lineTop())); + } + if (ty + bottomForPaginationCheck > v->printRect().bottom()) { if (ty + topForPaginationCheck < v->truncatedAt()) v->setBestTruncatedAt(ty + topForPaginationCheck, renderer); // If we were able to truncate, don't paint. diff --git a/WebCore/rendering/RenderListBox.cpp b/WebCore/rendering/RenderListBox.cpp index 39442e1..532c551 100644 --- a/WebCore/rendering/RenderListBox.cpp +++ b/WebCore/rendering/RenderListBox.cpp @@ -238,9 +238,9 @@ void RenderListBox::computeLogicalHeight() } } -int RenderListBox::baselinePosition(bool, bool) const +int RenderListBox::baselinePosition(bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const { - return height() + marginTop() + marginBottom() - baselineAdjustment; + return RenderBox::baselinePosition(firstLine, lineDirection, linePositionMode) - baselineAdjustment; } IntRect RenderListBox::itemBoundingBoxRect(int tx, int ty, int index) @@ -334,7 +334,7 @@ void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, in unsigned length = itemText.length(); const UChar* string = itemText.characters(); - TextRun textRun(string, length, 0, 0, 0, itemStyle->direction() == RTL, itemStyle->unicodeBidi() == Override, false, false); + TextRun textRun(string, length, 0, 0, 0, !itemStyle->isLeftToRightDirection(), itemStyle->unicodeBidi() == Override, false, false); // Draw the item text if (itemStyle->visibility() != HIDDEN) diff --git a/WebCore/rendering/RenderListBox.h b/WebCore/rendering/RenderListBox.h index c69f205..072fc91 100644 --- a/WebCore/rendering/RenderListBox.h +++ b/WebCore/rendering/RenderListBox.h @@ -71,7 +71,7 @@ private: virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0); virtual void computePreferredLogicalWidths(); - virtual int baselinePosition(bool firstLine, bool isRootLineBox) const; + virtual int baselinePosition(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; virtual void computeLogicalHeight(); virtual void layout(); diff --git a/WebCore/rendering/RenderListItem.cpp b/WebCore/rendering/RenderListItem.cpp index b414f38..1f600fe 100644 --- a/WebCore/rendering/RenderListItem.cpp +++ b/WebCore/rendering/RenderListItem.cpp @@ -102,7 +102,7 @@ static Node* enclosingList(const RenderListItem* listItem) static RenderListItem* previousListItem(Node* list, const RenderListItem* item) { - for (RenderObject* renderer = item->previousInPreOrder(); renderer != list->renderer(); renderer = renderer->previousInPreOrder()) { + for (RenderObject* renderer = item->previousInPreOrder(); renderer && renderer != list->renderer(); renderer = renderer->previousInPreOrder()) { if (!renderer->isListItem()) continue; Node* otherList = enclosingList(toRenderListItem(renderer)); @@ -261,7 +261,7 @@ void RenderListItem::positionListMarker() // FIXME: Inline flows in the line box hierarchy that have self-painting layers should act as cutoff points // and really shouldn't keep propagating overflow up. This won't really break anything other than repainting // not being as tight as it could be though. - if (style()->direction() == LTR) { + if (style()->isLeftToRightDirection()) { int leftLineOffset = logicalLeftOffsetForLine(yOffset, logicalLeftOffsetForLine(yOffset, false), false); markerXPos = leftLineOffset - xOffset - paddingLeft() - borderLeft() + m_marker->marginLeft(); m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0); @@ -326,12 +326,12 @@ String RenderListItem::markerTextWithSuffix() const const String markerSuffix = m_marker->suffix(); Vector<UChar> resultVector; - if (m_marker->style()->direction() == RTL) + if (!m_marker->style()->isLeftToRightDirection()) resultVector.append(markerSuffix.characters(), markerSuffix.length()); resultVector.append(markerText.characters(), markerText.length()); - if (m_marker->style()->direction() == LTR) + if (m_marker->style()->isLeftToRightDirection()) resultVector.append(markerSuffix.characters(), markerSuffix.length()); return String::adopt(resultVector); diff --git a/WebCore/rendering/RenderListItem.h b/WebCore/rendering/RenderListItem.h index f7bd661..2fcb6c4 100644 --- a/WebCore/rendering/RenderListItem.h +++ b/WebCore/rendering/RenderListItem.h @@ -31,7 +31,7 @@ class RenderListMarker; class RenderListItem : public RenderBlock { public: - RenderListItem(Node*); + explicit RenderListItem(Node*); int value() const { if (!m_isValueUpToDate) updateValueNow(); return m_value; } void updateValue(); diff --git a/WebCore/rendering/RenderListMarker.cpp b/WebCore/rendering/RenderListMarker.cpp index 18811df..ab5da61 100644 --- a/WebCore/rendering/RenderListMarker.cpp +++ b/WebCore/rendering/RenderListMarker.cpp @@ -504,7 +504,6 @@ static UChar listMarkerSuffix(EListStyleType type, int value) case Footnotes: case NoneListStyle: case Square: - ASSERT_NOT_REACHED(); return ' '; case Afar: case Amharic: @@ -1155,7 +1154,7 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) context->drawEllipse(marker); return; case Circle: - context->setFillColor(Color::transparent, DeviceColorSpace); + context->setFillColor(Color::transparent, ColorSpaceDeviceRGB); context->drawEllipse(marker); return; case Square: @@ -1257,7 +1256,7 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) const Font& font = style()->font(); const UChar suffix = listMarkerSuffix(type, m_listItem->value()); - if (style()->direction() == LTR) { + if (style()->isLeftToRightDirection()) { int width = font.width(textRun); context->drawText(style()->font(), textRun, marker.location()); UChar suffixSpace[2] = { suffix, ' ' }; @@ -1444,7 +1443,7 @@ void RenderListMarker::updateMargins() if (isInside()) { if (isImage()) { - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) marginRight = cMarkerPadding; else marginLeft = cMarkerPadding; @@ -1452,7 +1451,7 @@ void RenderListMarker::updateMargins() case Disc: case Circle: case Square: - if (style()->direction() == LTR) { + if (style()->isLeftToRightDirection()) { marginLeft = -1; marginRight = font.ascent() - minPreferredLogicalWidth() + 1; } else { @@ -1464,7 +1463,7 @@ void RenderListMarker::updateMargins() break; } } else { - if (style()->direction() == LTR) { + if (style()->isLeftToRightDirection()) { if (isImage()) marginLeft = -minPreferredLogicalWidth() - cMarkerPadding; else { @@ -1506,20 +1505,18 @@ void RenderListMarker::updateMargins() style()->setMarginRight(Length(marginRight, Fixed)); } -int RenderListMarker::lineHeight(bool, bool) const +int RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const { if (!isImage()) - return m_listItem->lineHeight(false, true); - return height(); + return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes); + return RenderBox::lineHeight(firstLine, direction, linePositionMode); } -int RenderListMarker::baselinePosition(bool, bool) const +int RenderListMarker::baselinePosition(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const { - if (!isImage()) { - const Font& font = style()->font(); - return font.ascent() + (lineHeight(false) - font.height())/2; - } - return height(); + if (!isImage()) + return m_listItem->baselinePosition(firstLine, direction, PositionOfInteriorLineBoxes); + return RenderBox::baselinePosition(firstLine, direction, linePositionMode); } String RenderListMarker::suffix() const @@ -1532,7 +1529,7 @@ String RenderListMarker::suffix() const // If the suffix is not ' ', an extra space is needed if (suffix != ' ') { - if (style()->direction() == LTR) + if (style()->isLeftToRightDirection()) resultVector.append(' '); else resultVector.prepend(' '); diff --git a/WebCore/rendering/RenderListMarker.h b/WebCore/rendering/RenderListMarker.h index 1e55898..fe025c3 100644 --- a/WebCore/rendering/RenderListMarker.h +++ b/WebCore/rendering/RenderListMarker.h @@ -58,8 +58,8 @@ private: virtual InlineBox* createInlineBox(); - virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; - virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; + virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + virtual int baselinePosition(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; bool isImage() const; bool isText() const { return !isImage(); } diff --git a/WebCore/rendering/RenderMarquee.cpp b/WebCore/rendering/RenderMarquee.cpp index bb917f8..90383d8 100644 --- a/WebCore/rendering/RenderMarquee.cpp +++ b/WebCore/rendering/RenderMarquee.cpp @@ -66,6 +66,10 @@ RenderMarquee::RenderMarquee(RenderLayer* l) { } +RenderMarquee::~RenderMarquee() +{ +} + int RenderMarquee::marqueeSpeed() const { int result = m_layer->renderer()->style()->marqueeSpeed(); @@ -110,7 +114,7 @@ int RenderMarquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge ASSERT(box); RenderStyle* s = box->style(); if (isHorizontal()) { - bool ltr = s->direction() == LTR; + bool ltr = s->isLeftToRightDirection(); int clientWidth = box->clientWidth(); int contentWidth = ltr ? box->rightmostPosition(true, false) : box->leftmostPosition(true, false); if (ltr) diff --git a/WebCore/rendering/RenderMarquee.h b/WebCore/rendering/RenderMarquee.h index 1651454..79998ed 100644 --- a/WebCore/rendering/RenderMarquee.h +++ b/WebCore/rendering/RenderMarquee.h @@ -55,7 +55,8 @@ class RenderLayer; // This class handles the auto-scrolling of layers with overflow: marquee. class RenderMarquee : public Noncopyable { public: - RenderMarquee(RenderLayer*); + explicit RenderMarquee(RenderLayer*); + virtual ~RenderMarquee(); int speed() const { return m_speed; } int marqueeSpeed() const; diff --git a/WebCore/rendering/RenderMedia.cpp b/WebCore/rendering/RenderMedia.cpp index a589a2d..49a536c 100644 --- a/WebCore/rendering/RenderMedia.cpp +++ b/WebCore/rendering/RenderMedia.cpp @@ -34,6 +34,7 @@ #include "MediaControlElements.h" #include "MouseEvent.h" #include "Page.h" +#include "RenderLayer.h" #include "RenderTheme.h" #include <wtf/CurrentTime.h> #include <wtf/MathExtras.h> @@ -599,31 +600,80 @@ void RenderMedia::forwardEvent(Event* event) } } -int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) const +int RenderMedia::topmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const { - int bottom = RenderImage::lowestPosition(includeOverflowInterior, includeSelf); + int top = RenderImage::topmostPosition(includeOverflowInterior, includeSelf, applyTransform); + if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) + return top; + + top = min(top, m_controlsShadowRoot->renderBox()->transformedFrameRect().y() + m_controlsShadowRoot->renderBox()->topmostPosition(includeOverflowInterior, includeSelf, applyTransform)); + + if (applyTransform == IncludeTransform && includeSelf && layer() && layer()->hasTransform()) { + int bottom = lowestPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int right = rightmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int left = leftmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + IntRect transformRect = applyLayerTransformToRect(IntRect(left, top, right - left, bottom - top)); + return transformRect.y(); + } + + return top; +} + +int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const +{ + int bottom = RenderImage::lowestPosition(includeOverflowInterior, includeSelf, applyTransform); if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return bottom; - return max(bottom, m_controlsShadowRoot->renderBox()->y() + m_controlsShadowRoot->renderBox()->lowestPosition(includeOverflowInterior, includeSelf)); + bottom = max(bottom, m_controlsShadowRoot->renderBox()->transformedFrameRect().y() + m_controlsShadowRoot->renderBox()->lowestPosition(includeOverflowInterior, includeSelf, applyTransform)); + + if (applyTransform == IncludeTransform && includeSelf && layer() && layer()->hasTransform()) { + int top = topmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int right = rightmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int left = leftmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + IntRect transformRect = applyLayerTransformToRect(IntRect(left, top, right - left, bottom - top)); + return transformRect.height() + transformRect.y(); + } + + return bottom; } -int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const +int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const { - int right = RenderImage::rightmostPosition(includeOverflowInterior, includeSelf); + int right = RenderImage::rightmostPosition(includeOverflowInterior, includeSelf, applyTransform); if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return right; - return max(right, m_controlsShadowRoot->renderBox()->x() + m_controlsShadowRoot->renderBox()->rightmostPosition(includeOverflowInterior, includeSelf)); + right = max(right, m_controlsShadowRoot->renderBox()->transformedFrameRect().x() + m_controlsShadowRoot->renderBox()->rightmostPosition(includeOverflowInterior, includeSelf, applyTransform)); + + if (applyTransform == IncludeTransform && includeSelf && layer() && layer()->hasTransform()) { + int top = rightmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int bottom = lowestPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int left = leftmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + IntRect transformRect = applyLayerTransformToRect(IntRect(left, top, right - left, bottom - top)); + return transformRect.width() + transformRect.x(); + } + + return right; } -int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const +int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const { - int left = RenderImage::leftmostPosition(includeOverflowInterior, includeSelf); + int left = RenderImage::leftmostPosition(includeOverflowInterior, includeSelf, applyTransform); if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return left; - return min(left, m_controlsShadowRoot->renderBox()->x() + m_controlsShadowRoot->renderBox()->leftmostPosition(includeOverflowInterior, includeSelf)); + left = min(left, m_controlsShadowRoot->renderBox()->transformedFrameRect().x() + m_controlsShadowRoot->renderBox()->leftmostPosition(includeOverflowInterior, includeSelf, applyTransform)); + + if (applyTransform == IncludeTransform && includeSelf && layer() && layer()->hasTransform()) { + int top = rightmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int bottom = lowestPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + int right = rightmostPosition(includeOverflowInterior, includeSelf, ExcludeTransform); + IntRect transformRect = applyLayerTransformToRect(IntRect(left, top, right - left, bottom - top)); + return transformRect.x(); + } + + return left; } diff --git a/WebCore/rendering/RenderMedia.h b/WebCore/rendering/RenderMedia.h index 4c980b5..aa725ff 100644 --- a/WebCore/rendering/RenderMedia.h +++ b/WebCore/rendering/RenderMedia.h @@ -85,9 +85,10 @@ private: virtual bool isMedia() const { return true; } virtual bool isImage() const { return false; } - virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; - virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; - virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + virtual int topmostPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; + virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; + virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; + virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true, ApplyTransform = IncludeTransform) const; void createControlsShadowRoot(); void destroyControlsShadowRoot(); diff --git a/WebCore/rendering/RenderMediaControls.h b/WebCore/rendering/RenderMediaControls.h index f05c549..9edeef1 100644 --- a/WebCore/rendering/RenderMediaControls.h +++ b/WebCore/rendering/RenderMediaControls.h @@ -26,6 +26,8 @@ #ifndef RenderMediaControls_h #define RenderMediaControls_h +#if ENABLE(VIDEO) + #include "RenderObject.h" #include "MediaControlElements.h" @@ -41,4 +43,6 @@ public: } // namespace WebCore +#endif // ENABLE(VIDEO) + #endif // RenderMediaControls_h diff --git a/WebCore/rendering/RenderMediaControlsChromium.cpp b/WebCore/rendering/RenderMediaControlsChromium.cpp index ab650da..f938a52 100644 --- a/WebCore/rendering/RenderMediaControlsChromium.cpp +++ b/WebCore/rendering/RenderMediaControlsChromium.cpp @@ -63,7 +63,7 @@ static bool hasSource(const HTMLMediaElement* mediaElement) static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Image* image) { IntRect imageRect = image->rect(); - context->drawImage(image, DeviceColorSpace, rect); + context->drawImage(image, ColorSpaceDeviceRGB, rect); return true; } @@ -120,9 +120,9 @@ static bool paintMediaSlider(RenderObject* object, const PaintInfo& paintInfo, c context->save(); context->setShouldAntialias(true); context->setStrokeStyle(SolidStroke); - context->setStrokeColor(style->visitedDependentColor(CSSPropertyBorderLeftColor), DeviceColorSpace); + context->setStrokeColor(style->visitedDependentColor(CSSPropertyBorderLeftColor), ColorSpaceDeviceRGB); context->setStrokeThickness(style->borderLeftWidth()); - context->setFillColor(style->visitedDependentColor(CSSPropertyBackgroundColor), DeviceColorSpace); + context->setFillColor(style->visitedDependentColor(CSSPropertyBackgroundColor), ColorSpaceDeviceRGB); context->drawRect(rect); context->restore(); @@ -189,13 +189,13 @@ static bool paintMediaVolumeSlider(RenderObject* object, const PaintInfo& paintI GraphicsContext* context = paintInfo.context; Color originalColor = context->strokeColor(); if (originalColor != Color::white) - context->setStrokeColor(Color::white, DeviceColorSpace); + context->setStrokeColor(Color::white, ColorSpaceDeviceRGB); int x = rect.x() + rect.width() / 2; context->drawLine(IntPoint(x, rect.y()), IntPoint(x, rect.y() + rect.height())); if (originalColor != Color::white) - context->setStrokeColor(originalColor, DeviceColorSpace); + context->setStrokeColor(originalColor, ColorSpaceDeviceRGB); return true; } @@ -224,17 +224,17 @@ static bool paintMediaTimelineContainer(RenderObject* object, const PaintInfo& p // Draw the left border using CSS defined width and color. context->setStrokeThickness(object->style()->borderLeftWidth()); - context->setStrokeColor(object->style()->visitedDependentColor(CSSPropertyBorderLeftColor).rgb(), DeviceColorSpace); + context->setStrokeColor(object->style()->visitedDependentColor(CSSPropertyBorderLeftColor).rgb(), ColorSpaceDeviceRGB); context->drawLine(IntPoint(rect.x() + 1, rect.y()), IntPoint(rect.x() + 1, rect.y() + rect.height())); // Draw the right border using CSS defined width and color. context->setStrokeThickness(object->style()->borderRightWidth()); - context->setStrokeColor(object->style()->visitedDependentColor(CSSPropertyBorderRightColor).rgb(), DeviceColorSpace); + context->setStrokeColor(object->style()->visitedDependentColor(CSSPropertyBorderRightColor).rgb(), ColorSpaceDeviceRGB); context->drawLine(IntPoint(rect.x() + rect.width() - 1, rect.y()), IntPoint(rect.x() + rect.width() - 1, rect.y() + rect.height())); - context->setStrokeColor(originalColor, DeviceColorSpace); + context->setStrokeColor(originalColor, ColorSpaceDeviceRGB); context->setStrokeThickness(originalThickness); context->setStrokeStyle(originalStyle); } diff --git a/WebCore/rendering/RenderMeter.cpp b/WebCore/rendering/RenderMeter.cpp index 226d605..f60e350 100644 --- a/WebCore/rendering/RenderMeter.cpp +++ b/WebCore/rendering/RenderMeter.cpp @@ -105,7 +105,7 @@ IntRect RenderMeter::valuePartRect() const if (rect.height() <= rect.width()) { int width = static_cast<int>(rect.width()*valueRatio()); - if (style()->direction() == RTL) { + if (!style()->isLeftToRightDirection()) { rect.setX(rect.x() + (rect.width() - width)); rect.setWidth(width); } else diff --git a/WebCore/rendering/RenderObject.cpp b/WebCore/rendering/RenderObject.cpp index eb75ce2..9d2895f 100644 --- a/WebCore/rendering/RenderObject.cpp +++ b/WebCore/rendering/RenderObject.cpp @@ -1154,6 +1154,16 @@ void RenderObject::drawArcForBoxSide(GraphicsContext* graphicsContext, int x, in } } #endif + +void RenderObject::paintFocusRing(GraphicsContext* context, int tx, int ty, RenderStyle* style) +{ + Vector<IntRect> focusRingRects; + addFocusRingRects(focusRingRects, tx, ty); + if (style->outlineStyleIsAuto()) + context->drawFocusRing(focusRingRects, style->outlineWidth(), style->outlineOffset(), style->visitedDependentColor(CSSPropertyOutlineColor)); + else + addPDFURLRect(context, unionRect(focusRingRects)); +} void RenderObject::addPDFURLRect(GraphicsContext* context, const IntRect& rect) { @@ -1184,12 +1194,7 @@ void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty 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. - Vector<IntRect> focusRingRects; - addFocusRingRects(focusRingRects, tx, ty); - if (styleToUse->outlineStyleIsAuto()) - graphicsContext->drawFocusRing(focusRingRects, ow, offset, oc); - else - addPDFURLRect(graphicsContext, unionRect(focusRingRects)); + paintFocusRing(graphicsContext, tx, ty, styleToUse); } } @@ -2227,7 +2232,7 @@ void RenderObject::updateDragState(bool dragOn) { bool valueChanged = (dragOn != m_isDragging); m_isDragging = dragOn; - if (valueChanged && style()->affectedByDragRules()) + if (valueChanged && style()->affectedByDragRules() && node()) node()->setNeedsStyleRecalc(); for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) curr->updateDragState(dragOn); @@ -2275,17 +2280,6 @@ bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int /*x*/, return false; } -int RenderObject::lineHeight(bool firstLine, bool /*isRootLineBox*/) const -{ - return style(firstLine)->computedLineHeight(); -} - -int RenderObject::baselinePosition(bool firstLine, bool isRootLineBox) const -{ - const Font& f = style(firstLine)->font(); - return f.ascent() + (lineHeight(firstLine, isRootLineBox) - f.height()) / 2; -} - void RenderObject::scheduleRelayout() { if (isRenderView()) { diff --git a/WebCore/rendering/RenderObject.h b/WebCore/rendering/RenderObject.h index 70b0da5..9c29ce1 100644 --- a/WebCore/rendering/RenderObject.h +++ b/WebCore/rendering/RenderObject.h @@ -291,7 +291,9 @@ public: bool isHTMLMarquee() const; + inline bool isBeforeContent() const; inline bool isAfterContent() const; + static inline bool isBeforeContent(const RenderObject* obj) { return obj && obj->isBeforeContent(); } static inline bool isAfterContent(const RenderObject* obj) { return obj && obj->isAfterContent(); } bool childrenInline() const { return m_childrenInline; } @@ -314,8 +316,9 @@ public: virtual bool isSVGContainer() const { return false; } virtual bool isSVGGradientStop() const { return false; } virtual bool isSVGHiddenContainer() const { return false; } - virtual bool isRenderPath() const { return false; } + virtual bool isSVGPath() const { return false; } virtual bool isSVGText() const { return false; } + virtual bool isSVGTextPath() const { return false; } virtual bool isSVGInline() const { return false; } virtual bool isSVGInlineText() const { return false; } virtual bool isSVGImage() const { return false; } @@ -482,11 +485,6 @@ public: void updateFillImages(const FillLayer*, const FillLayer*); void updateImage(StyleImage*, StyleImage*); - // for discussion of lineHeight see CSS2 spec - virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; - // for the vertical-align property of inline elements - // the offset of baseline from the top of the object. - virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; virtual void paint(PaintInfo&, int tx, int ty); // Recursive function that computes the size and position of this object and all its descendants. @@ -748,6 +746,7 @@ protected: // Overrides should call the superclass at the start virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + void paintFocusRing(GraphicsContext*, int tx, int ty, RenderStyle*); void paintOutline(GraphicsContext*, int tx, int ty, int w, int h); void addPDFURLRect(GraphicsContext*, const IntRect&); @@ -860,6 +859,16 @@ inline bool RenderObject::documentBeingDestroyed() const return !document()->renderer(); } +inline bool RenderObject::isBeforeContent() const +{ + if (style()->styleType() != BEFORE) + return false; + // Text nodes don't have their own styles, so ignore the style on a text node. + if (isText() && !isBR()) + return false; + return true; +} + inline bool RenderObject::isAfterContent() const { if (style()->styleType() != AFTER) diff --git a/WebCore/rendering/RenderObjectChildList.cpp b/WebCore/rendering/RenderObjectChildList.cpp index 96ec800..c7c8e44 100644 --- a/WebCore/rendering/RenderObjectChildList.cpp +++ b/WebCore/rendering/RenderObjectChildList.cpp @@ -461,7 +461,10 @@ void RenderObjectChildList::updateBeforeAfterContent(RenderObject* owner, Pseudo generatedContentContainer->setStyle(pseudoElementStyle); owner->addChild(generatedContentContainer, insertBefore); } - generatedContentContainer->addChild(renderer); + if (generatedContentContainer->isChildAllowed(renderer, pseudoElementStyle)) + generatedContentContainer->addChild(renderer); + else + renderer->destroy(); } } } diff --git a/WebCore/rendering/RenderProgress.cpp b/WebCore/rendering/RenderProgress.cpp index d6e2dc7..d2e9424 100644 --- a/WebCore/rendering/RenderProgress.cpp +++ b/WebCore/rendering/RenderProgress.cpp @@ -137,7 +137,7 @@ void RenderProgress::updateAnimationState() IntRect RenderProgress::valuePartRect() const { IntRect rect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), lround((width() - borderLeft() - paddingLeft() - borderRight() - paddingRight()) * position()), height() - borderTop() - paddingTop() - borderBottom() - paddingBottom()); - if (style()->direction() == RTL) + if (!style()->isLeftToRightDirection()) rect.setX(width() - borderRight() - paddingRight() - rect.width()); return rect; } diff --git a/WebCore/rendering/RenderReplaced.cpp b/WebCore/rendering/RenderReplaced.cpp index d3b449c..a087038 100644 --- a/WebCore/rendering/RenderReplaced.cpp +++ b/WebCore/rendering/RenderReplaced.cpp @@ -57,13 +57,6 @@ RenderReplaced::~RenderReplaced() { } -void RenderReplaced::setStyle(PassRefPtr<RenderStyle> newStyle) -{ - if (newStyle->blockFlow() != TopToBottomBlockFlow) - newStyle->setBlockFlow(TopToBottomBlockFlow); - RenderBox::setStyle(newStyle); -} - void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBox::styleDidChange(diff, oldStyle); @@ -88,8 +81,7 @@ void RenderReplaced::layout() m_overflow.clear(); addShadowOverflow(); - repainter.repaintAfterLayout(); - + repainter.repaintAfterLayout(); setNeedsLayout(false); } @@ -207,54 +199,54 @@ static inline bool lengthIsSpecified(Length length) return lengthType == Fixed || lengthType == Percent; } -int RenderReplaced::computeReplacedWidth(bool includeMaxWidth) const +int RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) const { - int width; + int logicalWidth; if (lengthIsSpecified(style()->width())) - width = computeReplacedWidthUsing(style()->width()); + logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth()); else if (m_hasIntrinsicSize) - width = calcAspectRatioWidth(); + logicalWidth = calcAspectRatioLogicalWidth(); else - width = intrinsicSize().width(); + logicalWidth = intrinsicLogicalWidth(); - int minW = computeReplacedWidthUsing(style()->minWidth()); - int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : computeReplacedWidthUsing(style()->maxWidth()); + int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); + int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); - return max(minW, min(width, maxW)); + return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); } -int RenderReplaced::computeReplacedHeight() const +int RenderReplaced::computeReplacedLogicalHeight() const { - int height; - if (lengthIsSpecified(style()->height())) - height = computeReplacedHeightUsing(style()->height()); + int logicalHeight; + if (lengthIsSpecified(style()->logicalHeight())) + logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight()); else if (m_hasIntrinsicSize) - height = calcAspectRatioHeight(); + logicalHeight = calcAspectRatioLogicalHeight(); else - height = intrinsicSize().height(); + logicalHeight = intrinsicLogicalHeight(); - int minH = computeReplacedHeightUsing(style()->minHeight()); - int maxH = style()->maxHeight().isUndefined() ? height : computeReplacedHeightUsing(style()->maxHeight()); + int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); + int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); - return max(minH, min(height, maxH)); + return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); } -int RenderReplaced::calcAspectRatioWidth() const +int RenderReplaced::calcAspectRatioLogicalWidth() const { - int intrinsicWidth = intrinsicSize().width(); - int intrinsicHeight = intrinsicSize().height(); + int intrinsicWidth = intrinsicLogicalWidth(); + int intrinsicHeight = intrinsicLogicalHeight(); if (!intrinsicHeight) return 0; - return RenderBox::computeReplacedHeight() * intrinsicWidth / intrinsicHeight; + return RenderBox::computeReplacedLogicalHeight() * intrinsicWidth / intrinsicHeight; } -int RenderReplaced::calcAspectRatioHeight() const +int RenderReplaced::calcAspectRatioLogicalHeight() const { - int intrinsicWidth = intrinsicSize().width(); - int intrinsicHeight = intrinsicSize().height(); + int intrinsicWidth = intrinsicLogicalWidth(); + int intrinsicHeight = intrinsicLogicalHeight(); if (!intrinsicWidth) return 0; - return RenderBox::computeReplacedWidth() * intrinsicHeight / intrinsicWidth; + return RenderBox::computeReplacedLogicalWidth() * intrinsicHeight / intrinsicWidth; } void RenderReplaced::computePreferredLogicalWidths() @@ -262,7 +254,7 @@ void RenderReplaced::computePreferredLogicalWidths() ASSERT(preferredLogicalWidthsDirty()); int borderAndPadding = borderAndPaddingWidth(); - m_maxPreferredLogicalWidth = computeReplacedWidth(false) + borderAndPadding; + m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(false) + borderAndPadding; if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0)); @@ -277,16 +269,6 @@ void RenderReplaced::computePreferredLogicalWidths() setPreferredLogicalWidthsDirty(false); } -int RenderReplaced::lineHeight(bool, bool) const -{ - return height() + marginTop() + marginBottom(); -} - -int RenderReplaced::baselinePosition(bool, bool) const -{ - return height() + marginTop() + marginBottom(); -} - unsigned RenderReplaced::caretMaxRenderedOffset() const { return 1; diff --git a/WebCore/rendering/RenderReplaced.h b/WebCore/rendering/RenderReplaced.h index 872f6d0..fbc5151 100644 --- a/WebCore/rendering/RenderReplaced.h +++ b/WebCore/rendering/RenderReplaced.h @@ -32,15 +32,13 @@ public: RenderReplaced(Node*, const IntSize& intrinsicSize); virtual ~RenderReplaced(); - virtual void setStyle(PassRefPtr<RenderStyle>); - protected: virtual void layout(); virtual IntSize intrinsicSize() const; - virtual int computeReplacedWidth(bool includeMaxWidth = true) const; - virtual int computeReplacedHeight() const; + virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual int computeReplacedLogicalHeight() const; virtual int minimumReplacedHeight() const { return 0; } virtual void setSelectionState(SelectionState); @@ -62,13 +60,10 @@ private: virtual bool canHaveChildren() const { return false; } - virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; - virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; - virtual void computePreferredLogicalWidths(); - int calcAspectRatioWidth() const; - int calcAspectRatioHeight() const; + int calcAspectRatioLogicalWidth() const; + int calcAspectRatioLogicalHeight() const; virtual void paintReplaced(PaintInfo&, int /*tx*/, int /*ty*/) { } diff --git a/WebCore/rendering/RenderRuby.cpp b/WebCore/rendering/RenderRuby.cpp index 2f543d5..7e19a79 100644 --- a/WebCore/rendering/RenderRuby.cpp +++ b/WebCore/rendering/RenderRuby.cpp @@ -44,8 +44,8 @@ static RenderRubyRun* lastRubyRun(const RenderObject* ruby) RenderObject* child = ruby->lastChild(); if (child && ruby->isAfterContent(child)) child = child->previousSibling(); - ASSERT(!child || child->isRubyRun()); - return static_cast<RenderRubyRun*>(child); + ASSERT(!child || child->isRubyRun() || child->isBeforeContent()); + return child && child->isRubyRun() ? static_cast<RenderRubyRun*>(child) : 0; } static inline RenderRubyRun* findRubyRunParent(RenderObject* child) @@ -75,15 +75,25 @@ bool RenderRubyAsInline::isChildAllowed(RenderObject* child, RenderStyle*) const void RenderRubyAsInline::addChild(RenderObject* child, RenderObject* beforeChild) { - // Note: ':after' content is handled implicitly below + // Insert :before and :after content outside of ruby runs. + if (child->isBeforeContent() || child->isAfterContent()) { + RenderInline::addChild(child, beforeChild); + return; + } + // If the child is a ruby run, just add it normally. if (child->isRubyRun()) { RenderInline::addChild(child, beforeChild); return; } - if (beforeChild && !isAfterContent(beforeChild) && !beforeChild->isRubyRun()) { - if (RenderRubyRun* run = findRubyRunParent(beforeChild)) { + if (beforeChild && !isAfterContent(beforeChild)) { + // insert child into run + ASSERT(!beforeChild->isRubyRun()); + RenderObject* run = beforeChild; + while (run && !run->isRubyRun()) + run = run->parent(); + if (run) { run->addChild(child, beforeChild); return; } @@ -104,15 +114,15 @@ void RenderRubyAsInline::addChild(RenderObject* child, RenderObject* beforeChild void RenderRubyAsInline::removeChild(RenderObject* child) { - // If the child's parent is *this, i.e. a ruby run or ':after' content, + // If the child's parent is *this (a ruby run or :before or :after content), // just use the normal remove method. - if (child->parent() == this) { - ASSERT(child->isRubyRun() || child->isAfterContent()); + if (child->isRubyRun() || child->isBeforeContent() || child->isAfterContent()) { RenderInline::removeChild(child); return; } - // Find the containing run + // Otherwise find the containing run and remove it from there. + ASSERT(child->parent() != this); RenderRubyRun* run = findRubyRunParent(child); ASSERT(run); run->removeChild(child); @@ -139,9 +149,13 @@ bool RenderRubyAsBlock::isChildAllowed(RenderObject* child, RenderStyle*) const void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild) { - // Note: ':after' content is handled implicitely below + // Insert :before and :after content outside of ruby runs. + if (child->isBeforeContent() || child->isAfterContent()) { + RenderBlock::addChild(child, beforeChild); + return; + } - // if child is a ruby run, just add it normally + // If the child is a ruby run, just add it normally. if (child->isRubyRun()) { RenderBlock::addChild(child, beforeChild); return; @@ -157,7 +171,7 @@ void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild) run->addChild(child, beforeChild); return; } - ASSERT(false); // beforeChild should always have a run as parent! + ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent! // Emergency fallback: fall through and just append. } @@ -174,14 +188,15 @@ void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild) void RenderRubyAsBlock::removeChild(RenderObject* child) { - // If the child's parent is *this, just use the normal remove method. - if (child->parent() == this) { - // This should happen only during destruction of the whole ruby element, though. + // If the child's parent is *this (a ruby run or :before or :after content), + // just use the normal remove method. + if (child->isRubyRun() || child->isBeforeContent() || child->isAfterContent()) { RenderBlock::removeChild(child); return; } - // Find the containing run + // Otherwise find the containing run and remove it from there. + ASSERT(child->parent() != this); RenderRubyRun* run = findRubyRunParent(child); ASSERT(run); run->removeChild(child); diff --git a/WebCore/rendering/RenderSVGAllInOne.cpp b/WebCore/rendering/RenderSVGAllInOne.cpp index 9c30d88..7002747 100644 --- a/WebCore/rendering/RenderSVGAllInOne.cpp +++ b/WebCore/rendering/RenderSVGAllInOne.cpp @@ -30,8 +30,6 @@ #include "RenderSVGGradientStop.cpp" #include "RenderSVGHiddenContainer.cpp" #include "RenderSVGImage.cpp" -#include "RenderSVGInline.cpp" -#include "RenderSVGInlineText.cpp" #include "RenderSVGModelObject.cpp" #include "RenderSVGResource.cpp" #include "RenderSVGResourceClipper.cpp" @@ -47,24 +45,32 @@ #include "RenderSVGResourceSolidColor.cpp" #include "RenderSVGRoot.cpp" #include "RenderSVGShadowTreeRootContainer.cpp" -#include "RenderSVGTSpan.cpp" -#include "RenderSVGText.cpp" -#include "RenderSVGTextPath.cpp" #include "RenderSVGTransformableContainer.cpp" #include "RenderSVGViewportContainer.cpp" -#include "SVGCharacterData.cpp" -#include "SVGCharacterLayoutInfo.cpp" #include "SVGImageBufferTools.cpp" -#include "SVGInlineFlowBox.cpp" -#include "SVGInlineTextBox.cpp" #include "SVGMarkerLayoutInfo.cpp" #include "SVGRenderSupport.cpp" #include "SVGRenderTreeAsText.cpp" #include "SVGResources.cpp" #include "SVGResourcesCache.cpp" #include "SVGResourcesCycleSolver.cpp" -#include "SVGRootInlineBox.cpp" #include "SVGShadowTreeElements.cpp" -#include "SVGTextChunkLayoutInfo.cpp" -#include "SVGTextLayoutUtilities.cpp" -#include "SVGTextQuery.cpp" + +// FIXME: As soon as all SVG renderers live in rendering/svg, this file should be moved there as well, removing the need for the svg/ includes below. +#include "svg/RenderSVGInline.cpp" +#include "svg/RenderSVGInlineText.cpp" +#include "svg/RenderSVGTSpan.cpp" +#include "svg/RenderSVGText.cpp" +#include "svg/RenderSVGTextPath.cpp" +#include "svg/SVGInlineFlowBox.cpp" +#include "svg/SVGInlineTextBox.cpp" +#include "svg/SVGRootInlineBox.cpp" +#include "svg/SVGTextChunk.cpp" +#include "svg/SVGTextChunkBuilder.cpp" +#include "svg/SVGTextLayoutAttributes.cpp" +#include "svg/SVGTextLayoutAttributesBuilder.cpp" +#include "svg/SVGTextLayoutEngine.cpp" +#include "svg/SVGTextLayoutEngineBaseline.cpp" +#include "svg/SVGTextLayoutEngineSpacing.cpp" +#include "svg/SVGTextMetrics.cpp" +#include "svg/SVGTextQuery.cpp" diff --git a/WebCore/rendering/RenderSVGBlock.h b/WebCore/rendering/RenderSVGBlock.h index c1379da..a9dd5db 100644 --- a/WebCore/rendering/RenderSVGBlock.h +++ b/WebCore/rendering/RenderSVGBlock.h @@ -31,7 +31,7 @@ class SVGElement; class RenderSVGBlock : public RenderBlock { public: - RenderSVGBlock(SVGElement*); + explicit RenderSVGBlock(SVGElement*); private: virtual void setStyle(PassRefPtr<RenderStyle>); diff --git a/WebCore/rendering/RenderSVGContainer.cpp b/WebCore/rendering/RenderSVGContainer.cpp index b5974ca..0761562 100644 --- a/WebCore/rendering/RenderSVGContainer.cpp +++ b/WebCore/rendering/RenderSVGContainer.cpp @@ -43,6 +43,10 @@ RenderSVGContainer::RenderSVGContainer(SVGStyledElement* node) { } +RenderSVGContainer::~RenderSVGContainer() +{ +} + void RenderSVGContainer::layout() { ASSERT(needsLayout()); diff --git a/WebCore/rendering/RenderSVGContainer.h b/WebCore/rendering/RenderSVGContainer.h index b0c952f..5bdbf32 100644 --- a/WebCore/rendering/RenderSVGContainer.h +++ b/WebCore/rendering/RenderSVGContainer.h @@ -33,7 +33,8 @@ class SVGElement; class RenderSVGContainer : public RenderSVGModelObject { public: - RenderSVGContainer(SVGStyledElement*); + explicit RenderSVGContainer(SVGStyledElement*); + virtual ~RenderSVGContainer(); const RenderObjectChildList* children() const { return &m_children; } RenderObjectChildList* children() { return &m_children; } diff --git a/WebCore/rendering/RenderSVGGradientStop.cpp b/WebCore/rendering/RenderSVGGradientStop.cpp index ebf7385..a5e5808 100644 --- a/WebCore/rendering/RenderSVGGradientStop.cpp +++ b/WebCore/rendering/RenderSVGGradientStop.cpp @@ -72,7 +72,7 @@ void RenderSVGGradientStop::layout() SVGGradientElement* RenderSVGGradientStop::gradientElement() const { - Node* parentNode = node()->parent(); + ContainerNode* parentNode = node()->parent(); if (parentNode->hasTagName(linearGradientTag) || parentNode->hasTagName(radialGradientTag)) return static_cast<SVGGradientElement*>(parentNode); return 0; diff --git a/WebCore/rendering/RenderSVGHiddenContainer.cpp b/WebCore/rendering/RenderSVGHiddenContainer.cpp index ee1d214..fb14ffe 100644 --- a/WebCore/rendering/RenderSVGHiddenContainer.cpp +++ b/WebCore/rendering/RenderSVGHiddenContainer.cpp @@ -25,7 +25,7 @@ #if ENABLE(SVG) #include "RenderSVGHiddenContainer.h" -#include "RenderPath.h" +#include "RenderSVGPath.h" #include "SVGStyledElement.h" namespace WebCore { diff --git a/WebCore/rendering/RenderSVGHiddenContainer.h b/WebCore/rendering/RenderSVGHiddenContainer.h index 97800d4..c591a88 100644 --- a/WebCore/rendering/RenderSVGHiddenContainer.h +++ b/WebCore/rendering/RenderSVGHiddenContainer.h @@ -34,7 +34,7 @@ namespace WebCore { // <defs>, <linearGradient>, <radialGradient> are all good examples class RenderSVGHiddenContainer : public RenderSVGContainer { public: - RenderSVGHiddenContainer(SVGStyledElement*); + explicit RenderSVGHiddenContainer(SVGStyledElement*); virtual const char* renderName() const { return "RenderSVGHiddenContainer"; } diff --git a/WebCore/rendering/RenderSVGImage.cpp b/WebCore/rendering/RenderSVGImage.cpp index a89a738..a569759 100644 --- a/WebCore/rendering/RenderSVGImage.cpp +++ b/WebCore/rendering/RenderSVGImage.cpp @@ -47,6 +47,7 @@ namespace WebCore { RenderSVGImage::RenderSVGImage(SVGImageElement* impl) : RenderSVGModelObject(impl) + , m_updateCachedRepaintRect(true) , m_needsTransformUpdate(true) , m_imageResource(RenderImageResource::create()) { @@ -62,36 +63,45 @@ void RenderSVGImage::layout() { ASSERT(needsLayout()); - LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); + LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout()); SVGImageElement* image = static_cast<SVGImageElement*>(node()); - bool updateCachedBoundariesInParents = false; + bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_updateCachedRepaintRect; if (m_needsTransformUpdate) { m_localTransform = image->animatedLocalTransform(); m_needsTransformUpdate = false; - updateCachedBoundariesInParents = true; } - // FIXME: Optimize caching the repaint rects. - FloatRect oldBoundaries = m_localBounds; - m_localBounds = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image)); - m_cachedLocalRepaintRect = FloatRect(); - - if (!updateCachedBoundariesInParents) - updateCachedBoundariesInParents = oldBoundaries != m_localBounds; + if (m_updateCachedRepaintRect) { + m_repaintBoundingBox = m_objectBoundingBox; + SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox); + m_updateCachedRepaintRect = false; + } // Invalidate all resources of this client if our layout changed. if (m_everHadLayout && selfNeedsLayout()) SVGResourcesCache::clientLayoutChanged(this); // If our bounds changed, notify the parents. - if (updateCachedBoundariesInParents) + if (transformOrBoundariesUpdate) RenderSVGModelObject::setNeedsBoundariesUpdate(); repainter.repaintAfterLayout(); setNeedsLayout(false); } +void RenderSVGImage::updateFromElement() +{ + SVGImageElement* image = static_cast<SVGImageElement*>(node()); + + FloatRect oldBoundaries = m_objectBoundingBox; + m_objectBoundingBox = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image)); + if (m_objectBoundingBox != oldBoundaries) { + m_updateCachedRepaintRect = true; + setNeedsLayout(true); + } +} + void RenderSVGImage::paint(PaintInfo& paintInfo, int, int) { if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || !m_imageResource->hasImage()) @@ -112,14 +122,14 @@ void RenderSVGImage::paint(PaintInfo& paintInfo, int, int) if (SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo)) { Image* image = m_imageResource->image(); - FloatRect destRect = m_localBounds; + FloatRect destRect = m_objectBoundingBox; FloatRect srcRect(0, 0, image->width(), image->height()); SVGImageElement* imageElement = static_cast<SVGImageElement*>(node()); if (imageElement->preserveAspectRatio().align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) imageElement->preserveAspectRatio().transformRect(destRect, srcRect); - childPaintInfo.context->drawImage(image, DeviceColorSpace, destRect, srcRect); + childPaintInfo.context->drawImage(image, ColorSpaceDeviceRGB, destRect, srcRect); } SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, savedInfo.context); @@ -148,7 +158,7 @@ bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResu return false; if (hitRules.canHitFill) { - if (m_localBounds.contains(localPoint)) { + if (m_objectBoundingBox.contains(localPoint)) { updateHitTestResult(result, roundedIntPoint(localPoint)); return true; } @@ -158,18 +168,6 @@ bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResu return false; } -FloatRect RenderSVGImage::repaintRectInLocalCoordinates() const -{ - // If we already have a cached repaint rect, return that - if (!m_cachedLocalRepaintRect.isEmpty()) - return m_cachedLocalRepaintRect; - - m_cachedLocalRepaintRect = m_localBounds; - SVGRenderSupport::intersectRepaintRectWithResources(this, m_cachedLocalRepaintRect); - - return m_cachedLocalRepaintRect; -} - void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*) { // The image resource defaults to nullImage until the resource arrives. diff --git a/WebCore/rendering/RenderSVGImage.h b/WebCore/rendering/RenderSVGImage.h index da94568..485d6ab 100644 --- a/WebCore/rendering/RenderSVGImage.h +++ b/WebCore/rendering/RenderSVGImage.h @@ -42,6 +42,7 @@ public: virtual ~RenderSVGImage(); virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } + virtual void updateFromElement(); RenderImageResource* imageResource() { return m_imageResource.get(); } const RenderImageResource* imageResource() const { return m_imageResource.get(); } @@ -52,9 +53,9 @@ private: virtual const AffineTransform& localToParentTransform() const { return m_localTransform; } - virtual FloatRect objectBoundingBox() const { return m_localBounds; } - virtual FloatRect strokeBoundingBox() const { return m_localBounds; } - virtual FloatRect repaintRectInLocalCoordinates() const; + virtual FloatRect objectBoundingBox() const { return m_objectBoundingBox; } + virtual FloatRect strokeBoundingBox() const { return m_objectBoundingBox; } + virtual FloatRect repaintRectInLocalCoordinates() const { return m_repaintBoundingBox; } virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); @@ -69,10 +70,11 @@ private: virtual AffineTransform localTransform() const { return m_localTransform; } + bool m_updateCachedRepaintRect : 1; bool m_needsTransformUpdate : 1; AffineTransform m_localTransform; - FloatRect m_localBounds; - mutable FloatRect m_cachedLocalRepaintRect; + FloatRect m_objectBoundingBox; + FloatRect m_repaintBoundingBox; OwnPtr<RenderImageResource> m_imageResource; }; diff --git a/WebCore/rendering/RenderSVGInlineText.cpp b/WebCore/rendering/RenderSVGInlineText.cpp deleted file mode 100644 index f5ea5fc..0000000 --- a/WebCore/rendering/RenderSVGInlineText.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of the WebKit project. - * - * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> - * (C) 2006 Apple Computer Inc. - * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * (C) 2008 Rob Buis <buis@kde.org> - * - * 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" - -#if ENABLE(SVG) -#include "RenderSVGInlineText.h" - -#include "FloatConversion.h" -#include "FloatQuad.h" -#include "RenderBlock.h" -#include "RenderSVGRoot.h" -#include "SVGInlineTextBox.h" -#include "SVGRootInlineBox.h" -#include "VisiblePosition.h" - -namespace WebCore { - -RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> str) - : RenderText(n, str) -{ -} - -void RenderSVGInlineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) -{ - RenderText::styleDidChange(diff, oldStyle); - - // FIXME: Should optimize this. - // SVG text is always transformed. - if (RefPtr<StringImpl> textToTransform = originalText()) - setText(textToTransform.release(), true); -} - -InlineTextBox* RenderSVGInlineText::createTextBox() -{ - InlineTextBox* box = new (renderArena()) SVGInlineTextBox(this); - box->setHasVirtualLogicalHeight(); - return box; -} - -IntRect RenderSVGInlineText::localCaretRect(InlineBox*, int, int*) -{ - return IntRect(); -} - -IntRect RenderSVGInlineText::linesBoundingBox() const -{ - IntRect boundingBox; - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - boundingBox.unite(box->calculateBoundaries()); - return boundingBox; -} - -bool RenderSVGInlineText::characterStartsNewTextChunk(int position) const -{ - ASSERT(m_attributes.xValues().size() == textLength()); - ASSERT(m_attributes.yValues().size() == textLength()); - ASSERT(position >= 0); - ASSERT(position < static_cast<int>(textLength())); - - int currentPosition = 0; - unsigned size = m_attributes.characterDataValues().size(); - for (unsigned i = 0; i < size; ++i) { - const SVGTextLayoutAttributes::CharacterData& data = m_attributes.characterDataValues().at(i); - - // We found the desired character. - if (currentPosition == position) { - if (isVerticalWritingMode(style()->svgStyle())) - return m_attributes.yValues().at(position) != SVGTextLayoutAttributes::emptyValue(); - - return m_attributes.xValues().at(position) != SVGTextLayoutAttributes::emptyValue(); - } - - currentPosition += data.spansCharacters; - if (currentPosition > position) - break; - } - - // The desired position is available in the x/y list, but not in the character data values list. - // That means the previous character data described a single glyph, consisting of multiple unicode characters. - // The consequence is that the desired character does not define a new absolute x/y position, even if present in the x/y test. - // This code is tested by svg/W3C-SVG-1.1/text-text-06-t.svg (and described in detail, why this influences chunk detection). - ASSERT(currentPosition > position); - return false; -} - -} - -#endif // ENABLE(SVG) diff --git a/WebCore/rendering/RenderSVGModelObject.h b/WebCore/rendering/RenderSVGModelObject.h index 9de217c..fb8f89f 100644 --- a/WebCore/rendering/RenderSVGModelObject.h +++ b/WebCore/rendering/RenderSVGModelObject.h @@ -47,7 +47,7 @@ class SVGStyledElement; class RenderSVGModelObject : public RenderObject { public: - RenderSVGModelObject(SVGStyledElement*); + explicit RenderSVGModelObject(SVGStyledElement*); virtual bool requiresLayer() const { return false; } diff --git a/WebCore/rendering/RenderSVGResource.cpp b/WebCore/rendering/RenderSVGResource.cpp index 0c943e5..f4c65d5 100644 --- a/WebCore/rendering/RenderSVGResource.cpp +++ b/WebCore/rendering/RenderSVGResource.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> - * 2007 Rob Buis <buis@kde.org> - * 2008 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2007 Rob Buis <buis@kde.org> + * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -33,124 +33,102 @@ namespace WebCore { -inline void RenderSVGResource::adjustColorForPseudoRules(const RenderStyle* style, bool useFillPaint, Color& color) -{ - if (style->insideLink() != InsideVisitedLink) - return; - - RenderStyle* visitedStyle = style->getCachedPseudoStyle(VISITED_LINK); - SVGPaint* visitedPaint = useFillPaint ? visitedStyle->svgStyle()->fillPaint() : visitedStyle->svgStyle()->strokePaint(); - if (visitedPaint->paintType() == SVGPaint::SVG_PAINTTYPE_URI) - return; - - Color visitedColor; - if (visitedPaint->paintType() == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) - visitedColor = visitedStyle->color(); - else - visitedColor = visitedPaint->color(); - - if (visitedColor.isValid()) - color = Color(visitedColor.red(), visitedColor.green(), visitedColor.blue(), color.alpha()); -} - -// FIXME: This method and strokePaintingResource() should be refactored, to share even more code -RenderSVGResource* RenderSVGResource::fillPaintingResource(RenderObject* object, const RenderStyle* style) +static inline RenderSVGResource* requestPaintingResource(RenderSVGResourceMode mode, RenderObject* object, const RenderStyle* style, Color& fallbackColor) { ASSERT(object); ASSERT(style); + // If we have no style at all, ignore it. const SVGRenderStyle* svgStyle = style->svgStyle(); - if (!svgStyle || !svgStyle->hasFill()) + if (!svgStyle) return 0; - SVGPaint* fillPaint = svgStyle->fillPaint(); - ASSERT(fillPaint); - - RenderSVGResource* fillPaintingResource = 0; - - SVGPaint::SVGPaintType paintType = fillPaint->paintType(); - if (paintType == SVGPaint::SVG_PAINTTYPE_URI || paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR) { - if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object)) - fillPaintingResource = resources->fill(); - } - - if (paintType != SVGPaint::SVG_PAINTTYPE_URI && !fillPaintingResource) { - RenderSVGResourceSolidColor* solidResource = sharedSolidPaintingResource(); - fillPaintingResource = solidResource; - - Color fillColor; - if (fillPaint->paintType() == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) - fillColor = style->visitedDependentColor(CSSPropertyColor); - else - fillColor = fillPaint->color(); - - adjustColorForPseudoRules(style, true /* useFillPaint */, fillColor); - - // FIXME: Ideally invalid colors would never get set on the RenderStyle and this could turn into an ASSERT - if (fillColor.isValid()) - solidResource->setColor(fillColor); - else - fillPaintingResource = 0; + // If we have no fill/stroke, return 0. + if (mode == ApplyToFillMode) { + if (!svgStyle->hasFill()) + return 0; + } else { + if (!svgStyle->hasStroke()) + return 0; } - if (!fillPaintingResource) { - // default value (black), see bug 11017 - RenderSVGResourceSolidColor* solidResource = sharedSolidPaintingResource(); - solidResource->setColor(Color::black); - fillPaintingResource = solidResource; - } + SVGPaint* paint = mode == ApplyToFillMode ? svgStyle->fillPaint() : svgStyle->strokePaint(); + ASSERT(paint); - return fillPaintingResource; -} - -RenderSVGResource* RenderSVGResource::strokePaintingResource(RenderObject* object, const RenderStyle* style) -{ - ASSERT(object); - ASSERT(style); - - const SVGRenderStyle* svgStyle = style->svgStyle(); - if (!svgStyle || !svgStyle->hasStroke()) + SVGPaint::SVGPaintType paintType = paint->paintType(); + if (paintType == SVGPaint::SVG_PAINTTYPE_NONE) return 0; - SVGPaint* strokePaint = svgStyle->strokePaint(); - ASSERT(strokePaint); + Color color; + if (paintType == SVGPaint::SVG_PAINTTYPE_RGBCOLOR + || paintType == SVGPaint::SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR + || paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR + || paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR) + color = paint->color(); + else if (paintType == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR || paintType == SVGPaint::SVG_PAINTTYPE_URI_CURRENTCOLOR) + color = style->visitedDependentColor(CSSPropertyColor); + + if (style->insideLink() == InsideVisitedLink) { + RenderStyle* visitedStyle = style->getCachedPseudoStyle(VISITED_LINK); + ASSERT(visitedStyle); + + if (SVGPaint* visitedPaint = mode == ApplyToFillMode ? visitedStyle->svgStyle()->fillPaint() : visitedStyle->svgStyle()->strokePaint()) { + // For SVG_PAINTTYPE_CURRENTCOLOR, 'color' already contains the 'visitedColor'. + if (visitedPaint->paintType() < SVGPaint::SVG_PAINTTYPE_URI_NONE && visitedPaint->paintType() != SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) { + const Color& visitedColor = visitedPaint->color(); + if (visitedColor.isValid()) + color = Color(visitedColor.red(), visitedColor.green(), visitedColor.blue(), color.alpha()); + } + } + } - RenderSVGResource* strokePaintingResource = 0; - FloatRect objectBoundingBox = object->objectBoundingBox(); + // If the primary resource is just a color, return immediately. + RenderSVGResourceSolidColor* colorResource = RenderSVGResource::sharedSolidPaintingResource(); + if (paintType < SVGPaint::SVG_PAINTTYPE_URI_NONE) { + // If an invalid fill color is specified, fallback to fill/stroke="none". + if (!color.isValid()) + return 0; - SVGPaint::SVGPaintType paintType = strokePaint->paintType(); - if (!objectBoundingBox.isEmpty() && (paintType == SVGPaint::SVG_PAINTTYPE_URI || paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR)) { - if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object)) - strokePaintingResource = resources->stroke(); + colorResource->setColor(color); + return colorResource; } - if (paintType != SVGPaint::SVG_PAINTTYPE_URI && !strokePaintingResource) { - RenderSVGResourceSolidColor* solidResource = sharedSolidPaintingResource(); - strokePaintingResource = solidResource; + // If no resources are associated with the given renderer, return the color resource. + SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); + if (!resources) { + // If a paint server is specified, and no or an invalid fallback color is given, default to fill/stroke="black". + if (!color.isValid()) + color = Color::black; - Color strokeColor; - if (strokePaint->paintType() == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) - strokeColor = style->visitedDependentColor(CSSPropertyColor); - else - strokeColor = strokePaint->color(); + colorResource->setColor(color); + return colorResource; + } - adjustColorForPseudoRules(style, false /* useFillPaint */, strokeColor); + // If the requested resource is not available, return the color resource. + RenderSVGResource* uriResource = mode == ApplyToFillMode ? resources->fill() : resources->stroke(); + if (!uriResource) { + // If a paint server is specified, and no or an invalid fallback color is given, default to fill/stroke="black". + if (!color.isValid()) + color = Color::black; - // FIXME: Ideally invalid colors would never get set on the RenderStyle and this could turn into an ASSERT - if (strokeColor.isValid()) - solidResource->setColor(strokeColor); - else - strokePaintingResource = 0; + colorResource->setColor(color); + return colorResource; } - if (!strokePaintingResource) { - // default value (black), see bug 11017 - RenderSVGResourceSolidColor* solidResource = sharedSolidPaintingResource(); - solidResource->setColor(Color::black); - strokePaintingResource = solidResource; - } + // The paint server resource exists, though it may be invalid (pattern with width/height=0). Pass the fallback color to our caller + // so it can use the solid color painting resource, if applyResource() on the URI resource failed. + fallbackColor = color; + return uriResource; +} + +RenderSVGResource* RenderSVGResource::fillPaintingResource(RenderObject* object, const RenderStyle* style, Color& fallbackColor) +{ + return requestPaintingResource(ApplyToFillMode, object, style, fallbackColor); +} - return strokePaintingResource; +RenderSVGResource* RenderSVGResource::strokePaintingResource(RenderObject* object, const RenderStyle* style, Color& fallbackColor) +{ + return requestPaintingResource(ApplyToStrokeMode, object, style, fallbackColor); } RenderSVGResourceSolidColor* RenderSVGResource::sharedSolidPaintingResource() diff --git a/WebCore/rendering/RenderSVGResource.h b/WebCore/rendering/RenderSVGResource.h index a70ce52..0230e98 100644 --- a/WebCore/rendering/RenderSVGResource.h +++ b/WebCore/rendering/RenderSVGResource.h @@ -75,15 +75,12 @@ public: return 0; } - // Helper utilities used in the render tree to access resources used for painting shapes/text (gradients & patterns only) - static RenderSVGResource* fillPaintingResource(RenderObject*, const RenderStyle*); - static RenderSVGResource* strokePaintingResource(RenderObject*, const RenderStyle*); + // Helper utilities used in the render tree to access resources used for painting shapes/text (gradients & patterns & solid colors only) + static RenderSVGResource* fillPaintingResource(RenderObject*, const RenderStyle*, Color& fallbackColor); + static RenderSVGResource* strokePaintingResource(RenderObject*, const RenderStyle*, Color& fallbackColor); static RenderSVGResourceSolidColor* sharedSolidPaintingResource(); static void markForLayoutAndParentResourceInvalidation(RenderObject*, bool needsLayout = true); - -private: - static void adjustColorForPseudoRules(const RenderStyle*, bool useFillPaint, Color&); }; } diff --git a/WebCore/rendering/RenderSVGResourceClipper.cpp b/WebCore/rendering/RenderSVGResourceClipper.cpp index ef44a79..e7b9fbb 100644 --- a/WebCore/rendering/RenderSVGResourceClipper.cpp +++ b/WebCore/rendering/RenderSVGResourceClipper.cpp @@ -136,7 +136,7 @@ bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const return false; // Fallback to masking, if there is more than one clipping path. if (clipPath.isEmpty()) { - clipPath = styled->toClipPath(); + styled->toClipPath(clipPath); clipRule = svgStyle->clipRule(); } else return false; @@ -178,7 +178,7 @@ bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, cons FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(object, absoluteTargetRect); if (shouldCreateClipData && !clampedAbsoluteTargetRect.isEmpty()) { - if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, clipperData->clipMaskImage, DeviceRGB)) + if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, clipperData->clipMaskImage, ColorSpaceDeviceRGB)) return false; GraphicsContext* maskContext = clipperData->clipMaskImage->context(); @@ -247,7 +247,7 @@ bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData } // Only shapes, paths and texts are allowed for clipping. - if (!renderer->isRenderPath() && !renderer->isSVGText()) + if (!renderer->isSVGPath() && !renderer->isSVGText()) continue; // Save the old RenderStyle of the current object for restoring after drawing @@ -289,7 +289,7 @@ void RenderSVGResourceClipper::calculateClipContentRepaintRect() RenderObject* renderer = childNode->renderer(); if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer) continue; - if (!renderer->isRenderPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer()) + if (!renderer->isSVGPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer()) continue; RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) @@ -315,7 +315,7 @@ bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundin RenderObject* renderer = childNode->renderer(); if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer) continue; - if (!renderer->isRenderPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer()) + if (!renderer->isSVGPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer()) continue; IntPoint hitPoint; HitTestResult result(hitPoint); diff --git a/WebCore/rendering/RenderSVGResourceContainer.cpp b/WebCore/rendering/RenderSVGResourceContainer.cpp index 41ab91f..fb30efd 100644 --- a/WebCore/rendering/RenderSVGResourceContainer.cpp +++ b/WebCore/rendering/RenderSVGResourceContainer.cpp @@ -179,7 +179,7 @@ void RenderSVGResourceContainer::registerResource() // FIXME: This does not belong here. AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform) { - if (!object->isRenderPath()) + if (!object->isSVGPath()) return resourceTransform; SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(object->node()); diff --git a/WebCore/rendering/RenderSVGResourceFilter.cpp b/WebCore/rendering/RenderSVGResourceFilter.cpp index 4dccad6..3203ddf 100644 --- a/WebCore/rendering/RenderSVGResourceFilter.cpp +++ b/WebCore/rendering/RenderSVGResourceFilter.cpp @@ -39,6 +39,7 @@ #include "SVGFilter.h" #include "SVGFilterElement.h" #include "SVGFilterPrimitiveStandardAttributes.h" +#include "SVGImageBufferTools.h" #include "SVGStyledElement.h" #include "SVGUnitTypes.h" #include <wtf/Vector.h> @@ -150,70 +151,93 @@ bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, } OwnPtr<FilterData> filterData(new FilterData); + FloatRect targetBoundingBox = object->objectBoundingBox(); + + SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); + filterData->boundaries = filterElement->filterBoundingBox(targetBoundingBox); + if (filterData->boundaries.isEmpty()) + return false; + + // Determine absolute transformation matrix for filter. + AffineTransform absoluteTransform; + SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); + if (!absoluteTransform.isInvertible()) + return false; + + // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile. + filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), absoluteTransform.e(), absoluteTransform.f()); + + // 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); + + // Create the SVGFilter object. + bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; + filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); + + // Create all relevant filter primitives. filterData->builder = buildPrimitives(); if (!filterData->builder) return false; - FloatRect paintRect = object->strokeBoundingBox(); - // Calculate the scale factor for the use of filterRes. // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion - SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); - filterData->boundaries = filterElement->filterBoundingBox(object->objectBoundingBox()); - if (filterData->boundaries.isEmpty()) - return false; - - FloatSize scale(1.0f, 1.0f); + FloatSize scale(1, 1); if (filterElement->hasAttribute(SVGNames::filterResAttr)) { - scale.setWidth(filterElement->filterResX() / filterData->boundaries.width()); - scale.setHeight(filterElement->filterResY() / filterData->boundaries.height()); + scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width()); + scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height()); } if (scale.isEmpty()) return false; - // clip sourceImage to filterRegion - FloatRect clippedSourceRect = paintRect; - clippedSourceRect.intersect(filterData->boundaries); - - // scale filter size to filterRes - FloatRect tempSourceRect = clippedSourceRect; - - // scale to big sourceImage size to kMaxFilterSize + // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize. + FloatRect tempSourceRect = absoluteDrawingRegion; tempSourceRect.scale(scale.width(), scale.height()); fitsInMaximumImageSize(tempSourceRect.size(), scale); - // prepare Filters - bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; - filterData->filter = SVGFilter::create(paintRect, filterData->boundaries, primitiveBoundingBoxMode); + // Set the scale level in SVGFilter. filterData->filter->setFilterResolution(scale); FilterEffect* lastEffect = filterData->builder->lastEffect(); if (!lastEffect) return false; - + + // Determine the filter primitive subregions of every effect. lastEffect->determineFilterPrimitiveSubregion(filterData->filter.get()); - // At least one FilterEffect has a too big image size, - // recalculate the effect sizes with new scale factors. if (!fitsInMaximumImageSize(filterData->filter->maxImageSize(), scale)) { + // At least one FilterEffect has a too big image size, + // recalculate the effect sizes with new scale factor. filterData->filter->setFilterResolution(scale); lastEffect->determineFilterPrimitiveSubregion(filterData->filter.get()); } - clippedSourceRect.scale(scale.width(), scale.height()); - - // Draw the content of the current element and it's childs to a imageBuffer to get the SourceGraphic. - // The size of the SourceGraphic is clipped to the size of the filterRegion. - IntRect bufferRect = enclosingIntRect(clippedSourceRect); - OwnPtr<ImageBuffer> sourceGraphic(ImageBuffer::create(bufferRect.size(), LinearRGB)); - - if (!sourceGraphic.get()) + // 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()) { + ASSERT(!m_filter.contains(object)); + filterData->savedContext = context; + m_filter.set(object, filterData.leakPtr()); return false; + } + absoluteDrawingRegion.scale(scale.width(), scale.height()); + + OwnPtr<ImageBuffer> sourceGraphic; + if (!SVGImageBufferTools::createImageBuffer(absoluteDrawingRegion, absoluteDrawingRegion, sourceGraphic, ColorSpaceLinearRGB)) + return false; + GraphicsContext* sourceGraphicContext = sourceGraphic->context(); - sourceGraphicContext->translate(-clippedSourceRect.x(), -clippedSourceRect.y()); - sourceGraphicContext->scale(scale); - sourceGraphicContext->clearRect(FloatRect(FloatPoint(), paintRect.size())); + ASSERT(sourceGraphicContext); + + sourceGraphicContext->translate(-absoluteDrawingRegion.x(), -absoluteDrawingRegion.y()); + if (scale.width() != 1 || scale.height() != 1) + sourceGraphicContext->scale(scale); + + sourceGraphicContext->concatCTM(filterData->shearFreeAbsoluteTransform); + sourceGraphicContext->clearRect(FloatRect(FloatPoint(), absoluteDrawingRegion.size())); filterData->sourceGraphicBuffer = sourceGraphic.release(); filterData->savedContext = context; @@ -248,7 +272,8 @@ void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsCo context = filterData->savedContext; filterData->savedContext = 0; #if !PLATFORM(CG) - filterData->sourceGraphicBuffer->transformColorSpace(DeviceRGB, LinearRGB); + if (filterData->sourceGraphicBuffer) + filterData->sourceGraphicBuffer->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB); #endif } @@ -264,16 +289,23 @@ void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsCo #if !PLATFORM(CG) ImageBuffer* resultImage = lastEffect->resultImage(); if (resultImage) - resultImage->transformColorSpace(LinearRGB, DeviceRGB); + resultImage->transformColorSpace(ColorSpaceLinearRGB, ColorSpaceDeviceRGB); #endif filterData->builded = true; } ImageBuffer* resultImage = lastEffect->resultImage(); - if (resultImage) - context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->filterPrimitiveSubregion()); - } + if (resultImage) { + context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse()); + context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height())); + context->clip(lastEffect->maxEffectRect()); + context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect()); + context->scale(filterData->filter->filterResolution()); + + context->concatCTM(filterData->shearFreeAbsoluteTransform); + } + } filterData->sourceGraphicBuffer.clear(); } diff --git a/WebCore/rendering/RenderSVGResourceFilter.h b/WebCore/rendering/RenderSVGResourceFilter.h index 314c94d..c64f5c6 100644 --- a/WebCore/rendering/RenderSVGResourceFilter.h +++ b/WebCore/rendering/RenderSVGResourceFilter.h @@ -42,7 +42,8 @@ namespace WebCore { struct FilterData { FilterData() - : builded(false) + : savedContext(0) + , builded(false) { } @@ -50,6 +51,7 @@ struct FilterData { RefPtr<SVGFilterBuilder> builder; OwnPtr<ImageBuffer> sourceGraphicBuffer; GraphicsContext* savedContext; + AffineTransform shearFreeAbsoluteTransform; FloatRect boundaries; FloatSize scale; bool builded; diff --git a/WebCore/rendering/RenderSVGResourceFilterPrimitive.h b/WebCore/rendering/RenderSVGResourceFilterPrimitive.h index 4b23737..b054203 100644 --- a/WebCore/rendering/RenderSVGResourceFilterPrimitive.h +++ b/WebCore/rendering/RenderSVGResourceFilterPrimitive.h @@ -36,7 +36,7 @@ namespace WebCore { class RenderSVGResourceFilterPrimitive : public RenderSVGHiddenContainer { public: - RenderSVGResourceFilterPrimitive(SVGFilterPrimitiveStandardAttributes* filterPrimitiveElement); + explicit RenderSVGResourceFilterPrimitive(SVGFilterPrimitiveStandardAttributes* filterPrimitiveElement); private: virtual const char* renderName() const { return "RenderSVGResourceFilterPrimitive"; } diff --git a/WebCore/rendering/RenderSVGResourceGradient.cpp b/WebCore/rendering/RenderSVGResourceGradient.cpp index 1c33de4..a757147 100644 --- a/WebCore/rendering/RenderSVGResourceGradient.cpp +++ b/WebCore/rendering/RenderSVGResourceGradient.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> - * 2008 Eric Seidel <eric@webkit.org> - * 2008 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -28,6 +28,7 @@ #include "GradientAttributes.h" #include "GraphicsContext.h" +#include "RenderSVGText.h" #include "SVGImageBufferTools.h" #include "SVGRenderSupport.h" #include <wtf/UnusedParam.h> @@ -36,6 +37,7 @@ namespace WebCore { RenderSVGResourceGradient::RenderSVGResourceGradient(SVGGradientElement* node) : RenderSVGResourceContainer(node) + , m_shouldCollectGradientAttributes(true) #if PLATFORM(CG) , m_savedContext(0) #endif @@ -58,6 +60,7 @@ void RenderSVGResourceGradient::removeAllClientsFromCache(bool markForInvalidati m_gradient.clear(); } + m_shouldCollectGradientAttributes = true; markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation); } @@ -75,9 +78,9 @@ void RenderSVGResourceGradient::removeClientFromCache(RenderObject* client, bool static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& context, GraphicsContext*& savedContext, OwnPtr<ImageBuffer>& imageBuffer, - const RenderObject* object) + RenderObject* object) { - const RenderObject* textRootBlock = SVGRenderSupport::findTextRootObject(object); + RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object); ASSERT(textRootBlock); AffineTransform absoluteTransform; @@ -89,7 +92,7 @@ static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& con return false; OwnPtr<ImageBuffer> maskImage; - if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskImage, DeviceRGB)) + if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskImage, ColorSpaceDeviceRGB)) return false; GraphicsContext* maskImageContext = maskImage->context(); @@ -108,10 +111,11 @@ static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& con static inline AffineTransform clipToTextMask(GraphicsContext* context, OwnPtr<ImageBuffer>& imageBuffer, FloatRect& targetRect, - const RenderObject* object, - GradientData* gradientData) + RenderObject* object, + bool boundingBoxMode, + const AffineTransform& gradientTransform) { - const RenderObject* textRootBlock = SVGRenderSupport::findTextRootObject(object); + RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object); ASSERT(textRootBlock); targetRect = textRootBlock->repaintRectInLocalCoordinates(); @@ -125,12 +129,12 @@ static inline AffineTransform clipToTextMask(GraphicsContext* context, SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, imageBuffer); AffineTransform matrix; - if (gradientData->boundingBoxMode) { + if (boundingBoxMode) { FloatRect maskBoundingBox = textRootBlock->objectBoundingBox(); matrix.translate(maskBoundingBox.x(), maskBoundingBox.y()); matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height()); } - matrix.multLeft(gradientData->transform); + matrix.multLeft(gradientTransform); return matrix; } #endif @@ -150,13 +154,22 @@ bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* if (!gradientElement) return false; - gradientElement->updateAnimatedSVGAttribute(anyQName()); + if (m_shouldCollectGradientAttributes) { + gradientElement->updateAnimatedSVGAttribute(anyQName()); + collectGradientAttributes(gradientElement); + m_shouldCollectGradientAttributes = false; + } + + // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified, + // then the given effect (e.g. a gradient or a filter) will be ignored. + FloatRect objectBoundingBox = object->objectBoundingBox(); + if (boundingBoxMode() && objectBoundingBox.isEmpty()) + return false; if (!m_gradient.contains(object)) m_gradient.set(object, new GradientData); GradientData* gradientData = m_gradient.get(object); - bool isPaintingText = resourceMode & ApplyToTextMode; // Create gradient object @@ -167,16 +180,18 @@ bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* // resource, so don't apply it here. For non-CG platforms, we want the text bounding // box applied to the gradient space transform now, so the gradient shader can use it. #if PLATFORM(CG) - if (gradientData->boundingBoxMode && !isPaintingText) { + if (boundingBoxMode() && !objectBoundingBox.isEmpty() && !isPaintingText) { #else - if (gradientData->boundingBoxMode) { + if (boundingBoxMode() && !objectBoundingBox.isEmpty()) { #endif - FloatRect objectBoundingBox = object->objectBoundingBox(); gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y()); gradientData->userspaceTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); } - gradientData->userspaceTransform.multLeft(gradientData->transform); + AffineTransform gradientTransform; + calculateGradientTransform(gradientTransform); + + gradientData->userspaceTransform.multLeft(gradientTransform); gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform); } @@ -230,8 +245,11 @@ void RenderSVGResourceGradient::postApplyResource(RenderObject* object, Graphics context = m_savedContext; m_savedContext = 0; + AffineTransform gradientTransform; + calculateGradientTransform(gradientTransform); + FloatRect targetRect; - gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, object, gradientData)); + gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, object, boundingBoxMode(), gradientTransform)); context->setFillGradient(gradientData->gradient); context->fillRect(targetRect); diff --git a/WebCore/rendering/RenderSVGResourceGradient.h b/WebCore/rendering/RenderSVGResourceGradient.h index 4de4272..bc0b864 100644 --- a/WebCore/rendering/RenderSVGResourceGradient.h +++ b/WebCore/rendering/RenderSVGResourceGradient.h @@ -37,10 +37,7 @@ namespace WebCore { struct GradientData { RefPtr<Gradient> gradient; - - bool boundingBoxMode; AffineTransform userspaceTransform; - AffineTransform transform; }; class GraphicsContext; @@ -59,9 +56,14 @@ public: protected: void addStops(GradientData*, const Vector<Gradient::ColorStop>&) const; + + virtual bool boundingBoxMode() const = 0; + virtual void calculateGradientTransform(AffineTransform&) = 0; + virtual void collectGradientAttributes(SVGGradientElement*) = 0; virtual void buildGradient(GradientData*, SVGGradientElement*) const = 0; private: + bool m_shouldCollectGradientAttributes : 1; HashMap<RenderObject*, GradientData*> m_gradient; #if PLATFORM(CG) diff --git a/WebCore/rendering/RenderSVGResourceLinearGradient.cpp b/WebCore/rendering/RenderSVGResourceLinearGradient.cpp index e34e524..ce8d69e 100644 --- a/WebCore/rendering/RenderSVGResourceLinearGradient.cpp +++ b/WebCore/rendering/RenderSVGResourceLinearGradient.cpp @@ -40,25 +40,26 @@ RenderSVGResourceLinearGradient::~RenderSVGResourceLinearGradient() { } +void RenderSVGResourceLinearGradient::collectGradientAttributes(SVGGradientElement* gradientElement) +{ + m_attributes = LinearGradientAttributes(); + static_cast<SVGLinearGradientElement*>(gradientElement)->collectGradientAttributes(m_attributes); +} + void RenderSVGResourceLinearGradient::buildGradient(GradientData* gradientData, SVGGradientElement* gradientElement) const { SVGLinearGradientElement* linearGradientElement = static_cast<SVGLinearGradientElement*>(gradientElement); - LinearGradientAttributes attributes = linearGradientElement->collectGradientProperties(); // Determine gradient start/end points FloatPoint startPoint; FloatPoint endPoint; - linearGradientElement->calculateStartEndPoints(attributes, startPoint, endPoint); + linearGradientElement->calculateStartEndPoints(m_attributes, startPoint, endPoint); gradientData->gradient = Gradient::create(startPoint, endPoint); - gradientData->gradient->setSpreadMethod(attributes.spreadMethod()); - - // Record current gradient transform - gradientData->transform = attributes.gradientTransform(); - gradientData->boundingBoxMode = attributes.boundingBoxMode(); + gradientData->gradient->setSpreadMethod(m_attributes.spreadMethod()); // Add stops - addStops(gradientData, attributes.stops()); + addStops(gradientData, m_attributes.stops()); } } diff --git a/WebCore/rendering/RenderSVGResourceLinearGradient.h b/WebCore/rendering/RenderSVGResourceLinearGradient.h index c1f84c2..9e4530d 100644 --- a/WebCore/rendering/RenderSVGResourceLinearGradient.h +++ b/WebCore/rendering/RenderSVGResourceLinearGradient.h @@ -23,6 +23,7 @@ #define RenderSVGResourceLinearGradient_h #if ENABLE(SVG) +#include "LinearGradientAttributes.h" #include "RenderSVGResourceGradient.h" namespace WebCore { @@ -39,7 +40,13 @@ public: virtual RenderSVGResourceType resourceType() const { return s_resourceType; } static RenderSVGResourceType s_resourceType; + virtual bool boundingBoxMode() const { return m_attributes.boundingBoxMode(); } + virtual void calculateGradientTransform(AffineTransform& transform) { transform = m_attributes.gradientTransform(); } + virtual void collectGradientAttributes(SVGGradientElement*); virtual void buildGradient(GradientData*, SVGGradientElement*) const; + +private: + LinearGradientAttributes m_attributes; }; } diff --git a/WebCore/rendering/RenderSVGResourceMasker.cpp b/WebCore/rendering/RenderSVGResourceMasker.cpp index 3e81929..10133b2 100644 --- a/WebCore/rendering/RenderSVGResourceMasker.cpp +++ b/WebCore/rendering/RenderSVGResourceMasker.cpp @@ -107,7 +107,7 @@ bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*, if (!maskElement) return false; - if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskerData->maskImage, LinearRGB)) + if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskerData->maskImage, ColorSpaceLinearRGB)) return false; GraphicsContext* maskImageContext = maskerData->maskImage->context(); @@ -156,7 +156,7 @@ void RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, c maskImageContext->restore(); #if !PLATFORM(CG) - maskerData->maskImage->transformColorSpace(DeviceRGB, LinearRGB); + maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB); #endif // Create the luminance mask. diff --git a/WebCore/rendering/RenderSVGResourcePattern.cpp b/WebCore/rendering/RenderSVGResourcePattern.cpp index ccbdaca..d2e4563 100644 --- a/WebCore/rendering/RenderSVGResourcePattern.cpp +++ b/WebCore/rendering/RenderSVGResourcePattern.cpp @@ -37,6 +37,7 @@ RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResource RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node) : RenderSVGResourceContainer(node) + , m_shouldCollectPatternAttributes(true) { } @@ -56,6 +57,7 @@ void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidatio m_pattern.clear(); } + m_shouldCollectPatternAttributes = true; markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation); } @@ -84,22 +86,34 @@ bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* if (!patternElement) return false; - patternElement->updateAnimatedSVGAttribute(anyQName()); + if (m_shouldCollectPatternAttributes) { + patternElement->updateAnimatedSVGAttribute(anyQName()); + + m_attributes = PatternAttributes(); + patternElement->collectPatternAttributes(m_attributes); + m_shouldCollectPatternAttributes = false; + } + + // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified, + // then the given effect (e.g. a gradient or a filter) will be ignored. + FloatRect objectBoundingBox = object->objectBoundingBox(); + if (m_attributes.boundingBoxMode() && objectBoundingBox.isEmpty()) + return false; if (!m_pattern.contains(object)) m_pattern.set(object, new PatternData); PatternData* patternData = m_pattern.get(object); if (!patternData->pattern) { - PatternAttributes attributes = patternElement->collectPatternProperties(); - // If we couldn't determine the pattern content element root, stop here. - if (!attributes.patternContentElement()) + if (!m_attributes.patternContentElement()) return false; // Compute all necessary transformations to build the tile image & the pattern. FloatRect tileBoundaries; - AffineTransform tileImageTransform = buildTileImageTransform(object, attributes, patternElement, tileBoundaries); + AffineTransform tileImageTransform; + if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform)) + return false; AffineTransform absoluteTransform; SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); @@ -107,7 +121,7 @@ bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* FloatRect absoluteTileBoundaries = absoluteTransform.mapRect(tileBoundaries); // Build tile image. - OwnPtr<ImageBuffer> tileImage = createTileImage(object, attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform); + OwnPtr<ImageBuffer> tileImage = createTileImage(object, m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform); if (!tileImage) return false; @@ -124,7 +138,7 @@ bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y()); patternData->transform.scale(tileBoundaries.width() / absoluteTileBoundaries.width(), tileBoundaries.height() / absoluteTileBoundaries.height()); - AffineTransform patternTransform = attributes.patternTransform(); + AffineTransform patternTransform = m_attributes.patternTransform(); if (!patternTransform.isIdentity()) patternData->transform.multiply(patternTransform); @@ -201,19 +215,21 @@ static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attr attributes.height().value(patternElement)); } -AffineTransform RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer, - const PatternAttributes& attributes, - const SVGPatternElement* patternElement, - FloatRect& patternBoundaries) const +bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer, + const PatternAttributes& attributes, + const SVGPatternElement* patternElement, + FloatRect& patternBoundaries, + AffineTransform& tileImageTransform) const { ASSERT(renderer); ASSERT(patternElement); - FloatRect objectBoundingBox = renderer->objectBoundingBox(); + FloatRect objectBoundingBox = renderer->objectBoundingBox(); patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); + if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0) + return false; AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(patternElement->viewBox(), patternElement->preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height()); - AffineTransform tileImageTransform; // Apply viewBox/objectBoundingBox transformations. if (!viewBoxCTM.isIdentity()) @@ -223,7 +239,7 @@ AffineTransform RenderSVGResourcePattern::buildTileImageTransform(RenderObject* tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height()); } - return tileImageTransform; + return true; } PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(RenderObject* object, @@ -246,7 +262,7 @@ PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(RenderObject* OwnPtr<ImageBuffer> tileImage; - if (!SVGImageBufferTools::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, DeviceRGB)) + if (!SVGImageBufferTools::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB)) return PassOwnPtr<ImageBuffer>(); GraphicsContext* tileImageContext = tileImage->context(); diff --git a/WebCore/rendering/RenderSVGResourcePattern.h b/WebCore/rendering/RenderSVGResourcePattern.h index 9a067c2..ba4aec4 100644 --- a/WebCore/rendering/RenderSVGResourcePattern.h +++ b/WebCore/rendering/RenderSVGResourcePattern.h @@ -27,6 +27,7 @@ #include "FloatRect.h" #include "ImageBuffer.h" #include "Pattern.h" +#include "PatternAttributes.h" #include "RenderSVGResourceContainer.h" #include "SVGPatternElement.h" #include "SVGUnitTypes.h" @@ -41,8 +42,6 @@ struct PatternData { AffineTransform transform; }; -struct PatternAttributes; - class RenderSVGResourcePattern : public RenderSVGResourceContainer { public: RenderSVGResourcePattern(SVGPatternElement*); @@ -61,11 +60,13 @@ public: static RenderSVGResourceType s_resourceType; private: - AffineTransform buildTileImageTransform(RenderObject*, const PatternAttributes&, const SVGPatternElement*, FloatRect& patternBoundaries) const; + bool buildTileImageTransform(RenderObject*, const PatternAttributes&, const SVGPatternElement*, FloatRect& patternBoundaries, AffineTransform& tileImageTransform) const; PassOwnPtr<ImageBuffer> createTileImage(RenderObject*, const PatternAttributes&, const FloatRect& tileBoundaries, const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform) const; + bool m_shouldCollectPatternAttributes : 1; + PatternAttributes m_attributes; HashMap<RenderObject*, PatternData*> m_pattern; }; diff --git a/WebCore/rendering/RenderSVGResourceRadialGradient.cpp b/WebCore/rendering/RenderSVGResourceRadialGradient.cpp index a8904c8..300afcb 100644 --- a/WebCore/rendering/RenderSVGResourceRadialGradient.cpp +++ b/WebCore/rendering/RenderSVGResourceRadialGradient.cpp @@ -40,30 +40,31 @@ RenderSVGResourceRadialGradient::~RenderSVGResourceRadialGradient() { } +void RenderSVGResourceRadialGradient::collectGradientAttributes(SVGGradientElement* gradientElement) +{ + m_attributes = RadialGradientAttributes(); + static_cast<SVGRadialGradientElement*>(gradientElement)->collectGradientAttributes(m_attributes); +} + void RenderSVGResourceRadialGradient::buildGradient(GradientData* gradientData, SVGGradientElement* gradientElement) const { SVGRadialGradientElement* radialGradientElement = static_cast<SVGRadialGradientElement*>(gradientElement); - RadialGradientAttributes attributes = radialGradientElement->collectGradientProperties(); // Determine gradient focal/center points and radius FloatPoint focalPoint; FloatPoint centerPoint; float radius; - radialGradientElement->calculateFocalCenterPointsAndRadius(attributes, focalPoint, centerPoint, radius); + radialGradientElement->calculateFocalCenterPointsAndRadius(m_attributes, focalPoint, centerPoint, radius); gradientData->gradient = Gradient::create(focalPoint, - 0.0f, // SVG does not support a "focus radius" + 0, // SVG does not support a "focus radius" centerPoint, radius); - gradientData->gradient->setSpreadMethod(attributes.spreadMethod()); - - // Record current gradient transform - gradientData->transform = attributes.gradientTransform(); - gradientData->boundingBoxMode = attributes.boundingBoxMode(); + gradientData->gradient->setSpreadMethod(m_attributes.spreadMethod()); // Add stops - addStops(gradientData, attributes.stops()); + addStops(gradientData, m_attributes.stops()); } } diff --git a/WebCore/rendering/RenderSVGResourceRadialGradient.h b/WebCore/rendering/RenderSVGResourceRadialGradient.h index 0583f99..6492ee3 100644 --- a/WebCore/rendering/RenderSVGResourceRadialGradient.h +++ b/WebCore/rendering/RenderSVGResourceRadialGradient.h @@ -23,6 +23,7 @@ #define RenderSVGResourceRadialGradient_h #if ENABLE(SVG) +#include "RadialGradientAttributes.h" #include "RenderSVGResourceGradient.h" namespace WebCore { @@ -39,7 +40,13 @@ public: virtual RenderSVGResourceType resourceType() const { return s_resourceType; } static RenderSVGResourceType s_resourceType; + virtual bool boundingBoxMode() const { return m_attributes.boundingBoxMode(); } + virtual void calculateGradientTransform(AffineTransform& transform) { transform = m_attributes.gradientTransform(); } + virtual void collectGradientAttributes(SVGGradientElement*); virtual void buildGradient(GradientData*, SVGGradientElement*) const; + +private: + RadialGradientAttributes m_attributes; }; } diff --git a/WebCore/rendering/RenderSVGResourceSolidColor.cpp b/WebCore/rendering/RenderSVGResourceSolidColor.cpp index 51ad658..8228c80 100644 --- a/WebCore/rendering/RenderSVGResourceSolidColor.cpp +++ b/WebCore/rendering/RenderSVGResourceSolidColor.cpp @@ -52,7 +52,7 @@ bool RenderSVGResourceSolidColor::applyResource(RenderObject* object, RenderStyl ASSERT(resourceMode != ApplyToDefaultMode); const SVGRenderStyle* svgStyle = style ? style->svgStyle() : 0; - ColorSpace colorSpace = style ? style->colorSpace() : DeviceColorSpace; + ColorSpace colorSpace = style ? style->colorSpace() : ColorSpaceDeviceRGB; if (resourceMode & ApplyToFillMode) { context->setAlpha(svgStyle ? svgStyle->fillOpacity() : 1.0f); @@ -90,7 +90,7 @@ void RenderSVGResourceSolidColor::postApplyResource(RenderObject*, GraphicsConte #if PLATFORM(SKIA) && !PLATFORM(ANDROID) // FIXME: Move this into the GraphicsContext // WebKit implicitly expects us to reset the path. - // For example in fillAndStrokePath() of RenderPath.cpp the path is + // For example in fillAndStrokePath() of RenderSVGPath.cpp the path is // added back to the context after filling. This is because internally it // calls CGContextFillPath() which closes the path. context->beginPath(); diff --git a/WebCore/rendering/RenderSVGRoot.cpp b/WebCore/rendering/RenderSVGRoot.cpp index 82b10d5..215aac7 100644 --- a/WebCore/rendering/RenderSVGRoot.cpp +++ b/WebCore/rendering/RenderSVGRoot.cpp @@ -53,14 +53,8 @@ RenderSVGRoot::RenderSVGRoot(SVGStyledElement* node) setReplaced(true); } -int RenderSVGRoot::lineHeight(bool, bool) const +RenderSVGRoot::~RenderSVGRoot() { - return height() + marginTop() + marginBottom(); -} - -int RenderSVGRoot::baselinePosition(bool, bool) const -{ - return height() + marginTop() + marginBottom(); } void RenderSVGRoot::computePreferredLogicalWidths() @@ -68,7 +62,7 @@ void RenderSVGRoot::computePreferredLogicalWidths() ASSERT(preferredLogicalWidthsDirty()); int borderAndPadding = borderAndPaddingWidth(); - int width = computeReplacedWidth(false) + borderAndPadding; + int width = computeReplacedLogicalWidth(false) + borderAndPadding; if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) width = min(width, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0)); @@ -82,10 +76,10 @@ void RenderSVGRoot::computePreferredLogicalWidths() setPreferredLogicalWidthsDirty(false); } -int RenderSVGRoot::computeReplacedWidth(bool includeMaxWidth) const +int RenderSVGRoot::computeReplacedLogicalWidth(bool includeMaxWidth) const { - int replacedWidth = RenderBox::computeReplacedWidth(includeMaxWidth); - if (!style()->width().isPercent()) + int replacedWidth = RenderBox::computeReplacedLogicalWidth(includeMaxWidth); + if (!style()->logicalWidth().isPercent()) return replacedWidth; // FIXME: Investigate in size rounding issues @@ -93,10 +87,10 @@ int RenderSVGRoot::computeReplacedWidth(bool includeMaxWidth) const return static_cast<int>(roundf(replacedWidth * svg->currentScale())); } -int RenderSVGRoot::computeReplacedHeight() const +int RenderSVGRoot::computeReplacedLogicalHeight() const { - int replacedHeight = RenderBox::computeReplacedHeight(); - if (!style()->height().isPercent()) + int replacedHeight = RenderBox::computeReplacedLogicalHeight(); + if (!style()->logicalHeight().isPercent()) return replacedHeight; // FIXME: Investigate in size rounding issues diff --git a/WebCore/rendering/RenderSVGRoot.h b/WebCore/rendering/RenderSVGRoot.h index 3c29b87..1b6aa22 100644 --- a/WebCore/rendering/RenderSVGRoot.h +++ b/WebCore/rendering/RenderSVGRoot.h @@ -35,7 +35,8 @@ class AffineTransform; class RenderSVGRoot : public RenderBox { public: - RenderSVGRoot(SVGStyledElement*); + explicit RenderSVGRoot(SVGStyledElement*); + virtual ~RenderSVGRoot(); const RenderObjectChildList* children() const { return &m_children; } RenderObjectChildList* children() { return &m_children; } @@ -51,11 +52,9 @@ private: virtual bool isSVGRoot() const { return true; } virtual const char* renderName() const { return "RenderSVGRoot"; } - virtual int lineHeight(bool b, bool isRootLineBox = false) const; - virtual int baselinePosition(bool b, bool isRootLineBox = false) const; virtual void computePreferredLogicalWidths(); - virtual int computeReplacedWidth(bool includeMaxWidth = true) const; - virtual int computeReplacedHeight() const; + virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual int computeReplacedLogicalHeight() const; virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); diff --git a/WebCore/rendering/RenderSVGTransformableContainer.h b/WebCore/rendering/RenderSVGTransformableContainer.h index b63b91c..b49a403 100644 --- a/WebCore/rendering/RenderSVGTransformableContainer.h +++ b/WebCore/rendering/RenderSVGTransformableContainer.h @@ -29,7 +29,7 @@ namespace WebCore { class SVGStyledTransformableElement; class RenderSVGTransformableContainer : public RenderSVGContainer { public: - RenderSVGTransformableContainer(SVGStyledTransformableElement*); + explicit RenderSVGTransformableContainer(SVGStyledTransformableElement*); virtual const AffineTransform& localToParentTransform() const { return m_localTransform; } virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } diff --git a/WebCore/rendering/RenderSVGViewportContainer.h b/WebCore/rendering/RenderSVGViewportContainer.h index 7b5702b..63c336c 100644 --- a/WebCore/rendering/RenderSVGViewportContainer.h +++ b/WebCore/rendering/RenderSVGViewportContainer.h @@ -32,7 +32,7 @@ namespace WebCore { // thus we inherit from RenderSVGContainer instead of RenderSVGTransformableContainer class RenderSVGViewportContainer : public RenderSVGContainer { public: - RenderSVGViewportContainer(SVGStyledElement*); + explicit RenderSVGViewportContainer(SVGStyledElement*); private: virtual bool isSVGContainer() const { return true; } diff --git a/WebCore/rendering/RenderScrollbarTheme.cpp b/WebCore/rendering/RenderScrollbarTheme.cpp index 19143cc..e32d87a 100644 --- a/WebCore/rendering/RenderScrollbarTheme.cpp +++ b/WebCore/rendering/RenderScrollbarTheme.cpp @@ -109,7 +109,7 @@ IntRect RenderScrollbarTheme::constrainTrackRectToTrackPieces(Scrollbar* scrollb void RenderScrollbarTheme::paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect) { // FIXME: Implement. - context->fillRect(cornerRect, Color::white, DeviceColorSpace); + context->fillRect(cornerRect, Color::white, ColorSpaceDeviceRGB); } void RenderScrollbarTheme::paintScrollbarBackground(GraphicsContext* context, Scrollbar* scrollbar) diff --git a/WebCore/rendering/RenderSlider.cpp b/WebCore/rendering/RenderSlider.cpp index 33df244..35d72f4 100644 --- a/WebCore/rendering/RenderSlider.cpp +++ b/WebCore/rendering/RenderSlider.cpp @@ -159,7 +159,7 @@ RenderSlider::~RenderSlider() m_thumb->detach(); } -int RenderSlider::baselinePosition(bool, bool) const +int RenderSlider::baselinePosition(bool /*firstLine*/, LineDirectionMode, LinePositionMode) const { return height() + marginTop(); } diff --git a/WebCore/rendering/RenderSlider.h b/WebCore/rendering/RenderSlider.h index d214e41..fa743de 100644 --- a/WebCore/rendering/RenderSlider.h +++ b/WebCore/rendering/RenderSlider.h @@ -42,7 +42,7 @@ namespace WebCore { virtual const char* renderName() const { return "RenderSlider"; } virtual bool isSlider() const { return true; } - virtual int baselinePosition(bool, bool) const; + virtual int baselinePosition(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; virtual void computePreferredLogicalWidths(); virtual void layout(); virtual void updateFromElement(); diff --git a/WebCore/rendering/RenderTable.cpp b/WebCore/rendering/RenderTable.cpp index ca33036..117a6ae 100644 --- a/WebCore/rendering/RenderTable.cpp +++ b/WebCore/rendering/RenderTable.cpp @@ -71,6 +71,10 @@ RenderTable::RenderTable(Node* node) #endif } +RenderTable::~RenderTable() +{ +} + void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); @@ -791,7 +795,7 @@ int RenderTable::calcBorderLeft() const if (tb.style() > BHIDDEN) borderWidth = tb.width(); - int leftmostColumn = style()->direction() == RTL ? numEffCols() - 1 : 0; + int leftmostColumn = !style()->isLeftToRightDirection() ? numEffCols() - 1 : 0; RenderTableCol* colGroup = colElement(leftmostColumn); if (colGroup) { const BorderValue& gb = style()->borderLeft(); @@ -850,7 +854,7 @@ int RenderTable::calcBorderRight() const if (tb.style() > BHIDDEN) borderWidth = tb.width(); - int rightmostColumn = style()->direction() == RTL ? 0 : numEffCols() - 1; + int rightmostColumn = !style()->isLeftToRightDirection() ? 0 : numEffCols() - 1; RenderTableCol* colGroup = colElement(rightmostColumn); if (colGroup) { const BorderValue& gb = style()->borderRight(); @@ -1186,7 +1190,7 @@ bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu ty += y(); // Check kids first. - if (!hasOverflowClip() || overflowClipRect(tx, ty).intersects(result.rectFromPoint(xPos, yPos))) { + if (!hasOverflowClip() || overflowClipRect(tx, ty).intersects(result.rectForPoint(xPos, yPos))) { for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption) && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) { @@ -1198,7 +1202,7 @@ bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu // Check our bounds next. IntRect boundsRect = IntRect(tx, ty, width(), height()); - if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && boundsRect.intersects(result.rectFromPoint(xPos, yPos))) { + if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && boundsRect.intersects(result.rectForPoint(xPos, yPos))) { updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); if (!result.addNodeToRectBasedTestResult(node(), xPos, yPos, boundsRect)) return true; diff --git a/WebCore/rendering/RenderTable.h b/WebCore/rendering/RenderTable.h index 58d4915..beb48f4 100644 --- a/WebCore/rendering/RenderTable.h +++ b/WebCore/rendering/RenderTable.h @@ -38,7 +38,8 @@ class TableLayout; class RenderTable : public RenderBlock { public: - RenderTable(Node*); + explicit RenderTable(Node*); + virtual ~RenderTable(); int getColumnPos(int col) const { return m_columnPos[col]; } diff --git a/WebCore/rendering/RenderTableCell.cpp b/WebCore/rendering/RenderTableCell.cpp index 06f4726..88cdd5e 100644 --- a/WebCore/rendering/RenderTableCell.cpp +++ b/WebCore/rendering/RenderTableCell.cpp @@ -174,34 +174,34 @@ void RenderTableCell::layout() int RenderTableCell::paddingTop(bool includeIntrinsicPadding) const { int result = RenderBlock::paddingTop(); - if (!includeIntrinsicPadding || !style()->isVerticalBlockFlow()) + if (!includeIntrinsicPadding || !style()->isHorizontalWritingMode()) return result; - return result + (style()->blockFlow() == TopToBottomBlockFlow ? intrinsicPaddingBefore() : intrinsicPaddingAfter()); + return result + (style()->writingMode() == TopToBottomWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter()); } int RenderTableCell::paddingBottom(bool includeIntrinsicPadding) const { int result = RenderBlock::paddingBottom(); - if (!includeIntrinsicPadding || !style()->isVerticalBlockFlow()) + if (!includeIntrinsicPadding || !style()->isHorizontalWritingMode()) return result; - return result + (style()->blockFlow() == TopToBottomBlockFlow ? intrinsicPaddingAfter() : intrinsicPaddingBefore()); + return result + (style()->writingMode() == TopToBottomWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore()); } int RenderTableCell::paddingLeft(bool includeIntrinsicPadding) const { int result = RenderBlock::paddingLeft(); - if (!includeIntrinsicPadding || style()->isVerticalBlockFlow()) + if (!includeIntrinsicPadding || style()->isHorizontalWritingMode()) return result; - return result + (style()->blockFlow() == LeftToRightBlockFlow ? intrinsicPaddingBefore() : intrinsicPaddingAfter()); + return result + (style()->writingMode() == LeftToRightWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter()); } int RenderTableCell::paddingRight(bool includeIntrinsicPadding) const { int result = RenderBlock::paddingRight(); - if (!includeIntrinsicPadding || style()->isVerticalBlockFlow()) + if (!includeIntrinsicPadding || style()->isHorizontalWritingMode()) return result; - return result + (style()->blockFlow() == LeftToRightBlockFlow ? intrinsicPaddingAfter() : intrinsicPaddingBefore()); + return result + (style()->writingMode() == LeftToRightWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore()); } int RenderTableCell::paddingBefore(bool includeIntrinsicPadding) const @@ -246,7 +246,7 @@ IntRect RenderTableCell::clippedOverflowRectForRepaint(RenderBoxModelObject* rep if (!table()->collapseBorders() || table()->needsSectionRecalc()) return RenderBlock::clippedOverflowRectForRepaint(repaintContainer); - bool rtl = table()->style()->direction() == RTL; + bool rtl = !table()->style()->isLeftToRightDirection(); int outlineSize = style()->outlineSize(); int left = max(borderHalfLeft(true), outlineSize); int right = max(borderHalfRight(true), outlineSize); @@ -300,10 +300,11 @@ void RenderTableCell::computeRectForRepaint(RenderBoxModelObject* repaintContain RenderBlock::computeRectForRepaint(repaintContainer, r, fixed); } -int RenderTableCell::baselinePosition(bool firstLine, bool isRootLineBox) const +int RenderTableCell::baselinePosition(bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const { - if (isRootLineBox) - return RenderBox::baselinePosition(firstLine, isRootLineBox); + // FIXME: This function still needs to be patched for writing-mode. + if (linePositionMode == PositionOfInteriorLineBoxes) + return RenderBlock::baselinePosition(firstLine, lineDirection, linePositionMode); // <http://www.w3.org/TR/2007/CR-CSS21-20070719/tables.html#height-layout>: The baseline of a cell is the baseline of // the first in-flow line box in the cell, or the first in-flow table-row in the cell, whichever comes first. If there @@ -723,7 +724,7 @@ int RenderTableCell::borderAfter() const int RenderTableCell::borderHalfLeft(bool outer) const { - CollapsedBorderValue border = collapsedLeftBorder(table()->style()->direction() == RTL); + CollapsedBorderValue border = collapsedLeftBorder(!table()->style()->isLeftToRightDirection()); if (border.exists()) return (border.width() + (outer ? 0 : 1)) / 2; // Give the extra pixel to top and left. return 0; @@ -731,7 +732,7 @@ int RenderTableCell::borderHalfLeft(bool outer) const int RenderTableCell::borderHalfRight(bool outer) const { - CollapsedBorderValue border = collapsedRightBorder(table()->style()->direction() == RTL); + CollapsedBorderValue border = collapsedRightBorder(!table()->style()->isLeftToRightDirection()); if (border.exists()) return (border.width() + (outer ? 1 : 0)) / 2; return 0; @@ -843,7 +844,7 @@ static void addBorderStyle(RenderTableCell::CollapsedBorderStyles& borderStyles, void RenderTableCell::collectBorderStyles(CollapsedBorderStyles& borderStyles) const { - bool rtl = table()->style()->direction() == RTL; + bool rtl = !table()->style()->isLeftToRightDirection(); addBorderStyle(borderStyles, collapsedLeftBorder(rtl)); addBorderStyle(borderStyles, collapsedRightBorder(rtl)); addBorderStyle(borderStyles, collapsedTopBorder()); @@ -870,7 +871,7 @@ void RenderTableCell::paintCollapsedBorder(GraphicsContext* graphicsContext, int if (!table()->currentBorderStyle()) return; - bool rtl = table()->style()->direction() == RTL; + bool rtl = !table()->style()->isLeftToRightDirection(); CollapsedBorderValue leftVal = collapsedLeftBorder(rtl); CollapsedBorderValue rightVal = collapsedRightBorder(rtl); CollapsedBorderValue topVal = collapsedTopBorder(); diff --git a/WebCore/rendering/RenderTableCell.h b/WebCore/rendering/RenderTableCell.h index 79376e9..31879d6 100644 --- a/WebCore/rendering/RenderTableCell.h +++ b/WebCore/rendering/RenderTableCell.h @@ -31,7 +31,7 @@ namespace WebCore { class RenderTableCell : public RenderBlock { public: - RenderTableCell(Node*); + explicit RenderTableCell(Node*); // FIXME: need to implement cellIndex int cellIndex() const { return 0; } @@ -88,7 +88,7 @@ public: void paintBackgroundsBehindCell(PaintInfo&, int tx, int ty, RenderObject* backgroundObject); - virtual int baselinePosition(bool firstLine = false, bool isRootLineBox = false) const; + virtual int baselinePosition(bool firstLine = false, LineDirectionMode = HorizontalLine, LinePositionMode = PositionOnContainingLine) const; void setIntrinsicPaddingBefore(int p) { m_intrinsicPaddingBefore = p; } void setIntrinsicPaddingAfter(int p) { m_intrinsicPaddingAfter = p; } diff --git a/WebCore/rendering/RenderTableCol.h b/WebCore/rendering/RenderTableCol.h index c5f9afc..8255937 100644 --- a/WebCore/rendering/RenderTableCol.h +++ b/WebCore/rendering/RenderTableCol.h @@ -34,7 +34,7 @@ class RenderTable; class RenderTableCol : public RenderBox { public: - RenderTableCol(Node*); + explicit RenderTableCol(Node*); const RenderObjectChildList* children() const { return &m_children; } RenderObjectChildList* children() { return &m_children; } @@ -50,7 +50,6 @@ private: virtual const char* renderName() const { return "RenderTableCol"; } virtual bool isTableCol() const { return true; } - virtual int lineHeight(bool) const { return 0; } virtual void updateFromElement(); virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; diff --git a/WebCore/rendering/RenderTableRow.h b/WebCore/rendering/RenderTableRow.h index 588252b..20aa424 100644 --- a/WebCore/rendering/RenderTableRow.h +++ b/WebCore/rendering/RenderTableRow.h @@ -31,7 +31,7 @@ namespace WebCore { class RenderTableRow : public RenderBox { public: - RenderTableRow(Node*); + explicit RenderTableRow(Node*); const RenderObjectChildList* children() const { return &m_children; } RenderObjectChildList* children() { return &m_children; } @@ -50,7 +50,6 @@ private: virtual void destroy(); virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); - virtual int lineHeight(bool, bool) const { return 0; } virtual void layout(); virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); diff --git a/WebCore/rendering/RenderTableSection.cpp b/WebCore/rendering/RenderTableSection.cpp index 37f2025..e9aa3cb 100644 --- a/WebCore/rendering/RenderTableSection.cpp +++ b/WebCore/rendering/RenderTableSection.cpp @@ -692,7 +692,7 @@ int RenderTableSection::layoutRows(int toAdd) IntRect oldCellRect(cell->x(), cell->y() , cell->width(), cell->height()); - if (style()->direction() == RTL) + if (!style()->isLeftToRightDirection()) cell->setLocation(table()->columnPositions()[nEffCols] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + hspacing, m_rowPos[rindx]); else cell->setLocation(table()->columnPositions()[c] + hspacing, m_rowPos[rindx]); @@ -748,9 +748,27 @@ int RenderTableSection::layoutRows(int toAdd) return height(); } -int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf) const +int RenderTableSection::topmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const { - int bottom = RenderBox::lowestPosition(includeOverflowInterior, includeSelf); + int top = RenderBox::topmostPosition(includeOverflowInterior, includeSelf, applyTransform); + if (!includeOverflowInterior && hasOverflowClip()) + return top; + + for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { + for (RenderObject* curr = row->firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isTableCell()) { + RenderTableCell* cell = toRenderTableCell(curr); + top = min(top, cell->transformedFrameRect().y() + cell->topmostPosition(false)); + } + } + } + + return top; +} + +int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const +{ + int bottom = RenderBox::lowestPosition(includeOverflowInterior, includeSelf, applyTransform); if (!includeOverflowInterior && hasOverflowClip()) return bottom; @@ -758,7 +776,7 @@ int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includ for (RenderObject* curr = row->firstChild(); curr; curr = curr->nextSibling()) { if (curr->isTableCell()) { RenderTableCell* cell = toRenderTableCell(curr); - bottom = max(bottom, cell->y() + cell->lowestPosition(false)); + bottom = max(bottom, cell->transformedFrameRect().y() + cell->lowestPosition(false)); } } } @@ -766,9 +784,9 @@ int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includ return bottom; } -int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const +int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const { - int right = RenderBox::rightmostPosition(includeOverflowInterior, includeSelf); + int right = RenderBox::rightmostPosition(includeOverflowInterior, includeSelf, applyTransform); if (!includeOverflowInterior && hasOverflowClip()) return right; @@ -776,7 +794,7 @@ int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool inc for (RenderObject* curr = row->firstChild(); curr; curr = curr->nextSibling()) { if (curr->isTableCell()) { RenderTableCell* cell = toRenderTableCell(curr); - right = max(right, cell->x() + cell->rightmostPosition(false)); + right = max(right, cell->transformedFrameRect().x() + cell->rightmostPosition(false)); } } } @@ -784,9 +802,9 @@ int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool inc return right; } -int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const +int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform applyTransform) const { - int left = RenderBox::leftmostPosition(includeOverflowInterior, includeSelf); + int left = RenderBox::leftmostPosition(includeOverflowInterior, includeSelf, applyTransform); if (!includeOverflowInterior && hasOverflowClip()) return left; @@ -794,7 +812,7 @@ int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool incl for (RenderObject* curr = row->firstChild(); curr; curr = curr->nextSibling()) { if (curr->isTableCell()) { RenderTableCell* cell = toRenderTableCell(curr); - left = min(left, cell->x() + cell->leftmostPosition(false)); + left = min(left, cell->transformedFrameRect().x() + cell->leftmostPosition(false)); } } } @@ -998,7 +1016,7 @@ int RenderTableSection::calcOuterBorderRight(bool rtl) const void RenderTableSection::recalcOuterBorder() { - bool rtl = table()->style()->direction() == RTL; + bool rtl = !table()->style()->isLeftToRightDirection(); m_outerBorderTop = calcOuterBorderTop(); m_outerBorderBottom = calcOuterBorderBottom(); m_outerBorderLeft = calcOuterBorderLeft(rtl); @@ -1136,9 +1154,9 @@ void RenderTableSection::paintObject(PaintInfo& paintInfo, int tx, int ty) if (startrow == m_rowPos.size() || (startrow > 0 && (m_rowPos[startrow] > top))) --startrow; - int bottom = relativeY + h + os - 1; + int bottom = relativeY + h + os; endrow = std::lower_bound(m_rowPos.begin(), m_rowPos.end(), bottom) - m_rowPos.begin(); - if ((endrow == m_rowPos.size()) || (endrow > 0 && m_rowPos[endrow - 1] == bottom)) + if (endrow == m_rowPos.size()) --endrow; if (!endrow && ty + m_rowPos[0] - table()->outerBorderTop() <= y + h + os) @@ -1147,7 +1165,7 @@ void RenderTableSection::paintObject(PaintInfo& paintInfo, int tx, int ty) unsigned startcol = 0; unsigned endcol = totalCols; // FIXME: Implement RTL. - if (!m_hasOverflowingCell && style()->direction() == LTR) { + if (!m_hasOverflowingCell && style()->isLeftToRightDirection()) { int relativeX = x - tx; int left = relativeX - os; Vector<int>& columnPos = table()->columnPositions(); @@ -1155,9 +1173,9 @@ void RenderTableSection::paintObject(PaintInfo& paintInfo, int tx, int ty) if ((startcol == columnPos.size()) || (startcol > 0 && (columnPos[startcol] > left))) --startcol; - int right = relativeX + w + os - 1; + int right = relativeX + w + os; endcol = std::lower_bound(columnPos.begin(), columnPos.end(), right) - columnPos.begin(); - if (endcol == columnPos.size() || (endcol > 0 && (columnPos[endcol - 1] == right))) + if (endcol == columnPos.size()) --endcol; if (!endcol && tx + table()->columnPositions()[0] - table()->outerBorderLeft() <= y + w + os) @@ -1305,7 +1323,7 @@ bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResul tx += x(); ty += y(); - if (hasOverflowClip() && !overflowClipRect(tx, ty).intersects(result.rectFromPoint(xPos, yPos))) + if (hasOverflowClip() && !overflowClipRect(tx, ty).intersects(result.rectForPoint(xPos, yPos))) return false; if (m_hasOverflowingCell) { @@ -1332,7 +1350,7 @@ bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResul --leftrow; Vector<int>& columnPos = table()->columnPositions(); - bool rtl = style()->direction() == RTL; + bool rtl = !style()->isLeftToRightDirection(); int relativeX = xPos - tx; if (rtl) relativeX = columnPos[columnPos.size() - 1] - relativeX; diff --git a/WebCore/rendering/RenderTableSection.h b/WebCore/rendering/RenderTableSection.h index 6d2f752..fce1e0f 100644 --- a/WebCore/rendering/RenderTableSection.h +++ b/WebCore/rendering/RenderTableSection.h @@ -136,9 +136,10 @@ private: virtual void removeChild(RenderObject* oldChild); - virtual int lowestPosition(bool includeOverflowInterior, bool includeSelf) const; - virtual int rightmostPosition(bool includeOverflowInterior, bool includeSelf) const; - virtual int leftmostPosition(bool includeOverflowInterior, bool includeSelf) const; + virtual int topmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform = IncludeTransform) const; + virtual int lowestPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform = IncludeTransform) const; + virtual int rightmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform = IncludeTransform) const; + virtual int leftmostPosition(bool includeOverflowInterior, bool includeSelf, ApplyTransform = IncludeTransform) const; virtual void paint(PaintInfo&, int tx, int ty); virtual void paintCell(RenderTableCell*, PaintInfo&, int tx, int ty); @@ -148,8 +149,6 @@ private: virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); - virtual int lineHeight(bool, bool) const { return 0; } - bool ensureRows(int); void clearGrid(); diff --git a/WebCore/rendering/RenderText.cpp b/WebCore/rendering/RenderText.cpp index fada8b4..510d830 100644 --- a/WebCore/rendering/RenderText.cpp +++ b/WebCore/rendering/RenderText.cpp @@ -511,7 +511,7 @@ IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* e switch (cbStyle->textAlign()) { case TAAUTO: case JUSTIFY: - rightAligned = cbStyle->direction() == RTL; + rightAligned = !cbStyle->isLeftToRightDirection(); break; case RIGHT: case WEBKIT_RIGHT: @@ -1084,35 +1084,6 @@ void RenderText::setTextInternal(PassRefPtr<StringImpl> text) } ASSERT(m_text); -#if ENABLE(SVG) - if (isSVGInlineText()) { - if (style() && style()->whiteSpace() == PRE) { - // Spec: When xml:space="preserve", the SVG user agent will do the following using a - // copy of the original character data content. It will convert all newline and tab - // characters into space characters. Then, it will draw all space characters, including - // leading, trailing and multiple contiguous space characters. - - m_text.replace('\n', ' '); - - // If xml:space="preserve" is set, white-space is set to "pre", which - // preserves leading, trailing & contiguous space character for us. - } else { - // Spec: When xml:space="default", the SVG user agent will do the following using a - // copy of the original character data content. First, it will remove all newline - // characters. Then it will convert all tab characters into space characters. - // Then, it will strip off all leading and trailing space characters. - // Then, all contiguous space characters will be consolidated. - - m_text.replace('\n', StringImpl::empty()); - - // If xml:space="default" is set, white-space is set to "nowrap", which handles - // leading, trailing & contiguous space character removal for us. - } - - m_text.replace('\t', ' '); - } -#endif - if (style()) { transformText(m_text); @@ -1168,12 +1139,6 @@ String RenderText::textWithoutTranscoding() const return text; } -int RenderText::lineHeight(bool firstLine, bool) const -{ - // Always use the interior line height of the parent (e.g., if our parent is an inline block). - return parent()->lineHeight(firstLine, true); -} - void RenderText::dirtyLineBoxes(bool fullLayout) { if (fullLayout) @@ -1224,7 +1189,7 @@ void RenderText::positionLineBox(InlineBox* box) return; } - m_containsReversedText |= s->direction() == RTL; + m_containsReversedText |= !s->isLeftToRightDirection(); } unsigned RenderText::width(unsigned from, unsigned len, int xPos, bool firstLine, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const diff --git a/WebCore/rendering/RenderText.h b/WebCore/rendering/RenderText.h index e3a6997..964a1d3 100644 --- a/WebCore/rendering/RenderText.h +++ b/WebCore/rendering/RenderText.h @@ -74,8 +74,6 @@ public: virtual unsigned width(unsigned from, unsigned len, const Font&, int xPos, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const; virtual unsigned width(unsigned from, unsigned len, int xPos, bool firstLine = false, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const; - virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; - virtual int minPreferredLogicalWidth() const; virtual int maxPreferredLogicalWidth() const; diff --git a/WebCore/rendering/RenderTextControl.cpp b/WebCore/rendering/RenderTextControl.cpp index f48081b..4855bab 100644 --- a/WebCore/rendering/RenderTextControl.cpp +++ b/WebCore/rendering/RenderTextControl.cpp @@ -262,10 +262,12 @@ void RenderTextControl::setSelectionRange(int start, int end) frame->selection()->setSelection(newSelection); } -VisibleSelection RenderTextControl::selection(int start, int end) const +PassRefPtr<Range> RenderTextControl::selection(int start, int end) const { - return VisibleSelection(VisiblePosition(m_innerText.get(), start, VP_DEFAULT_AFFINITY), - VisiblePosition(m_innerText.get(), end, VP_DEFAULT_AFFINITY)); + if (!m_innerText) + return 0; + + return Range::create(document(), m_innerText, start, m_innerText, end); } VisiblePosition RenderTextControl::visiblePositionForIndex(int index) @@ -407,7 +409,7 @@ void RenderTextControl::computeLogicalHeight() m_innerText->renderBox()->paddingTop() + m_innerText->renderBox()->paddingBottom() + m_innerText->renderBox()->marginTop() + m_innerText->renderBox()->marginBottom()); - adjustControlHeightBasedOnLineHeight(m_innerText->renderer()->lineHeight(true, true)); + adjustControlHeightBasedOnLineHeight(m_innerText->renderBox()->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes)); setHeight(height() + 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. diff --git a/WebCore/rendering/RenderTextControl.h b/WebCore/rendering/RenderTextControl.h index a33f11d..84d7b0b 100644 --- a/WebCore/rendering/RenderTextControl.h +++ b/WebCore/rendering/RenderTextControl.h @@ -46,7 +46,7 @@ public: void setSelectionEnd(int); void select(); void setSelectionRange(int start, int end); - VisibleSelection selection(int start, int end) const; + PassRefPtr<Range> selection(int start, int end) const; virtual void subtreeHasChanged(); String text(); diff --git a/WebCore/rendering/RenderTextControlMultiLine.cpp b/WebCore/rendering/RenderTextControlMultiLine.cpp index d3fe3f6..2c93164 100644 --- a/WebCore/rendering/RenderTextControlMultiLine.cpp +++ b/WebCore/rendering/RenderTextControlMultiLine.cpp @@ -102,9 +102,9 @@ void RenderTextControlMultiLine::adjustControlHeightBasedOnLineHeight(int lineHe setHeight(height() + lineHeight * static_cast<HTMLTextAreaElement*>(node())->rows()); } -int RenderTextControlMultiLine::baselinePosition(bool, bool) const +int RenderTextControlMultiLine::baselinePosition(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const { - return height() + marginTop() + marginBottom(); + return RenderBox::baselinePosition(firstLine, direction, linePositionMode); } void RenderTextControlMultiLine::updateFromElement() diff --git a/WebCore/rendering/RenderTextControlMultiLine.h b/WebCore/rendering/RenderTextControlMultiLine.h index fbca308..e061e89 100644 --- a/WebCore/rendering/RenderTextControlMultiLine.h +++ b/WebCore/rendering/RenderTextControlMultiLine.h @@ -43,7 +43,7 @@ private: virtual float getAvgCharWidth(AtomicString family); virtual int preferredContentWidth(float charWidth) const; virtual void adjustControlHeightBasedOnLineHeight(int lineHeight); - virtual int baselinePosition(bool firstLine, bool isRootLineBox) const; + virtual int baselinePosition(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; virtual void updateFromElement(); virtual void cacheSelection(int start, int end); diff --git a/WebCore/rendering/RenderTextControlSingleLine.cpp b/WebCore/rendering/RenderTextControlSingleLine.cpp index 7edbe7f..ea03c61 100644 --- a/WebCore/rendering/RenderTextControlSingleLine.cpp +++ b/WebCore/rendering/RenderTextControlSingleLine.cpp @@ -372,7 +372,7 @@ void RenderTextControlSingleLine::forwardEvent(Event* event) if (event->type() == eventNames().blurEvent) { if (innerTextRenderer) { if (RenderLayer* innerLayer = innerTextRenderer->layer()) - innerLayer->scrollToOffset(style()->direction() == RTL ? innerLayer->scrollWidth() : 0, 0); + innerLayer->scrollToOffset(!style()->isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0); } capsLockStateMayHaveChanged(); @@ -687,11 +687,18 @@ void RenderTextControlSingleLine::updateFromElement() } else { if (!inputElement()->suggestedValue().isNull()) setInnerTextValue(inputElement()->suggestedValue()); - else if (!node()->isHTMLElement() || !static_cast<HTMLInputElement*>(node())->formControlValueMatchesRenderer()) - // For HTMLInputElement, update the renderer value only if the - // formControlValueMatchesRenderer() flag is false. It protects an - // unacceptable renderer value from being overwritten with the DOM value. - setInnerTextValue(inputElement()->value()); + else { + bool shouldUpdateValue = true; + if (node()->isHTMLElement()) { + // For HTMLInputElement, update the renderer value if the element + // supports placeholder or the formControlValueMatchesRenderer() + // flag is false. It protects an unacceptable renderer value from + // being overwritten with the DOM value. + shouldUpdateValue = static_cast<HTMLTextFormControlElement*>(node())->supportsPlaceholder() || !static_cast<HTMLInputElement*>(node())->formControlValueMatchesRenderer(); + } + if (shouldUpdateValue) + setInnerTextValue(inputElement()->value()); + } } if (m_searchPopupIsVisible) @@ -723,7 +730,7 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const textBlockStyle->setOverflowY(OHIDDEN); // Do not allow line-height to be smaller than our default. - if (textBlockStyle->font().lineSpacing() > lineHeight(true, true)) + if (textBlockStyle->font().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes)) textBlockStyle->setLineHeight(Length(-100.0f, Percent)); WebCore::EDisplay display = (m_innerBlock || inputElement()->hasSpinButton() ? INLINE_BLOCK : BLOCK); diff --git a/WebCore/rendering/RenderTextFragment.cpp b/WebCore/rendering/RenderTextFragment.cpp index b14308d..705c095 100644 --- a/WebCore/rendering/RenderTextFragment.cpp +++ b/WebCore/rendering/RenderTextFragment.cpp @@ -44,6 +44,10 @@ RenderTextFragment::RenderTextFragment(Node* node, StringImpl* str) { } +RenderTextFragment::~RenderTextFragment() +{ +} + PassRefPtr<StringImpl> RenderTextFragment::originalText() const { Node* e = node(); diff --git a/WebCore/rendering/RenderTextFragment.h b/WebCore/rendering/RenderTextFragment.h index e023042..abb3fa4 100644 --- a/WebCore/rendering/RenderTextFragment.h +++ b/WebCore/rendering/RenderTextFragment.h @@ -35,6 +35,7 @@ class RenderTextFragment : public RenderText { public: RenderTextFragment(Node*, StringImpl*, int startOffset, int length); RenderTextFragment(Node*, StringImpl*); + virtual ~RenderTextFragment(); virtual bool isTextFragment() const { return true; } diff --git a/WebCore/rendering/RenderThemeMac.mm b/WebCore/rendering/RenderThemeMac.mm index 7982834..b632d9a 100644 --- a/WebCore/rendering/RenderThemeMac.mm +++ b/WebCore/rendering/RenderThemeMac.mm @@ -905,7 +905,7 @@ NSLevelIndicatorCell* RenderThemeMac::levelIndicatorFor(const RenderMeter* rende } [cell setLevelIndicatorStyle:levelIndicatorStyleFor(style->appearance())]; - [cell setBaseWritingDirection:style->direction() == LTR ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft]; + [cell setBaseWritingDirection:style->isLeftToRightDirection() ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft]; [cell setMinValue:element->min()]; [cell setMaxValue:element->max()]; RetainPtr<NSNumber> valueObject = [NSNumber numberWithDouble:value]; @@ -959,12 +959,12 @@ bool RenderThemeMac::paintProgressBar(RenderObject* renderObject, const PaintInf paintInfo.context->save(); - if (renderProgress->style()->direction() == RTL) { + if (!renderProgress->style()->isLeftToRightDirection()) { paintInfo.context->translate(2 * rect.x() + rect.width(), 0); paintInfo.context->scale(FloatSize(-1, 1)); } - paintInfo.context->drawImageBuffer(imageBuffer.get(), DeviceColorSpace, rect.location()); + paintInfo.context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, rect.location()); paintInfo.context->restore(); return false; @@ -1141,11 +1141,11 @@ bool RenderThemeMac::paintMenuListButton(RenderObject* o, const PaintInfo& paint // 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, DeviceColorSpace); + paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), IntPoint(leftEdgeOfSeparator, bounds.bottom())); - paintInfo.context->setStrokeColor(rightSeparatorColor, DeviceColorSpace); + paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom())); diff --git a/WebCore/rendering/RenderThemeSafari.cpp b/WebCore/rendering/RenderThemeSafari.cpp index 07b8eb8..8d66ba7 100644 --- a/WebCore/rendering/RenderThemeSafari.cpp +++ b/WebCore/rendering/RenderThemeSafari.cpp @@ -830,8 +830,8 @@ bool RenderThemeSafari::paintMenuListButton(RenderObject* o, const PaintInfo& pa paintInfo.context->save(); - paintInfo.context->setFillColor(o->style()->visitedDependentColor(CSSPropertyColor), DeviceColorSpace); - paintInfo.context->setStrokeColor(NoStroke, DeviceColorSpace); + paintInfo.context->setFillColor(o->style()->visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB); + paintInfo.context->setStrokeColor(NoStroke, ColorSpaceDeviceRGB); FloatPoint arrow[3]; arrow[0] = FloatPoint(leftEdge, centerY - arrowHeight / 2.0f); @@ -851,11 +851,11 @@ bool RenderThemeSafari::paintMenuListButton(RenderObject* o, const PaintInfo& pa // Draw the separator to the left of the arrows paintInfo.context->setStrokeThickness(1.0f); paintInfo.context->setStrokeStyle(SolidStroke); - paintInfo.context->setStrokeColor(leftSeparatorColor, DeviceColorSpace); + paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), IntPoint(leftEdgeOfSeparator, bounds.bottom())); - paintInfo.context->setStrokeColor(rightSeparatorColor, DeviceColorSpace); + paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom())); diff --git a/WebCore/rendering/RenderThemeWin.cpp b/WebCore/rendering/RenderThemeWin.cpp index b4fb8eb..f0f8268 100644 --- a/WebCore/rendering/RenderThemeWin.cpp +++ b/WebCore/rendering/RenderThemeWin.cpp @@ -92,6 +92,21 @@ #define PBS_DISABLED 4 #define PBS_DEFAULTED 5 +// Spin button parts +#define SPNP_UP 1 +#define SPNP_DOWN 2 + +// Spin button states +#define DNS_NORMAL 1 +#define DNS_HOT 2 +#define DNS_PRESSED 3 +#define DNS_DISABLED 4 +#define UPS_NORMAL 1 +#define UPS_HOT 2 +#define UPS_PRESSED 3 +#define UPS_DISABLED 4 + + SOFT_LINK_LIBRARY(uxtheme) SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList)) SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme)) @@ -154,6 +169,7 @@ RenderThemeWin::RenderThemeWin() , m_textFieldTheme(0) , m_menuListTheme(0) , m_sliderTheme(0) + , m_spinButtonTheme(0) { haveTheme = uxthemeLibrary() && IsThemeActive(); } @@ -194,6 +210,13 @@ HANDLE RenderThemeWin::sliderTheme() const return m_sliderTheme; } +HANDLE RenderThemeWin::spinButtonTheme() const +{ + if (haveTheme && !m_spinButtonTheme) + m_spinButtonTheme = OpenThemeData(0, L"Spin"); + return m_spinButtonTheme; +} + void RenderThemeWin::close() { // This method will need to be called when the OS theme changes to flush our cached themes. @@ -205,7 +228,9 @@ void RenderThemeWin::close() CloseThemeData(m_menuListTheme); if (m_sliderTheme) CloseThemeData(m_sliderTheme); - m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = 0; + if (m_spinButtonTheme) + CloseThemeData(m_spinButtonTheme); + m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = m_spinButtonTheme = 0; haveTheme = uxthemeLibrary() && IsThemeActive(); } @@ -368,7 +393,7 @@ bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const return supportsFocus(style->appearance()); } -unsigned RenderThemeWin::determineClassicState(RenderObject* o) +unsigned RenderThemeWin::determineClassicState(RenderObject* o, ControlSubPart subPart) { unsigned state = 0; switch (o->style()->appearance()) { @@ -397,6 +422,18 @@ unsigned RenderThemeWin::determineClassicState(RenderObject* o) state |= DFCS_INACTIVE; else if (isPressed(o)) state |= DFCS_PUSHED; + break; + case InnerSpinButtonPart: { + bool isUpButton = subPart == SpinButtonUp; + state = isUpButton ? DFCS_SCROLLUP : DFCS_SCROLLDOWN; + if (!isEnabled(o) || isReadOnlyControl(o)) + state |= DFCS_INACTIVE; + else if (isPressed(o) && isUpButton == isSpinUpButtonPartPressed(o)) + state |= DFCS_PUSHED; + else if (isHovered(o) && isUpButton == isSpinUpButtonPartHovered(o)) + state |= DFCS_HOT; + break; + } default: break; } @@ -452,7 +489,20 @@ unsigned RenderThemeWin::determineButtonState(RenderObject* o) return result; } -ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o) +unsigned RenderThemeWin::determineSpinButtonState(RenderObject* o, ControlSubPart subPart) +{ + bool isUpButton = subPart == SpinButtonUp; + unsigned result = isUpButton ? UPS_NORMAL : DNS_NORMAL; + if (!isEnabled(o) || isReadOnlyControl(o)) + result = isUpButton ? UPS_DISABLED : DNS_DISABLED; + else if (isPressed(o) && isUpButton == isSpinUpButtonPartPressed(o)) + result = isUpButton ? UPS_PRESSED : DNS_PRESSED; + else if (isHovered(o) && isUpButton == isSpinUpButtonPartHovered(o)) + result = isUpButton ? UPS_HOT : DNS_HOT; + return result; +} + +ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o, ControlSubPart subPart) { ThemeData result; switch (o->style()->appearance()) { @@ -490,16 +540,20 @@ ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o) result.m_part = TKP_THUMBRIGHT; result.m_state = determineSliderThumbState(o); break; + case InnerSpinButtonPart: + result.m_part = DFC_SCROLL; + result.m_state = determineClassicState(o, subPart); + break; default: break; } return result; } -ThemeData RenderThemeWin::getThemeData(RenderObject* o) +ThemeData RenderThemeWin::getThemeData(RenderObject* o, ControlSubPart subPart) { if (!haveTheme) - return getClassicThemeData(o); + return getClassicThemeData(o, subPart); ThemeData result; switch (o->style()->appearance()) { @@ -549,6 +603,10 @@ ThemeData RenderThemeWin::getThemeData(RenderObject* o) result.m_part = TKP_THUMBRIGHT; result.m_state = determineSliderThumbState(o); break; + case InnerSpinButtonPart: + result.m_part = subPart == SpinButtonUp ? SPNP_UP : SPNP_DOWN; + result.m_state = determineSpinButtonState(o, subPart); + break; } return result; @@ -618,6 +676,31 @@ bool RenderThemeWin::paintButton(RenderObject* o, const PaintInfo& i, const IntR return false; } +void RenderThemeWin::adjustInnerSpinButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + int width = ::GetSystemMetrics(SM_CXVSCROLL); + if (width <= 0) + width = 17; // Vista's default. + style->setWidth(Length(width, Fixed)); + style->setMinWidth(Length(width, Fixed)); +} + +bool RenderThemeWin::paintInnerSpinButton(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + // We split the specified rectangle into two vertically. We can't draw a + // spin button of which height is less than 2px. + if (r.height() < 2) + return false; + IntRect upRect(r); + upRect.setHeight(r.height() / 2); + IntRect downRect(r); + downRect.setY(upRect.bottom()); + downRect.setHeight(r.height() - upRect.height()); + drawControl(i.context, o, spinButtonTheme(), getThemeData(o, SpinButtonUp), upRect); + drawControl(i.context, o, spinButtonTheme(), getThemeData(o, SpinButtonDown), downRect); + return false; +} + void RenderThemeWin::setCheckboxSize(RenderStyle* style) const { // If the width and height are both specified, then we have nothing to do. diff --git a/WebCore/rendering/RenderThemeWin.h b/WebCore/rendering/RenderThemeWin.h index 1efb117..c05d2ee 100644 --- a/WebCore/rendering/RenderThemeWin.h +++ b/WebCore/rendering/RenderThemeWin.h @@ -77,6 +77,9 @@ public: virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&); virtual bool paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) @@ -139,6 +142,12 @@ public: #endif private: + enum ControlSubPart { + None, + SpinButtonDown, + SpinButtonUp, + }; + RenderThemeWin(); ~RenderThemeWin(); @@ -146,24 +155,27 @@ private: void close(); unsigned determineState(RenderObject*); - unsigned determineClassicState(RenderObject*); + unsigned determineClassicState(RenderObject*, ControlSubPart = None); unsigned determineSliderThumbState(RenderObject*); unsigned determineButtonState(RenderObject*); + unsigned determineSpinButtonState(RenderObject*, ControlSubPart = None); bool supportsFocus(ControlPart) const; - ThemeData getThemeData(RenderObject*); - ThemeData getClassicThemeData(RenderObject* o); + ThemeData getThemeData(RenderObject*, ControlSubPart = None); + ThemeData getClassicThemeData(RenderObject* o, ControlSubPart = None); HANDLE buttonTheme() const; HANDLE textFieldTheme() const; HANDLE menuListTheme() const; HANDLE sliderTheme() const; + HANDLE spinButtonTheme() const; mutable HANDLE m_buttonTheme; mutable HANDLE m_textFieldTheme; mutable HANDLE m_menuListTheme; mutable HANDLE m_sliderTheme; + mutable HANDLE m_spinButtonTheme; }; }; diff --git a/WebCore/rendering/RenderThemeWinCE.cpp b/WebCore/rendering/RenderThemeWinCE.cpp index 66cda11..27b8783 100644 --- a/WebCore/rendering/RenderThemeWinCE.cpp +++ b/WebCore/rendering/RenderThemeWinCE.cpp @@ -378,12 +378,12 @@ bool RenderThemeWinCE::paintSearchFieldCancelButton(RenderObject* o, const Paint IntRect cancelBounds(IntPoint(x, y), cancelSize); paintInfo.context->save(); paintInfo.context->addRoundedRectClip(cancelBounds, cancelRadius, cancelRadius, cancelRadius, cancelRadius); - paintInfo.context->fillRect(cancelBounds, buttonColor, DeviceColorSpace); + paintInfo.context->fillRect(cancelBounds, buttonColor, ColorSpaceDeviceRGB); // Draw the 'x' IntSize xSize(3, 3); IntRect xBounds(cancelBounds.location() + IntSize(3, 3), xSize); - paintInfo.context->setStrokeColor(Color::white, DeviceColorSpace); + paintInfo.context->setStrokeColor(Color::white, ColorSpaceDeviceRGB); paintInfo.context->drawLine(xBounds.location(), xBounds.location() + xBounds.size()); paintInfo.context->drawLine(IntPoint(xBounds.right(), xBounds.y()), IntPoint(xBounds.x(), xBounds.bottom())); @@ -490,8 +490,8 @@ bool RenderThemeWinCE::paintSliderTrack(RenderObject* o, const PaintInfo& i, con bool rc = RenderTheme::paintSliderTrack(o, i, r); IntPoint left = IntPoint(r.x() + 2, (r.y() + r.bottom()) / 2); i.context->save(); - i.context->setStrokeColor(Color::gray, DeviceColorSpace); - i.context->setFillColor(Color::gray, DeviceColorSpace); + i.context->setStrokeColor(Color::gray, ColorSpaceDeviceRGB); + i.context->setFillColor(Color::gray, ColorSpaceDeviceRGB); i.context->fillRect(r); #if ENABLE(VIDEO) HTMLMediaElement* mediaElement = mediaElementParent(o->node()); @@ -502,7 +502,7 @@ bool RenderThemeWinCE::paintSliderTrack(RenderObject* o, const PaintInfo& i, con left = right; } #endif - i.context->setStrokeColor(Color::black, DeviceColorSpace); + i.context->setStrokeColor(Color::black, ColorSpaceDeviceRGB); i.context->drawLine(left, IntPoint(r.right() - 2, left.y())); i.context->restore(); return rc; @@ -512,8 +512,8 @@ bool RenderThemeWinCE::paintSliderThumb(RenderObject* o, const PaintInfo& i, con { bool rc = RenderTheme::paintSliderThumb(o, i, r); i.context->save(); - i.context->setStrokeColor(Color::black, DeviceColorSpace); - i.context->setFillColor(Color::black, DeviceColorSpace); + i.context->setStrokeColor(Color::black, ColorSpaceDeviceRGB); + i.context->setFillColor(Color::black, ColorSpaceDeviceRGB); #if ENABLE(VIDEO) HTMLMediaElement* mediaElement = mediaElementParent(o->node()); if (mediaElement) { diff --git a/WebCore/rendering/RenderTreeAsText.cpp b/WebCore/rendering/RenderTreeAsText.cpp index ab6247b..ec36b7e 100644 --- a/WebCore/rendering/RenderTreeAsText.cpp +++ b/WebCore/rendering/RenderTreeAsText.cpp @@ -50,11 +50,11 @@ #include <wtf/Vector.h> #if ENABLE(SVG) -#include "RenderPath.h" #include "RenderSVGContainer.h" #include "RenderSVGGradientStop.h" #include "RenderSVGImage.h" #include "RenderSVGInlineText.h" +#include "RenderSVGPath.h" #include "RenderSVGRoot.h" #include "RenderSVGText.h" #include "SVGRenderTreeAsText.h" @@ -440,8 +440,8 @@ static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBo if (o.containingBlock()->isTableCell()) y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore(); ts << "text run at (" << run.m_x << "," << y << ") width " << run.m_logicalWidth; - if (run.direction() == RTL || run.m_dirOverride) { - ts << (run.direction() == RTL ? " RTL" : " LTR"); + if (!run.isLeftToRightDirection() || run.m_dirOverride) { + ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR"); if (run.m_dirOverride) ts << " override"; } @@ -453,8 +453,8 @@ static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBo void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior) { #if ENABLE(SVG) - if (o.isRenderPath()) { - write(ts, *toRenderPath(&o), indent); + if (o.isSVGPath()) { + write(ts, *toRenderSVGPath(&o), indent); return; } if (o.isSVGGradientStop()) { diff --git a/WebCore/rendering/RenderVideo.cpp b/WebCore/rendering/RenderVideo.cpp index 470499a..32ba91b 100644 --- a/WebCore/rendering/RenderVideo.cpp +++ b/WebCore/rendering/RenderVideo.cpp @@ -246,14 +246,14 @@ void RenderVideo::updatePlayer() mediaPlayer->setVisible(true); } -int RenderVideo::computeReplacedWidth(bool includeMaxWidth) const +int RenderVideo::computeReplacedLogicalWidth(bool includeMaxWidth) const { - return RenderReplaced::computeReplacedWidth(includeMaxWidth); + return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth); } -int RenderVideo::computeReplacedHeight() const +int RenderVideo::computeReplacedLogicalHeight() const { - return RenderReplaced::computeReplacedHeight(); + return RenderReplaced::computeReplacedLogicalHeight(); } int RenderVideo::minimumReplacedHeight() const diff --git a/WebCore/rendering/RenderVideo.h b/WebCore/rendering/RenderVideo.h index 24f473d..9091490 100644 --- a/WebCore/rendering/RenderVideo.h +++ b/WebCore/rendering/RenderVideo.h @@ -70,8 +70,8 @@ private: virtual void layout(); - virtual int computeReplacedWidth(bool includeMaxWidth = true) const; - virtual int computeReplacedHeight() const; + virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual int computeReplacedLogicalHeight() const; virtual int minimumReplacedHeight() const; void updatePlayer(); diff --git a/WebCore/rendering/RenderView.cpp b/WebCore/rendering/RenderView.cpp index 1ae1589..260a081 100644 --- a/WebCore/rendering/RenderView.cpp +++ b/WebCore/rendering/RenderView.cpp @@ -54,6 +54,7 @@ RenderView::RenderView(Node* node, FrameView* view) , m_selectionEndPos(-1) , m_maximalOutlineSize(0) , m_pageHeight(0) + , m_pageHeightChanged(false) , m_layoutState(0) , m_layoutStateDisableCount(0) { @@ -127,6 +128,8 @@ void RenderView::layout() // FIXME: May be better to push a clip and avoid issuing offscreen repaints. state.m_clipped = false; state.m_pageHeight = m_pageHeight; + state.m_pageHeightChanged = m_pageHeightChanged; + m_pageHeightChanged = false; m_layoutState = &state; if (needsLayout()) diff --git a/WebCore/rendering/RenderView.h b/WebCore/rendering/RenderView.h index b03312f..8f64766 100644 --- a/WebCore/rendering/RenderView.h +++ b/WebCore/rendering/RenderView.h @@ -54,8 +54,8 @@ public: // The same as the FrameView's layoutHeight/layoutWidth but with null check guards. int viewHeight() const; int viewWidth() const; - int viewLogicalWidth() const { return style()->isVerticalBlockFlow() ? viewWidth() : viewHeight(); } - int viewLogicalHeight() const { return style()->isVerticalBlockFlow() ? viewHeight() : viewWidth(); } + int viewLogicalWidth() const { return style()->isHorizontalWritingMode() ? viewWidth() : viewHeight(); } + int viewLogicalHeight() const { return style()->isHorizontalWritingMode() ? viewHeight() : viewWidth(); } float zoomFactor() const; @@ -138,7 +138,7 @@ public: { if (m_pageHeight != height) { m_pageHeight = height; - markDescendantBlocksAndLinesForLayout(); + m_pageHeightChanged = true; } } @@ -181,11 +181,11 @@ public: // used by layout function int docWidth() const; // These functions may only be accessed by LayoutStateMaintainer. - bool pushLayoutState(RenderBox* renderer, const IntSize& offset, int pageHeight = 0, ColumnInfo* colInfo = 0) + bool pushLayoutState(RenderBox* renderer, const IntSize& offset, int pageHeight = 0, bool pageHeightChanged = false, ColumnInfo* colInfo = 0) { // We push LayoutState even if layoutState is disabled because it stores layoutDelta too. if (!doingFullRepaint() || renderer->hasColumns() || m_layoutState->isPaginated()) { - m_layoutState = new (renderArena()) LayoutState(m_layoutState, renderer, offset, pageHeight, colInfo); + m_layoutState = new (renderArena()) LayoutState(m_layoutState, renderer, offset, pageHeight, pageHeightChanged, colInfo); return true; } return false; @@ -233,6 +233,7 @@ protected: private: unsigned m_pageHeight; + bool m_pageHeightChanged; LayoutState* m_layoutState; unsigned m_layoutStateDisableCount; #if USE(ACCELERATED_COMPOSITING) @@ -260,14 +261,14 @@ void toRenderView(const RenderView*); class LayoutStateMaintainer : public Noncopyable { public: // ctor to push now - LayoutStateMaintainer(RenderView* view, RenderBox* root, IntSize offset, bool disableState = false, int pageHeight = 0, ColumnInfo* colInfo = 0) + LayoutStateMaintainer(RenderView* view, RenderBox* root, IntSize offset, bool disableState = false, int pageHeight = 0, bool pageHeightChanged = false, ColumnInfo* colInfo = 0) : m_view(view) , m_disabled(disableState) , m_didStart(false) , m_didEnd(false) , m_didCreateLayoutState(false) { - push(root, offset, pageHeight, colInfo); + push(root, offset, pageHeight, pageHeightChanged, colInfo); } // ctor to maybe push later @@ -285,11 +286,11 @@ public: ASSERT(m_didStart == m_didEnd); // if this fires, it means that someone did a push(), but forgot to pop(). } - void push(RenderBox* root, IntSize offset, int pageHeight = 0, ColumnInfo* colInfo = 0) + void push(RenderBox* root, IntSize offset, int pageHeight = 0, bool pageHeightChanged = false, ColumnInfo* colInfo = 0) { ASSERT(!m_didStart); // We push state even if disabled, because we still need to store layoutDelta - m_didCreateLayoutState = m_view->pushLayoutState(root, offset, pageHeight, colInfo); + m_didCreateLayoutState = m_view->pushLayoutState(root, offset, pageHeight, pageHeightChanged, colInfo); if (m_disabled && m_didCreateLayoutState) m_view->disableLayoutState(); m_didStart = true; diff --git a/WebCore/rendering/RenderWordBreak.h b/WebCore/rendering/RenderWordBreak.h index acd8179..db31eec 100644 --- a/WebCore/rendering/RenderWordBreak.h +++ b/WebCore/rendering/RenderWordBreak.h @@ -35,7 +35,7 @@ class HTMLElement; class RenderWordBreak : public RenderText { public: - RenderWordBreak(HTMLElement*); + explicit RenderWordBreak(HTMLElement*); virtual const char* renderName() const; virtual bool isWordBreak() const; diff --git a/WebCore/rendering/RenderingAllInOne.cpp b/WebCore/rendering/RenderingAllInOne.cpp new file mode 100644 index 0000000..7a3a705 --- /dev/null +++ b/WebCore/rendering/RenderingAllInOne.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 Apple 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. + */ + +// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build. + +#include "AutoTableLayout.cpp" +#include "BidiRun.cpp" +#include "CounterNode.cpp" +#include "EllipsisBox.cpp" +#include "FixedTableLayout.cpp" +#include "HitTestResult.cpp" +#include "InlineBox.cpp" +#include "InlineFlowBox.cpp" +#include "InlineTextBox.cpp" +#include "LayoutState.cpp" +#include "MediaControlElements.cpp" +#include "PointerEventsHitRules.cpp" +#include "RenderApplet.cpp" +#include "RenderArena.cpp" +#include "RenderBR.cpp" +#include "RenderBlock.cpp" +#include "RenderBlockLineLayout.cpp" +#include "RenderBox.cpp" +#include "RenderBoxModelObject.cpp" +#include "RenderButton.cpp" +#include "RenderCounter.cpp" +#include "RenderDataGrid.cpp" +#include "RenderEmbeddedObject.cpp" +#include "RenderFieldset.cpp" +#include "RenderFileUploadControl.cpp" +#include "RenderFlexibleBox.cpp" +#include "RenderForeignObject.cpp" +#include "RenderFrame.cpp" +#include "RenderFrameBase.cpp" +#include "RenderFrameSet.cpp" +#include "RenderHTMLCanvas.cpp" +#include "RenderIFrame.cpp" +#include "RenderImage.cpp" +#include "RenderImageResource.cpp" +#include "RenderImageResourceStyleImage.cpp" +#include "RenderIndicator.cpp" +#include "RenderInline.cpp" +#include "RenderLayer.cpp" +#include "RenderLayerCompositor.cpp" +#include "RenderLineBoxList.cpp" +#include "RenderListBox.cpp" +#include "RenderListItem.cpp" +#include "RenderListMarker.cpp" +#include "RenderMarquee.cpp" +#include "RenderMedia.cpp" +#include "RenderMediaControls.cpp" +#include "RenderMenuList.cpp" +#include "RenderMeter.cpp" +#include "RenderObject.cpp" +#include "RenderObjectChildList.cpp" +#include "RenderPart.cpp" +#include "RenderProgress.cpp" +#include "RenderReplaced.cpp" +#include "RenderReplica.cpp" +#include "RenderRuby.cpp" +#include "RenderRubyBase.cpp" +#include "RenderRubyRun.cpp" +#include "RenderRubyText.cpp" +#include "RenderScrollbar.cpp" +#include "RenderScrollbarPart.cpp" +#include "RenderScrollbarTheme.cpp" +#include "RenderSlider.cpp" +#include "RenderTable.cpp" +#include "RenderTableCell.cpp" +#include "RenderTableCol.cpp" +#include "RenderTableRow.cpp" +#include "RenderTableSection.cpp" +#include "RenderText.cpp" +#include "RenderTextControl.cpp" +#include "RenderTextControlMultiLine.cpp" +#include "RenderTextControlSingleLine.cpp" +#include "RenderTextFragment.cpp" +#include "RenderTheme.cpp" +#include "RenderThemeWin.cpp" +#include "RenderTreeAsText.cpp" +#include "RenderVideo.cpp" +#include "RenderView.cpp" +#include "RenderWidget.cpp" +#include "RenderWordBreak.cpp" +#include "RootInlineBox.cpp" +#include "ScrollBehavior.cpp" +#include "ShadowElement.cpp" +#include "TextControlInnerElements.cpp" +#include "TransformState.cpp" +#include "break_lines.cpp" diff --git a/WebCore/rendering/RootInlineBox.cpp b/WebCore/rendering/RootInlineBox.cpp index 52586c5..c7fcaeb 100644 --- a/WebCore/rendering/RootInlineBox.cpp +++ b/WebCore/rendering/RootInlineBox.cpp @@ -46,8 +46,9 @@ RootInlineBox::RootInlineBox(RenderBlock* block) , m_lineTop(0) , m_lineBottom(0) , m_paginationStrut(0) + , m_blockLogicalHeight(0) { - setIsVertical(!block->style()->isVerticalBlockFlow()); + setIsVertical(!block->style()->isHorizontalWritingMode()); } @@ -199,7 +200,7 @@ void RootInlineBox::adjustPosition(int dx, int dy) InlineFlowBox::adjustPosition(dx, dy); m_lineTop += dy; m_lineBottom += dy; - m_blockHeight += dy; + m_blockLogicalHeight += dy; } void RootInlineBox::childRemoved(InlineBox* box) @@ -240,10 +241,12 @@ int RootInlineBox::alignBoxesInBlockDirection(int heightOfBlock, GlyphOverflowAn placeBoxesInBlockDirection(heightOfBlock, maxHeight, maxAscent, noQuirksMode, lineTop, lineBottom); computeBlockDirectionOverflow(lineTop, lineBottom, noQuirksMode, textBoxDataMap); setLineTopBottomPositions(lineTop, lineBottom); - - heightOfBlock += maxHeight; - - return heightOfBlock; + + // Detect integer overflow. + if (heightOfBlock > numeric_limits<int>::max() - maxHeight) + return numeric_limits<int>::max(); + + return heightOfBlock + maxHeight; } GapRects RootInlineBox::fillLineSelectionGap(int selTop, int selHeight, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, diff --git a/WebCore/rendering/RootInlineBox.h b/WebCore/rendering/RootInlineBox.h index 8c75072..4a0b485 100644 --- a/WebCore/rendering/RootInlineBox.h +++ b/WebCore/rendering/RootInlineBox.h @@ -69,8 +69,8 @@ public: unsigned lineBreakPos() const { return m_lineBreakPos; } void setLineBreakPos(unsigned p) { m_lineBreakPos = p; } - int blockHeight() const { return m_blockHeight; } - void setBlockHeight(int h) { m_blockHeight = h; } + int blockLogicalHeight() const { return m_blockLogicalHeight; } + void setBlockLogicalHeight(int h) { m_blockLogicalHeight = h; } bool endsWithBreak() const { return m_endsWithBreak; } void setEndsWithBreak(bool b) { m_endsWithBreak = b; } @@ -88,6 +88,9 @@ public: virtual void clearTruncation(); + virtual int baselinePosition() const { return boxModelObject()->baselinePosition(m_firstLine, m_isVertical ? VerticalLine : HorizontalLine, PositionOfInteriorLineBoxes); } + virtual int lineHeight() const { return boxModelObject()->lineHeight(m_firstLine, m_isVertical ? VerticalLine : HorizontalLine, PositionOfInteriorLineBoxes); } + #if PLATFORM(MAC) void addHighlightOverflow(); void paintCustomHighlight(PaintInfo&, int tx, int ty, const AtomicString& highlightType); @@ -143,8 +146,8 @@ private: // good for as long as the line has not been marked dirty. OwnPtr<Vector<RenderBox*> > m_floats; - // The height of the block at the end of this line. This is where the next line starts. - int m_blockHeight; + // The logical height of the block at the end of this line. This is where the next line starts. + int m_blockLogicalHeight; WTF::Unicode::Direction m_lineBreakBidiStatusEor : 5; WTF::Unicode::Direction m_lineBreakBidiStatusLastStrong : 5; diff --git a/WebCore/rendering/SVGCharacterData.h b/WebCore/rendering/SVGCharacterData.h deleted file mode 100644 index 5143042..0000000 --- a/WebCore/rendering/SVGCharacterData.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) Research In Motion Limited 2010. 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 SVGCharacterData_h -#define SVGCharacterData_h - -#if ENABLE(SVG) -#include <wtf/HashMap.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> - -namespace WebCore { - -class AffineTransform; -class RenderObject; - -// Holds extra data, when the character is laid out on a path -struct SVGCharOnPath : RefCounted<SVGCharOnPath> { - static PassRefPtr<SVGCharOnPath> create() { return adoptRef(new SVGCharOnPath); } - - float xScale; - float yScale; - - float xShift; - float yShift; - - float orientationAngle; - - bool hidden : 1; - -private: - SVGCharOnPath() - : xScale(1.0f) - , yScale(1.0f) - , xShift(0.0f) - , yShift(0.0f) - , orientationAngle(0.0f) - , hidden(false) - { - } -}; - -struct SVGChar { - SVGChar() - : x(0.0f) - , y(0.0f) - , angle(0.0f) - , orientationShiftX(0.0f) - , orientationShiftY(0.0f) - , drawnSeperated(false) - , newTextChunk(false) - { - } - - float x; - float y; - float angle; - - float orientationShiftX; - float orientationShiftY; - - RefPtr<SVGCharOnPath> pathData; - - // Determines wheter this char needs to be drawn seperated - bool drawnSeperated : 1; - - // Determines wheter this char starts a new chunk - bool newTextChunk : 1; - - // Helper methods - bool isHidden() const; - AffineTransform characterTransform() const; -}; - -} // namespace WebCore - -#endif // ENABLE(SVG) -#endif // SVGCharacterData_h diff --git a/WebCore/rendering/SVGCharacterLayoutInfo.cpp b/WebCore/rendering/SVGCharacterLayoutInfo.cpp deleted file mode 100644 index 71ff1aa..0000000 --- a/WebCore/rendering/SVGCharacterLayoutInfo.cpp +++ /dev/null @@ -1,519 +0,0 @@ -/* - * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * - * 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 "SVGCharacterLayoutInfo.h" - -#if ENABLE(SVG) -#include "InlineFlowBox.h" -#include "InlineTextBox.h" -#include "RenderSVGTextPath.h" -#include "SVGCharacterData.h" -#include "SVGLengthList.h" -#include "SVGNumberList.h" -#include "SVGTextPositioningElement.h" - -#include <float.h> - -namespace WebCore { - -// Helper function -static float calculateBaselineShift(RenderObject* item) -{ - ASSERT(item); - ASSERT(item->style()); - ASSERT(item->node()); - ASSERT(item->node()->isSVGElement()); - - const Font& font = item->style()->font(); - const SVGRenderStyle* svgStyle = item->style()->svgStyle(); - - float baselineShift = 0.0f; - if (svgStyle->baselineShift() == BS_LENGTH) { - SVGLength baselineShiftValueLength = svgStyle->baselineShiftValue(); - if (baselineShiftValueLength.unitType() == LengthTypePercentage) - baselineShift = baselineShiftValueLength.valueAsPercentage() * font.pixelSize(); - else - baselineShift = baselineShiftValueLength.value(static_cast<SVGElement*>(item->node())); - } else { - float baselineAscent = font.ascent() + font.descent(); - - switch (svgStyle->baselineShift()) { - case BS_BASELINE: - break; - case BS_SUB: - baselineShift = -baselineAscent / 2.0f; - break; - case BS_SUPER: - baselineShift = baselineAscent / 2.0f; - break; - default: - ASSERT_NOT_REACHED(); - } - } - - return baselineShift; -} - -SVGCharacterLayoutInfo::SVGCharacterLayoutInfo() - : curx(0.0f) - , cury(0.0f) - , angle(0.0f) - , dx(0.0f) - , dy(0.0f) - , shiftx(0.0f) - , shifty(0.0f) - , pathExtraAdvance(0.0f) - , pathTextLength(0.0f) - , pathChunkLength(0.0f) - , nextDrawnSeperated(false) - , xStackChanged(false) - , yStackChanged(false) - , dxStackChanged(false) - , dyStackChanged(false) - , angleStackChanged(false) - , baselineShiftStackChanged(false) - , pathLayout(false) - , currentOffset(0.0f) - , startOffset(0.0f) - , layoutPathLength(0.0f) -{ -} - -bool SVGCharacterLayoutInfo::xValueAvailable() const -{ - return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size(); -} - -bool SVGCharacterLayoutInfo::yValueAvailable() const -{ - return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size(); -} - -bool SVGCharacterLayoutInfo::dxValueAvailable() const -{ - return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size(); -} - -bool SVGCharacterLayoutInfo::dyValueAvailable() const -{ - return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size(); -} - -bool SVGCharacterLayoutInfo::angleValueAvailable() const -{ - return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size(); -} - -bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const -{ - return !baselineShiftStack.isEmpty(); -} - -float SVGCharacterLayoutInfo::xValueNext() const -{ - ASSERT(!xStack.isEmpty()); - return xStack.last().valueAtCurrentPosition(); -} - -float SVGCharacterLayoutInfo::yValueNext() const -{ - ASSERT(!yStack.isEmpty()); - return yStack.last().valueAtCurrentPosition(); -} - -float SVGCharacterLayoutInfo::dxValueNext() const -{ - ASSERT(!dxStack.isEmpty()); - return dxStack.last().valueAtCurrentPosition(); -} - -float SVGCharacterLayoutInfo::dyValueNext() const -{ - ASSERT(!dyStack.isEmpty()); - return dyStack.last().valueAtCurrentPosition(); -} - -float SVGCharacterLayoutInfo::angleValueNext() const -{ - ASSERT(!angleStack.isEmpty()); - return angleStack.last().valueAtCurrentPosition(); -} - -float SVGCharacterLayoutInfo::baselineShiftValueNext() const -{ - ASSERT(!baselineShiftStack.isEmpty()); - return baselineShiftStack.last(); -} - - -bool SVGCharacterLayoutInfo::isInitialLayout() const -{ - return xStack.isEmpty() - && yStack.isEmpty() - && dxStack.isEmpty() - && dyStack.isEmpty() - && angleStack.isEmpty() - && baselineShiftStack.isEmpty() - && curx == 0.0f - && cury == 0.0f; -} - -void SVGCharacterLayoutInfo::processedSingleCharacter() -{ - xStackWalk(); - yStackWalk(); - dxStackWalk(); - dyStackWalk(); - angleStackWalk(); - baselineShiftStackWalk(); -} - -void SVGCharacterLayoutInfo::processedChunk(float savedShiftX, float savedShiftY) -{ - // baseline-shift doesn't span across ancestors, unlike dx/dy. - curx += savedShiftX - shiftx; - cury += savedShiftY - shifty; - - if (inPathLayout()) { - shiftx = savedShiftX; - shifty = savedShiftY; - } - - // rotation also doesn't span - angle = 0.0f; - - if (xStackChanged) { - ASSERT(!xStack.isEmpty()); - xStack.removeLast(); - xStackChanged = false; - } - - if (yStackChanged) { - ASSERT(!yStack.isEmpty()); - yStack.removeLast(); - yStackChanged = false; - } - - if (dxStackChanged) { - ASSERT(!dxStack.isEmpty()); - dxStack.removeLast(); - dxStackChanged = false; - } - - if (dyStackChanged) { - ASSERT(!dyStack.isEmpty()); - dyStack.removeLast(); - dyStackChanged = false; - } - - if (angleStackChanged) { - ASSERT(!angleStack.isEmpty()); - angleStack.removeLast(); - angleStackChanged = false; - } - - if (baselineShiftStackChanged) { - ASSERT(!baselineShiftStack.isEmpty()); - baselineShiftStack.removeLast(); - baselineShiftStackChanged = false; - } -} - -bool SVGCharacterLayoutInfo::nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset) -{ - if (layoutPathLength <= 0.0f) - return false; - - if (newOffset != FLT_MIN) - currentOffset = startOffset + newOffset; - - // Respect translation along path (extraAdvance is orthogonal to the path) - currentOffset += extraAdvance; - - float offset = currentOffset + glyphAdvance / 2.0f; - currentOffset += glyphAdvance + pathExtraAdvance; - - if (offset < 0.0f || offset > layoutPathLength) - return false; - - bool ok = false; - FloatPoint point = layoutPath.pointAtLength(offset, ok); - ASSERT(ok); - - curx = point.x(); - cury = point.y(); - - angle = layoutPath.normalAngleAtLength(offset, ok); - ASSERT(ok); - - return true; -} - -bool SVGCharacterLayoutInfo::inPathLayout() const -{ - return pathLayout; -} - -void SVGCharacterLayoutInfo::setInPathLayout(bool value) -{ - pathLayout = value; - - pathExtraAdvance = 0.0f; - pathTextLength = 0.0f; - pathChunkLength = 0.0f; -} - -void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox* flowBox, float textAnchorStartOffset) -{ - bool wasInitialLayout = isInitialLayout(); - - RenderSVGTextPath* textPath = toRenderSVGTextPath(flowBox->renderer()); - Path path = textPath->layoutPath(); - - float baselineShift = calculateBaselineShift(textPath); - - layoutPath = path; - layoutPathLength = path.length(); - - if (layoutPathLength <= 0.0f) - return; - - startOffset = textPath->startOffset(); - - if (textPath->startOffset() >= 0.0f && textPath->startOffset() <= 1.0f) - startOffset *= layoutPathLength; - - startOffset += textAnchorStartOffset; - currentOffset = startOffset; - - // Only baseline-shift is handled through the normal layout system - addStackContent(BaselineShiftStack, baselineShift); - - if (wasInitialLayout) { - xStackChanged = false; - yStackChanged = false; - dxStackChanged = false; - dyStackChanged = false; - angleStackChanged = false; - baselineShiftStackChanged = false; - } -} - -void SVGCharacterLayoutInfo::addLayoutInformation(SVGTextPositioningElement* element) -{ - bool wasInitialLayout = isInitialLayout(); - float baselineShift = calculateBaselineShift(element->renderer()); - - addStackContent(XStack, element->x(), element); - addStackContent(YStack, element->y(), element); - addStackContent(DxStack, element->dx(), element); - addStackContent(DyStack, element->dy(), element); - addStackContent(AngleStack, element->rotate()); - addStackContent(BaselineShiftStack, baselineShift); - - if (wasInitialLayout) { - xStackChanged = false; - yStackChanged = false; - dxStackChanged = false; - dyStackChanged = false; - angleStackChanged = false; - baselineShiftStackChanged = false; - } -} - -void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGNumberList* list) -{ - unsigned length = list->numberOfItems(); - if (!length) - return; - - PositionedFloatVector newLayoutInfo; - - // TODO: Convert more efficiently! - ExceptionCode ec = 0; - for (unsigned i = 0; i < length; ++i) { - float value = list->getItem(i, ec); - ASSERT(!ec); - - newLayoutInfo.append(value); - } - - addStackContent(type, newLayoutInfo); -} - -void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGLengthList* list, const SVGElement* context) -{ - unsigned length = list->numberOfItems(); - if (!length) - return; - - PositionedFloatVector newLayoutInfo; - - ExceptionCode ec = 0; - for (unsigned i = 0; i < length; ++i) { - float value = list->getItem(i, ec).value(context); - ASSERT(!ec); - - newLayoutInfo.append(value); - } - - addStackContent(type, newLayoutInfo); -} - -void SVGCharacterLayoutInfo::addStackContent(StackType type, const PositionedFloatVector& list) -{ - switch (type) { - case XStack: - xStackChanged = true; - xStack.append(list); - break; - case YStack: - yStackChanged = true; - yStack.append(list); - break; - case DxStack: - dxStackChanged = true; - dxStack.append(list); - break; - case DyStack: - dyStackChanged = true; - dyStack.append(list); - break; - case AngleStack: - angleStackChanged = true; - angleStack.append(list); - break; - default: - ASSERT_NOT_REACHED(); - } -} - -void SVGCharacterLayoutInfo::addStackContent(StackType type, float value) -{ - if (value == 0.0f) - return; - - switch (type) { - case BaselineShiftStack: - baselineShiftStackChanged = true; - baselineShiftStack.append(value); - break; - default: - ASSERT_NOT_REACHED(); - } -} - -void SVGCharacterLayoutInfo::xStackWalk() -{ - unsigned i = 1; - - while (!xStack.isEmpty()) { - PositionedFloatVector& cur = xStack.last(); - if (i + cur.position() < cur.size()) { - cur.advance(i); - break; - } - - i += cur.position(); - xStack.removeLast(); - xStackChanged = false; - } -} - -void SVGCharacterLayoutInfo::yStackWalk() -{ - unsigned i = 1; - - while (!yStack.isEmpty()) { - PositionedFloatVector& cur = yStack.last(); - if (i + cur.position() < cur.size()) { - cur.advance(i); - break; - } - - i += cur.position(); - yStack.removeLast(); - yStackChanged = false; - } -} - -void SVGCharacterLayoutInfo::dxStackWalk() -{ - unsigned i = 1; - - while (!dxStack.isEmpty()) { - PositionedFloatVector& cur = dxStack.last(); - if (i + cur.position() < cur.size()) { - cur.advance(i); - break; - } - - i += cur.position(); - dxStack.removeLast(); - dxStackChanged = false; - } -} - -void SVGCharacterLayoutInfo::dyStackWalk() -{ - unsigned i = 1; - - while (!dyStack.isEmpty()) { - PositionedFloatVector& cur = dyStack.last(); - if (i + cur.position() < cur.size()) { - cur.advance(i); - break; - } - - i += cur.position(); - dyStack.removeLast(); - dyStackChanged = false; - } -} - -void SVGCharacterLayoutInfo::angleStackWalk() -{ - unsigned i = 1; - - while (!angleStack.isEmpty()) { - PositionedFloatVector& cur = angleStack.last(); - if (i + cur.position() < cur.size()) { - cur.advance(i); - break; - } - - i += cur.position(); - angleStack.removeLast(); - angleStackChanged = false; - } -} - -void SVGCharacterLayoutInfo::baselineShiftStackWalk() -{ - if (!baselineShiftStack.isEmpty()) { - baselineShiftStack.removeLast(); - baselineShiftStackChanged = false; - } -} - -} // namespace WebCore - -#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGCharacterLayoutInfo.h b/WebCore/rendering/SVGCharacterLayoutInfo.h deleted file mode 100644 index 7549283..0000000 --- a/WebCore/rendering/SVGCharacterLayoutInfo.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * - * 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 SVGCharacterLayoutInfo_h -#define SVGCharacterLayoutInfo_h - -#if ENABLE(SVG) -#include "Path.h" -#include <wtf/Assertions.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class InlineFlowBox; -class SVGElement; -class SVGLengthList; -class SVGNumberList; -class SVGTextPositioningElement; - -template<class Type> -class PositionedVector : public Vector<Type> { -public: - PositionedVector<Type>() - : m_position(0) - { - } - - unsigned position() const - { - return m_position; - } - - void advance(unsigned position) - { - m_position += position; - ASSERT(m_position < Vector<Type>::size()); - } - - Type valueAtCurrentPosition() const - { - ASSERT(m_position < Vector<Type>::size()); - return Vector<Type>::at(m_position); - } - -private: - unsigned m_position; -}; - -class PositionedFloatVector : public PositionedVector<float> { }; -struct SVGChar; - -struct SVGCharacterLayoutInfo { - SVGCharacterLayoutInfo(); - - enum StackType { XStack, YStack, DxStack, DyStack, AngleStack, BaselineShiftStack }; - - bool xValueAvailable() const; - bool yValueAvailable() const; - bool dxValueAvailable() const; - bool dyValueAvailable() const; - bool angleValueAvailable() const; - bool baselineShiftValueAvailable() const; - - float xValueNext() const; - float yValueNext() const; - float dxValueNext() const; - float dyValueNext() const; - float angleValueNext() const; - float baselineShiftValueNext() const; - - void processedChunk(float savedShiftX, float savedShiftY); - void processedSingleCharacter(); - - bool nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset); - - // Used for text-on-path. - void addLayoutInformation(InlineFlowBox*, float textAnchorOffset = 0.0f); - - bool inPathLayout() const; - void setInPathLayout(bool value); - - // Used for anything else. - void addLayoutInformation(SVGTextPositioningElement*); - - // Global position - float curx; - float cury; - - // Global rotation - float angle; - - // Accumulated dx/dy values - float dx; - float dy; - - // Accumulated baseline-shift values - float shiftx; - float shifty; - - // Path specific advance values to handle lengthAdjust - float pathExtraAdvance; - float pathTextLength; - float pathChunkLength; - - // Result vector - Vector<SVGChar> svgChars; - bool nextDrawnSeperated : 1; - -private: - // Used for baseline-shift. - void addStackContent(StackType, float); - - // Used for angle. - void addStackContent(StackType, SVGNumberList*); - - // Used for x/y/dx/dy. - void addStackContent(StackType, SVGLengthList*, const SVGElement*); - - void addStackContent(StackType, const PositionedFloatVector&); - - void xStackWalk(); - void yStackWalk(); - void dxStackWalk(); - void dyStackWalk(); - void angleStackWalk(); - void baselineShiftStackWalk(); - - bool isInitialLayout() const; - -private: - bool xStackChanged : 1; - bool yStackChanged : 1; - bool dxStackChanged : 1; - bool dyStackChanged : 1; - bool angleStackChanged : 1; - bool baselineShiftStackChanged : 1; - - // text on path layout - bool pathLayout : 1; - float currentOffset; - float startOffset; - float layoutPathLength; - Path layoutPath; - - Vector<PositionedFloatVector> xStack; - Vector<PositionedFloatVector> yStack; - Vector<PositionedFloatVector> dxStack; - Vector<PositionedFloatVector> dyStack; - Vector<PositionedFloatVector> angleStack; - Vector<float> baselineShiftStack; -}; - -} // namespace WebCore - -#endif // ENABLE(SVG) -#endif // SVGCharacterLayoutInfo_h diff --git a/WebCore/rendering/SVGImageBufferTools.cpp b/WebCore/rendering/SVGImageBufferTools.cpp index 709bf10..903aa21 100644 --- a/WebCore/rendering/SVGImageBufferTools.cpp +++ b/WebCore/rendering/SVGImageBufferTools.cpp @@ -50,7 +50,7 @@ void SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem( } } -bool SVGImageBufferTools::createImageBuffer(const FloatRect& absoluteTargetRect, const FloatRect& clampedAbsoluteTargetRect, OwnPtr<ImageBuffer>& imageBuffer, ImageColorSpace colorSpace) +bool SVGImageBufferTools::createImageBuffer(const FloatRect& absoluteTargetRect, const FloatRect& clampedAbsoluteTargetRect, OwnPtr<ImageBuffer>& imageBuffer, ColorSpace colorSpace) { IntSize imageSize(roundedImageBufferSize(clampedAbsoluteTargetRect.size())); IntSize unclampedImageSize(SVGImageBufferTools::roundedImageBufferSize(absoluteTargetRect.size())); diff --git a/WebCore/rendering/SVGImageBufferTools.h b/WebCore/rendering/SVGImageBufferTools.h index 8894aae..9bcc7a4 100644 --- a/WebCore/rendering/SVGImageBufferTools.h +++ b/WebCore/rendering/SVGImageBufferTools.h @@ -34,7 +34,7 @@ class RenderObject; class SVGImageBufferTools : public Noncopyable { public: - static bool createImageBuffer(const FloatRect& absoluteTargetRect, const FloatRect& clampedAbsoluteTargetRect, OwnPtr<ImageBuffer>&, ImageColorSpace); + static bool createImageBuffer(const FloatRect& absoluteTargetRect, const FloatRect& clampedAbsoluteTargetRect, OwnPtr<ImageBuffer>&, ColorSpace); static void renderSubtreeToImageBuffer(ImageBuffer*, RenderObject*, const AffineTransform&); static void clipToImageBuffer(GraphicsContext*, const AffineTransform& absoluteTransform, const FloatRect& clampedAbsoluteTargetRect, OwnPtr<ImageBuffer>&); diff --git a/WebCore/rendering/SVGInlineFlowBox.cpp b/WebCore/rendering/SVGInlineFlowBox.cpp deleted file mode 100644 index 4008938..0000000 --- a/WebCore/rendering/SVGInlineFlowBox.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> - * (C) 2006 Apple Computer Inc. - * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) Research In Motion Limited 2010. 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. - * - */ - -#include "config.h" -#include "SVGInlineFlowBox.h" - -#if ENABLE(SVG) -#include "GraphicsContext.h" -#include "SVGRenderSupport.h" - -namespace WebCore { - -void SVGInlineFlowBox::paint(PaintInfo& paintInfo, int, int) -{ - ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); - ASSERT(!paintInfo.context->paintingDisabled()); - - RenderObject* boxRenderer = renderer(); - ASSERT(boxRenderer); - - PaintInfo childPaintInfo(paintInfo); - childPaintInfo.context->save(); - - if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) { - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) - child->paint(childPaintInfo, 0, 0); - } - - SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context); - childPaintInfo.context->restore(); -} - -IntRect SVGInlineFlowBox::calculateBoundaries() const -{ - IntRect childRect; - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) - childRect.unite(child->calculateBoundaries()); - return childRect; -} - -} // namespace WebCore - -#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGInlineTextBox.cpp b/WebCore/rendering/SVGInlineTextBox.cpp deleted file mode 100644 index c367598..0000000 --- a/WebCore/rendering/SVGInlineTextBox.cpp +++ /dev/null @@ -1,889 +0,0 @@ -/** - * Copyright (C) 2007 Rob Buis <buis@kde.org> - * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) Research In Motion Limited 2010. 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. - * - */ - -#include "config.h" -#include "SVGInlineTextBox.h" - -#if ENABLE(SVG) -#include "FloatConversion.h" -#include "GraphicsContext.h" -#include "InlineFlowBox.h" -#include "RenderBlock.h" -#include "RenderSVGResource.h" -#include "SVGRootInlineBox.h" -#include "SVGTextLayoutUtilities.h" - -#include <float.h> - -using namespace std; - -namespace WebCore { - -SVGInlineTextBox::SVGInlineTextBox(RenderObject* object) - : InlineTextBox(object) - , m_logicalHeight(0) - , m_paintingResource(0) - , m_paintingResourceMode(ApplyToDefaultMode) -{ -} - -SVGRootInlineBox* SVGInlineTextBox::svgRootInlineBox() const -{ - // Find associated root inline box - InlineFlowBox* parentBox = parent(); - - while (parentBox && !parentBox->isRootInlineBox()) - parentBox = parentBox->parent(); - - ASSERT(parentBox); - ASSERT(parentBox->isRootInlineBox()); - - if (!parentBox->isSVGRootInlineBox()) - return 0; - - return static_cast<SVGRootInlineBox*>(parentBox); -} - -void SVGInlineTextBox::measureCharacter(RenderStyle* style, int position, int& charsConsumed, String& glyphName, String& unicodeString, float& glyphWidth, float& glyphHeight) const -{ - ASSERT(style); - - int offset = direction() == RTL ? end() - position : start() + position; - int extraCharsAvailable = len() - position - 1; - const UChar* characters = textRenderer()->characters(); - - const Font& font = style->font(); - glyphWidth = font.floatWidth(svgTextRunForInlineTextBox(characters + offset, 1, style, this), extraCharsAvailable, charsConsumed, glyphName); - glyphHeight = font.height(); - - // The unicodeString / glyphName pair is needed for kerning calculations. - unicodeString = String(characters + offset, charsConsumed); -} - -int SVGInlineTextBox::offsetForPosition(int xCoordinate, bool includePartialGlyphs) const -{ - ASSERT(!m_currentChunkPart.isValid()); - float x = xCoordinate; - - RenderText* textRenderer = this->textRenderer(); - ASSERT(textRenderer); - - RenderStyle* style = textRenderer->style(); - ASSERT(style); - - RenderBlock* containingBlock = textRenderer->containingBlock(); - ASSERT(containingBlock); - - // Move incoming relative x position to absolute position, as the character origins stored in the chunk parts use absolute coordinates - x += containingBlock->x(); - - // Figure out which text chunk part is hit - SVGTextChunkPart hitPart; - - const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); - for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) { - const SVGTextChunkPart& part = *it; - - // Check whether we're past the hit part. - if (x < part.firstCharacter->x) - break; - - hitPart = part; - } - - // If we did not hit anything, just exit. - if (!hitPart.isValid()) - return 0; - - // Before calling Font::offsetForPosition(), subtract the start position of the first character - // in the hit text chunk part, to pass in coordinates, which are relative to the text chunk part, as - // constructTextRun() only builds a TextRun for the current chunk part, not the whole inline text box. - x -= hitPart.firstCharacter->x; - - m_currentChunkPart = hitPart; - TextRun textRun(constructTextRun(style)); - m_currentChunkPart = SVGTextChunkPart(); - - // Eventually handle lengthAdjust="spacingAndGlyphs". - // FIXME: Need to revisit the whole offsetForPosition concept for vertical text selection. - if (!m_chunkTransformation.isIdentity()) - textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(m_chunkTransformation.a())); - - return hitPart.offset + style->font().offsetForPosition(textRun, x, includePartialGlyphs); -} - -int SVGInlineTextBox::positionForOffset(int) const -{ - // SVG doesn't use the offset <-> position selection system. - ASSERT_NOT_REACHED(); - return 0; -} - -FloatRect SVGInlineTextBox::selectionRectForTextChunkPart(const SVGTextChunkPart& part, int partStartPos, int partEndPos, RenderStyle* style) -{ - // Map startPos/endPos positions into chunk part - mapStartEndPositionsIntoChunkPartCoordinates(partStartPos, partEndPos, part); - - if (partStartPos >= partEndPos) - return FloatRect(); - - // Set current chunk part, so constructTextRun() works properly. - m_currentChunkPart = part; - - const Font& font = style->font(); - Vector<SVGChar>::const_iterator character = part.firstCharacter; - FloatPoint textOrigin(character->x, character->y - font.ascent()); - - FloatRect partRect(font.selectionRectForText(constructTextRun(style), textOrigin, part.height, partStartPos, partEndPos)); - m_currentChunkPart = SVGTextChunkPart(); - - return character->characterTransform().mapRect(partRect); -} - -IntRect SVGInlineTextBox::selectionRect(int, int, int startPos, int endPos) -{ - ASSERT(!m_currentChunkPart.isValid()); - - int boxStart = start(); - startPos = max(startPos - boxStart, 0); - endPos = min(endPos - boxStart, static_cast<int>(len())); - - if (startPos >= endPos) - return IntRect(); - - RenderText* text = textRenderer(); - ASSERT(text); - - RenderStyle* style = text->style(); - ASSERT(style); - - FloatRect selectionRect; - - // Figure out which text chunk part is hit - const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); - for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) - selectionRect.unite(selectionRectForTextChunkPart(*it, startPos, endPos, style)); - - // Resepect possible chunk transformation - if (m_chunkTransformation.isIdentity()) - return enclosingIntRect(selectionRect); - - return enclosingIntRect(m_chunkTransformation.mapRect(selectionRect)); -} - -void SVGInlineTextBox::paint(PaintInfo& paintInfo, int, int) -{ - ASSERT(paintInfo.shouldPaintWithinRoot(renderer())); - ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); - ASSERT(truncation() == cNoTruncation); - - if (renderer()->style()->visibility() != VISIBLE) - return; - - // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox. - // If we ever need that for SVG, it's very easy to refactor and reuse the code. - - RenderObject* parentRenderer = parent()->renderer(); - ASSERT(parentRenderer); - - RenderStyle* style = parentRenderer->style(); - ASSERT(style); - - const SVGRenderStyle* svgStyle = style->svgStyle(); - ASSERT(svgStyle); - - bool hasFill = svgStyle->hasFill(); - bool hasStroke = svgStyle->hasStroke(); - bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; - - // Determine whether or not we're selected. - bool isPrinting = parentRenderer->document()->printing(); - bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone; - if (!hasSelection && paintSelectedTextOnly) - return; - - RenderStyle* selectionStyle = style; - if (hasSelection) { - selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION); - if (selectionStyle) { - const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle(); - ASSERT(svgSelectionStyle); - - if (!hasFill) - hasFill = svgSelectionStyle->hasFill(); - if (!hasStroke) - hasStroke = svgSelectionStyle->hasStroke(); - } else - selectionStyle = style; - } - - // Compute text match marker rects. It needs to traverse all text chunk parts to figure - // out the union selection rect of all text chunk parts that contribute to the selection. - computeTextMatchMarkerRect(style); - ASSERT(!m_currentChunkPart.isValid()); - - const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); - for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) { - ASSERT(!m_paintingResource); - - // constructTextRun() uses the current chunk part to figure out what text to render. - m_currentChunkPart = *it; - paintInfo.context->save(); - - // Prepare context and draw text - if (!m_chunkTransformation.isIdentity()) - paintInfo.context->concatCTM(m_chunkTransformation); - - Vector<SVGChar>::const_iterator firstCharacter = m_currentChunkPart.firstCharacter; - AffineTransform characterTransform = firstCharacter->characterTransform(); - if (!characterTransform.isIdentity()) - paintInfo.context->concatCTM(characterTransform); - - FloatPoint textOrigin(firstCharacter->x, firstCharacter->y); - - // Draw background once (not in both fill/stroke phases) - if (!isPrinting && !paintSelectedTextOnly && hasSelection) - paintSelection(paintInfo.context, textOrigin, style); - - // 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, textOrigin, UNDERLINE, hasSelection); - if (decorations & OVERLINE) - paintDecoration(paintInfo.context, textOrigin, OVERLINE, hasSelection); - - // Fill text - if (hasFill) { - m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode; - paintText(paintInfo.context, textOrigin, style, selectionStyle, hasSelection, paintSelectedTextOnly); - } - - // Stroke text - if (hasStroke) { - m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode; - paintText(paintInfo.context, textOrigin, style, selectionStyle, hasSelection, paintSelectedTextOnly); - } - - // 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, textOrigin, LINE_THROUGH, hasSelection); - - m_paintingResourceMode = ApplyToDefaultMode; - paintInfo.context->restore(); - } - - m_currentChunkPart = SVGTextChunkPart(); - ASSERT(!m_paintingResource); -} - -bool SVGInlineTextBox::acquirePaintingResource(GraphicsContext*& context, RenderObject* renderer, RenderStyle* style) -{ - ASSERT(renderer); - ASSERT(style); - ASSERT(m_paintingResourceMode != ApplyToDefaultMode); - - if (m_paintingResourceMode & ApplyToFillMode) - m_paintingResource = RenderSVGResource::fillPaintingResource(renderer, style); - else if (m_paintingResourceMode & ApplyToStrokeMode) - m_paintingResource = RenderSVGResource::strokePaintingResource(renderer, style); - else { - // We're either called for stroking or filling. - ASSERT_NOT_REACHED(); - } - - if (!m_paintingResource) - return false; - - m_paintingResource->applyResource(renderer, style, context, m_paintingResourceMode); - return true; -} - -void SVGInlineTextBox::releasePaintingResource(GraphicsContext*& context) -{ - ASSERT(m_paintingResource); - - RenderObject* parentRenderer = parent()->renderer(); - ASSERT(parentRenderer); - - m_paintingResource->postApplyResource(parentRenderer, context, m_paintingResourceMode); - m_paintingResource = 0; -} - -bool SVGInlineTextBox::prepareGraphicsContextForTextPainting(GraphicsContext*& context, TextRun& textRun, RenderStyle* style) -{ - bool acquiredResource = acquirePaintingResource(context, parent()->renderer(), style); - -#if ENABLE(SVG_FONTS) - // SVG Fonts need access to the painting resource used to draw the current text chunk. - if (acquiredResource) - textRun.setActivePaintingResource(m_paintingResource); -#endif - - return acquiredResource; -} - -void SVGInlineTextBox::restoreGraphicsContextAfterTextPainting(GraphicsContext*& context, TextRun& textRun) -{ - releasePaintingResource(context); - -#if ENABLE(SVG_FONTS) - textRun.setActivePaintingResource(0); -#endif -} - -TextRun SVGInlineTextBox::constructTextRun(RenderStyle* style) const -{ - ASSERT(m_currentChunkPart.isValid()); - return svgTextRunForInlineTextBox(textRenderer()->text()->characters() + start() + m_currentChunkPart.offset, m_currentChunkPart.length, style, this); -} - -void SVGInlineTextBox::mapStartEndPositionsIntoChunkPartCoordinates(int& startPos, int& endPos, const SVGTextChunkPart& part) const -{ - if (startPos >= endPos) - return; - - // Take <text x="10 50 100">ABC</text> as example. We're called three times from paint(), because all absolute positioned - // characters are drawn on their own. For each of them we want to find out whehter it's selected. startPos=0, endPos=1 - // could mean A, B or C is hit, depending on which chunk part is processed at the moment. With the offset & length values - // of each chunk part we can easily find out which one is meant to be selected. Bail out for the other chunk parts. - // If starPos is behind the current chunk or the endPos ends before this text chunk part, we're not meant to be selected. - if (startPos >= part.offset + part.length || endPos <= part.offset) { - startPos = 0; - endPos = -1; - return; - } - - // The current processed chunk part is hit. When painting the selection, constructTextRun() builds - // a TextRun object whose startPos is 0 and endPos is chunk part length. The code below maps the incoming - // startPos/endPos range into a [0, part length] coordinate system, relative to the current chunk part. - if (startPos < part.offset) - startPos = 0; - else - startPos -= part.offset; - - if (endPos > part.offset + part.length) - endPos = part.length; - else { - ASSERT(endPos >= part.offset); - endPos -= part.offset; - } - - ASSERT(startPos < endPos); -} - -void SVGInlineTextBox::selectionStartEnd(int& startPos, int& endPos) -{ - InlineTextBox::selectionStartEnd(startPos, endPos); - - if (!m_currentChunkPart.isValid()) - return; - - mapStartEndPositionsIntoChunkPartCoordinates(startPos, endPos, m_currentChunkPart); -} - -void SVGInlineTextBox::computeTextMatchMarkerRect(RenderStyle* style) -{ - ASSERT(!m_currentChunkPart.isValid()); - Node* node = renderer()->node(); - if (!node || !node->inDocument()) - return; - - Document* document = renderer()->document(); - Vector<DocumentMarker> markers = document->markers()->markersForNode(renderer()->node()); - - Vector<DocumentMarker>::iterator markerEnd = markers.end(); - for (Vector<DocumentMarker>::iterator markerIt = markers.begin(); markerIt != markerEnd; ++markerIt) { - const DocumentMarker& marker = *markerIt; - - // SVG is only interessted in the TextMatch marker, for now. - if (marker.type != DocumentMarker::TextMatch) - continue; - - FloatRect markerRect; - int partStartPos = max(marker.startOffset - start(), static_cast<unsigned>(0)); - int partEndPos = min(marker.endOffset - start(), static_cast<unsigned>(len())); - - // Iterate over all text chunk parts, to see which ones have to be highlighted - const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); - for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) - markerRect.unite(selectionRectForTextChunkPart(*it, partStartPos, partEndPos, style)); - - if (!m_chunkTransformation.isIdentity()) - markerRect = m_chunkTransformation.mapRect(markerRect); - - document->markers()->setRenderedRectForMarker(node, marker, renderer()->localToAbsoluteQuad(markerRect).enclosingBoundingBox()); - } -} - -static inline float positionOffsetForDecoration(ETextDecoration decoration, const Font& font, 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) - return font.ascent() + thickness * 1.5f; - if (decoration == OVERLINE) - return thickness; - if (decoration == LINE_THROUGH) - return font.ascent() * 5.0f / 8.0f; - - ASSERT_NOT_REACHED(); - return 0.0f; -} - -static inline float thicknessForDecoration(ETextDecoration, const Font& font) -{ - // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified. - // Compatible with Batik/Opera - return font.size() / 20.0f; -} - -static inline RenderObject* findRenderObjectDefininingTextDecoration(InlineFlowBox* parentBox, ETextDecoration decoration) -{ - // Lookup render object which has text-decoration set. - RenderObject* renderer = 0; - while (parentBox) { - renderer = parentBox->renderer(); - - // Explicitely check textDecoration() not textDecorationsInEffect(), which is inherited to - // children, as we want to lookup the render object whose style defined the text-decoration. - if (renderer->style() && renderer->style()->textDecoration() & decoration) - break; - - parentBox = parentBox->parent(); - } - - ASSERT(renderer); - return renderer; -} - -void SVGInlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& textOrigin, ETextDecoration decoration, bool hasSelection) -{ - // Find out which render style defined the text-decoration, as its fill/stroke properties have to be used for drawing instead of ours. - RenderObject* decorationRenderer = findRenderObjectDefininingTextDecoration(parent(), decoration); - RenderStyle* decorationStyle = decorationRenderer->style(); - ASSERT(decorationStyle); - - const SVGRenderStyle* svgDecorationStyle = decorationStyle->svgStyle(); - ASSERT(svgDecorationStyle); - - bool hasDecorationFill = svgDecorationStyle->hasFill(); - bool hasDecorationStroke = svgDecorationStyle->hasStroke(); - - if (hasSelection) { - if (RenderStyle* pseudoStyle = decorationRenderer->getCachedPseudoStyle(SELECTION)) { - decorationStyle = pseudoStyle; - - svgDecorationStyle = decorationStyle->svgStyle(); - ASSERT(svgDecorationStyle); - - if (!hasDecorationFill) - hasDecorationFill = svgDecorationStyle->hasFill(); - if (!hasDecorationStroke) - hasDecorationStroke = svgDecorationStyle->hasStroke(); - } - } - - if (decorationStyle->visibility() == HIDDEN) - return; - - if (hasDecorationFill) { - m_paintingResourceMode = ApplyToFillMode; - paintDecorationWithStyle(context, textOrigin, decorationRenderer, decoration); - } - - if (hasDecorationStroke) { - m_paintingResourceMode = ApplyToStrokeMode; - paintDecorationWithStyle(context, textOrigin, decorationRenderer, decoration); - } -} - -void SVGInlineTextBox::paintDecorationWithStyle(GraphicsContext* context, const FloatPoint& textOrigin, RenderObject* decorationRenderer, ETextDecoration decoration) -{ - ASSERT(!m_paintingResource); - ASSERT(m_paintingResourceMode != ApplyToDefaultMode); - ASSERT(m_currentChunkPart.isValid()); - - RenderStyle* decorationStyle = decorationRenderer->style(); - ASSERT(decorationStyle); - - const Font& font = decorationStyle->font(); - - // The initial y value refers to overline position. - float thickness = thicknessForDecoration(decoration, font); - float x = textOrigin.x(); - float y = textOrigin.y() - font.ascent() + positionOffsetForDecoration(decoration, font, thickness); - - context->save(); - context->beginPath(); - context->addPath(Path::createRectangle(FloatRect(x, y, m_currentChunkPart.width, thickness))); - - if (acquirePaintingResource(context, decorationRenderer, decorationStyle)) - releasePaintingResource(context); - - context->restore(); -} - -void SVGInlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& textOrigin, RenderStyle* style) -{ - // See if we have a selection to paint at all. - int startPos, endPos; - selectionStartEnd(startPos, endPos); - if (startPos >= endPos) - return; - - Color backgroundColor = renderer()->selectionBackgroundColor(); - if (!backgroundColor.isValid() || !backgroundColor.alpha()) - return; - - const Font& font = style->font(); - - FloatPoint selectionOrigin = textOrigin; - selectionOrigin.move(0, -font.ascent()); - - context->save(); - context->setFillColor(backgroundColor, style->colorSpace()); - context->fillRect(font.selectionRectForText(constructTextRun(style), selectionOrigin, m_currentChunkPart.height, startPos, endPos), backgroundColor, style->colorSpace()); - context->restore(); -} - -void SVGInlineTextBox::paintTextWithShadows(GraphicsContext* context, const FloatPoint& textOrigin, RenderStyle* style, TextRun& textRun, int startPos, int endPos) -{ - const Font& font = style->font(); - const ShadowData* shadow = style->textShadow(); - - FloatRect shadowRect(FloatPoint(textOrigin.x(), textOrigin.y() - font.ascent()), FloatSize(m_currentChunkPart.width, m_currentChunkPart.height)); - do { - if (!prepareGraphicsContextForTextPainting(context, textRun, style)) - break; - - FloatSize extraOffset; - if (shadow) - extraOffset = applyShadowToGraphicsContext(context, shadow, shadowRect, false /* stroked */, true /* opaque */); - - font.drawText(context, textRun, textOrigin + extraOffset, startPos, endPos); - restoreGraphicsContextAfterTextPainting(context, textRun); - - if (!shadow) - break; - - if (shadow->next()) - context->restore(); - else - context->clearShadow(); - - shadow = shadow->next(); - } while (shadow); -} - -void SVGInlineTextBox::paintText(GraphicsContext* context, const FloatPoint& textOrigin, RenderStyle* style, RenderStyle* selectionStyle, bool hasSelection, bool paintSelectedTextOnly) -{ - ASSERT(style); - ASSERT(selectionStyle); - ASSERT(m_currentChunkPart.isValid()); - - int startPos = 0; - int endPos = 0; - selectionStartEnd(startPos, endPos); - - // Fast path if there is no selection, just draw the whole chunk part using the regular style - TextRun textRun(constructTextRun(style)); - if (!hasSelection || startPos >= endPos) { - paintTextWithShadows(context, textOrigin, style, textRun, 0, m_currentChunkPart.length); - return; - } - - // Eventually draw text using regular style until the start position of the selection - if (startPos > 0 && !paintSelectedTextOnly) - paintTextWithShadows(context, textOrigin, style, textRun, 0, startPos); - - // Draw text using selection style from the start to the end position of the selection - if (style != selectionStyle) - SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, selectionStyle); - - TextRun selectionTextRun(constructTextRun(selectionStyle)); - paintTextWithShadows(context, textOrigin, selectionStyle, textRun, startPos, endPos); - - if (style != selectionStyle) - SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, style); - - // Eventually draw text using regular style from the end position of the selection to the end of the current chunk part - if (endPos < m_currentChunkPart.length && !paintSelectedTextOnly) - paintTextWithShadows(context, textOrigin, style, textRun, endPos, m_currentChunkPart.length); -} - -void SVGInlineTextBox::buildLayoutInformation(SVGCharacterLayoutInfo& info, SVGLastGlyphInfo& lastGlyph) -{ - RenderText* textRenderer = this->textRenderer(); - ASSERT(textRenderer); - - RenderStyle* style = textRenderer->style(); - ASSERT(style); - - RenderObject* parentRenderer = parent()->renderer(); - ASSERT(parentRenderer); - ASSERT(parentRenderer->node()); - ASSERT(parentRenderer->node()->isSVGElement()); - SVGElement* lengthContext = static_cast<SVGElement*>(parentRenderer->node()); - - const Font& font = style->font(); - const UChar* characters = textRenderer->characters(); - - TextDirection textDirection = direction(); - unsigned startPosition = start(); - unsigned endPosition = end(); - unsigned length = len(); - - const SVGRenderStyle* svgStyle = style->svgStyle(); - bool isVerticalText = isVerticalWritingMode(svgStyle); - - int charsConsumed = 0; - for (unsigned i = 0; i < length; i += charsConsumed) { - SVGChar svgChar; - - if (info.inPathLayout()) - svgChar.pathData = SVGCharOnPath::create(); - - float glyphWidth = 0.0f; - float glyphHeight = 0.0f; - String glyphName; - String unicodeString; - measureCharacter(style, i, charsConsumed, glyphName, unicodeString, glyphWidth, glyphHeight); - - bool assignedX = false; - bool assignedY = false; - - if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) { - if (!isVerticalText) - svgChar.newTextChunk = true; - - assignedX = true; - svgChar.drawnSeperated = true; - info.curx = info.xValueNext(); - } - - if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) { - if (isVerticalText) - svgChar.newTextChunk = true; - - assignedY = true; - svgChar.drawnSeperated = true; - info.cury = info.yValueNext(); - } - - float dx = 0.0f; - float dy = 0.0f; - - // Apply x-axis shift - if (info.dxValueAvailable()) { - svgChar.drawnSeperated = true; - - dx = info.dxValueNext(); - info.dx += dx; - - if (!info.inPathLayout()) - info.curx += dx; - } - - // Apply y-axis shift - if (info.dyValueAvailable()) { - svgChar.drawnSeperated = true; - - dy = info.dyValueNext(); - info.dy += dy; - - if (!info.inPathLayout()) - info.cury += dy; - } - - // Take letter & word spacing and kerning into account - float spacing = font.letterSpacing() + calculateCSSKerning(lengthContext, style); - - const UChar* currentCharacter = characters + (textDirection == RTL ? endPosition - i : startPosition + i); - const UChar* lastCharacter = 0; - - if (textDirection == RTL) { - if (i < endPosition) - lastCharacter = characters + endPosition - i + 1; - } else { - if (i > 0) - lastCharacter = characters + startPosition + i - 1; - } - - // FIXME: SVG Kerning doesn't get applied on texts on path. - bool appliedSVGKerning = applySVGKerning(info, style, lastGlyph, unicodeString, glyphName, isVerticalText); - if (info.nextDrawnSeperated || spacing != 0.0f || appliedSVGKerning) { - info.nextDrawnSeperated = false; - svgChar.drawnSeperated = true; - } - - if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) { - spacing += font.wordSpacing(); - - if (spacing != 0.0f && !info.inPathLayout()) - info.nextDrawnSeperated = true; - } - - float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter); - - float xOrientationShift = 0.0f; - float yOrientationShift = 0.0f; - float glyphAdvance = applyGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight, - font, svgChar, xOrientationShift, yOrientationShift); - - // Handle textPath layout mode - if (info.inPathLayout()) { - float extraAdvance = isVerticalText ? dy : dx; - float newOffset = FLT_MIN; - - if (assignedX && !isVerticalText) - newOffset = info.curx; - else if (assignedY && isVerticalText) - newOffset = info.cury; - - float correctedGlyphAdvance = glyphAdvance; - - // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations - if (info.pathTextLength && info.pathChunkLength) { - if (isVerticalText) { - svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength; - spacing *= svgChar.pathData->yScale; - correctedGlyphAdvance *= svgChar.pathData->yScale; - } else { - svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength; - spacing *= svgChar.pathData->xScale; - correctedGlyphAdvance *= svgChar.pathData->xScale; - } - } - - // Handle letter & word spacing on text path - float pathExtraAdvance = info.pathExtraAdvance; - info.pathExtraAdvance += spacing; - - svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset); - svgChar.drawnSeperated = true; - - info.pathExtraAdvance = pathExtraAdvance; - } - - // Apply rotation - if (info.angleValueAvailable()) - info.angle = info.angleValueNext(); - - // Apply baseline-shift - if (info.baselineShiftValueAvailable()) { - svgChar.drawnSeperated = true; - float shift = info.baselineShiftValueNext(); - - if (isVerticalText) - info.shiftx += shift; - else - info.shifty -= shift; - } - - // Take dominant-baseline / alignment-baseline into account - yOrientationShift += alignmentBaselineToShift(isVerticalText, textRenderer, font); - - svgChar.x = info.curx; - svgChar.y = info.cury; - svgChar.angle = info.angle; - - // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation - if (!info.inPathLayout()) { - svgChar.x += info.shiftx + xOrientationShift; - svgChar.y += info.shifty + yOrientationShift; - - if (orientationAngle != 0.0f) - svgChar.angle += orientationAngle; - - if (svgChar.angle != 0.0f) - svgChar.drawnSeperated = true; - } else { - svgChar.pathData->orientationAngle = orientationAngle; - - if (isVerticalText) - svgChar.angle -= 90.0f; - - svgChar.pathData->xShift = info.shiftx + xOrientationShift; - svgChar.pathData->yShift = info.shifty + yOrientationShift; - - // Translate to glyph midpoint - if (isVerticalText) { - svgChar.pathData->xShift += info.dx; - svgChar.pathData->yShift -= glyphAdvance / 2.0f; - } else { - svgChar.pathData->xShift -= glyphAdvance / 2.0f; - svgChar.pathData->yShift += info.dy; - } - } - - // Advance to new position - if (isVerticalText) { - svgChar.drawnSeperated = true; - info.cury += glyphAdvance + spacing; - } else - info.curx += glyphAdvance + spacing; - - // Advance to next character group - for (int k = 0; k < charsConsumed; ++k) { - info.svgChars.append(svgChar); - info.processedSingleCharacter(); - svgChar.drawnSeperated = false; - svgChar.newTextChunk = false; - } - } -} - -FloatRect SVGInlineTextBox::calculateGlyphBoundaries(RenderStyle* style, int position, const SVGChar& character) const -{ - int charsConsumed = 0; - String glyphName; - String unicodeString; - float glyphWidth = 0.0f; - float glyphHeight = 0.0f; - measureCharacter(style, position, charsConsumed, glyphName, unicodeString, glyphWidth, glyphHeight); - - FloatRect glyphRect(character.x, character.y - style->font().ascent(), glyphWidth, glyphHeight); - glyphRect = character.characterTransform().mapRect(glyphRect); - if (m_chunkTransformation.isIdentity()) - return glyphRect; - - return m_chunkTransformation.mapRect(glyphRect); -} - -IntRect SVGInlineTextBox::calculateBoundaries() const -{ - FloatRect textRect; - int baseline = baselinePosition(true); - - const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); - for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) - textRect.unite(it->firstCharacter->characterTransform().mapRect(FloatRect(it->firstCharacter->x, it->firstCharacter->y - baseline, it->width, it->height))); - - if (m_chunkTransformation.isIdentity()) - return enclosingIntRect(textRect); - - return enclosingIntRect(m_chunkTransformation.mapRect(textRect)); -} - -} // namespace WebCore - -#endif diff --git a/WebCore/rendering/SVGRenderSupport.cpp b/WebCore/rendering/SVGRenderSupport.cpp index 644ff70..608cf4d 100644 --- a/WebCore/rendering/SVGRenderSupport.cpp +++ b/WebCore/rendering/SVGRenderSupport.cpp @@ -32,7 +32,7 @@ #include "ImageBuffer.h" #include "NodeRenderStyle.h" #include "RenderLayer.h" -#include "RenderPath.h" +#include "RenderSVGPath.h" #include "RenderSVGResource.h" #include "RenderSVGResourceClipper.h" #include "RenderSVGResourceFilter.h" @@ -220,9 +220,9 @@ void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) // 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()) { - // When the layout size changed and when using relative values tell the RenderPath to update its Path object - if (child->isRenderPath()) - toRenderPath(child)->setNeedsPathUpdate(); + // When the layout size changed and when using relative values tell the RenderSVGPath to update its Path object + if (child->isSVGPath()) + toRenderSVGPath(child)->setNeedsPathUpdate(); needsLayout = true; } @@ -347,16 +347,6 @@ void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const } } -const RenderObject* SVGRenderSupport::findTextRootObject(const RenderObject* start) -{ - while (start && !start->isSVGText()) - start = start->parent(); - ASSERT(start); - ASSERT(start->isSVGText()); - - return start; -} - } #endif diff --git a/WebCore/rendering/SVGRenderSupport.h b/WebCore/rendering/SVGRenderSupport.h index 54622d2..7814863 100644 --- a/WebCore/rendering/SVGRenderSupport.h +++ b/WebCore/rendering/SVGRenderSupport.h @@ -70,7 +70,6 @@ public: static void applyStrokeStyleToContext(GraphicsContext*, const RenderStyle*, const RenderObject*); // FIXME: These methods do not belong here. - static const RenderObject* findTextRootObject(const RenderObject* start); static const RenderSVGRoot* findTreeRootObject(const RenderObject* start); private: diff --git a/WebCore/rendering/SVGRenderTreeAsText.cpp b/WebCore/rendering/SVGRenderTreeAsText.cpp index 4e26f52..b7f7f95 100644 --- a/WebCore/rendering/SVGRenderTreeAsText.cpp +++ b/WebCore/rendering/SVGRenderTreeAsText.cpp @@ -40,11 +40,11 @@ #include "PatternAttributes.h" #include "RadialGradientAttributes.h" #include "RenderImage.h" -#include "RenderPath.h" #include "RenderSVGContainer.h" #include "RenderSVGGradientStop.h" #include "RenderSVGImage.h" #include "RenderSVGInlineText.h" +#include "RenderSVGPath.h" #include "RenderSVGResourceClipper.h" #include "RenderSVGResourceFilter.h" #include "RenderSVGResourceGradient.h" @@ -57,15 +57,21 @@ #include "RenderSVGRoot.h" #include "RenderSVGText.h" #include "RenderTreeAsText.h" -#include "SVGCharacterLayoutInfo.h" +#include "SVGCircleElement.h" +#include "SVGEllipseElement.h" #include "SVGInlineTextBox.h" +#include "SVGLineElement.h" #include "SVGLinearGradientElement.h" +#include "SVGPathElement.h" +#include "SVGPathParserFactory.h" #include "SVGPatternElement.h" +#include "SVGPointList.h" +#include "SVGPolyElement.h" #include "SVGRadialGradientElement.h" +#include "SVGRectElement.h" #include "SVGRootInlineBox.h" #include "SVGStopElement.h" #include "SVGStyledElement.h" -#include "SVGTextLayoutUtilities.h" #include <math.h> @@ -316,12 +322,13 @@ static void writeStyle(TextStream& ts, const RenderObject& object) writeNameValuePair(ts, "transform", object.localTransform()); writeIfNotDefault(ts, "image rendering", svgStyle->imageRendering(), SVGRenderStyle::initialImageRendering()); writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity()); - if (object.isRenderPath()) { - const RenderPath& path = static_cast<const RenderPath&>(object); + if (object.isSVGPath()) { + const RenderSVGPath& path = static_cast<const RenderSVGPath&>(object); ASSERT(path.node()); ASSERT(path.node()->isSVGElement()); - if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(const_cast<RenderPath*>(&path), path.style())) { + Color fallbackColor; + if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(const_cast<RenderSVGPath*>(&path), path.style(), fallbackColor)) { TextStreamSeparator s(" "); ts << " [stroke={" << s; writeSVGPaintingResource(ts, strokePaintingResource); @@ -348,7 +355,7 @@ static void writeStyle(TextStream& ts, const RenderObject& object) ts << "}]"; } - if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(const_cast<RenderPath*>(&path), path.style())) { + if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(const_cast<RenderSVGPath*>(&path), path.style(), fallbackColor)) { TextStreamSeparator s(" "); ts << " [fill={" << s; writeSVGPaintingResource(ts, fillPaintingResource); @@ -372,10 +379,46 @@ static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& obj return ts; } -static TextStream& operator<<(TextStream& ts, const RenderPath& path) +static TextStream& operator<<(TextStream& ts, const RenderSVGPath& path) { writePositionAndStyle(ts, path); - writeNameAndQuotedValue(ts, "data", path.path().debugString()); + + ASSERT(path.node()->isSVGElement()); + SVGElement* svgElement = static_cast<SVGElement*>(path.node()); + + if (svgElement->hasTagName(SVGNames::rectTag)) { + SVGRectElement* element = static_cast<SVGRectElement*>(svgElement); + writeNameValuePair(ts, "x", element->x().value(element)); + writeNameValuePair(ts, "y", element->y().value(element)); + writeNameValuePair(ts, "width", element->width().value(element)); + writeNameValuePair(ts, "height", element->height().value(element)); + } else if (svgElement->hasTagName(SVGNames::lineTag)) { + SVGLineElement* element = static_cast<SVGLineElement*>(svgElement); + writeNameValuePair(ts, "x1", element->x1().value(element)); + writeNameValuePair(ts, "y1", element->y1().value(element)); + writeNameValuePair(ts, "x2", element->x2().value(element)); + writeNameValuePair(ts, "y2", element->y2().value(element)); + } else if (svgElement->hasTagName(SVGNames::ellipseTag)) { + SVGEllipseElement* element = static_cast<SVGEllipseElement*>(svgElement); + writeNameValuePair(ts, "cx", element->cx().value(element)); + writeNameValuePair(ts, "cy", element->cy().value(element)); + writeNameValuePair(ts, "rx", element->rx().value(element)); + writeNameValuePair(ts, "ry", element->ry().value(element)); + } else if (svgElement->hasTagName(SVGNames::circleTag)) { + SVGCircleElement* element = static_cast<SVGCircleElement*>(svgElement); + writeNameValuePair(ts, "cx", element->cx().value(element)); + writeNameValuePair(ts, "cy", element->cy().value(element)); + writeNameValuePair(ts, "r", element->r().value(element)); + } else if (svgElement->hasTagName(SVGNames::polygonTag) || svgElement->hasTagName(SVGNames::polylineTag)) { + SVGPolyElement* element = static_cast<SVGPolyElement*>(svgElement); + writeNameAndQuotedValue(ts, "points", element->points()->valueAsString()); + } else if (svgElement->hasTagName(SVGNames::pathTag)) { + SVGPathElement* element = static_cast<SVGPathElement*>(svgElement); + String pathString; + SVGPathParserFactory::self()->buildStringFromSVGPathSegList(element->pathSegList(), pathString, UnalteredParsing); + writeNameAndQuotedValue(ts, "data", pathString); + } else + ASSERT_NOT_REACHED(); return ts; } @@ -387,116 +430,72 @@ static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root) static void writeRenderSVGTextBox(TextStream& ts, const RenderBlock& text) { SVGRootInlineBox* box = static_cast<SVGRootInlineBox*>(text.firstRootBox()); - if (!box) return; - Vector<SVGTextChunk>& chunks = const_cast<Vector<SVGTextChunk>& >(box->svgTextChunks()); - ts << " at (" << text.x() << "," << text.y() << ") size " << box->logicalWidth() << "x" << box->logicalHeight() << " contains " << chunks.size() << " chunk(s)"; + ts << " at (" << text.x() << "," << text.y() << ") size " << box->logicalWidth() << "x" << box->logicalHeight(); + + // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now. + ts << " contains 1 chunk(s)"; if (text.parent() && (text.parent()->style()->visitedDependentColor(CSSPropertyColor) != text.style()->visitedDependentColor(CSSPropertyColor))) writeNameValuePair(ts, "color", text.style()->visitedDependentColor(CSSPropertyColor).name()); } -static inline bool containsInlineTextBox(SVGTextChunk& chunk, SVGInlineTextBox* box) -{ - Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); - Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); - - bool found = false; - for (; boxIt != boxEnd; ++boxIt) { - SVGInlineBoxCharacterRange& range = *boxIt; - - if (box == static_cast<SVGInlineTextBox*>(range.box)) { - found = true; - break; - } - } - - return found; -} - static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent) { - SVGRootInlineBox* rootBox = textBox->svgRootInlineBox(); - if (!rootBox) + Vector<SVGTextFragment>& fragments = textBox->textFragments(); + if (fragments.isEmpty()) return; - Vector<SVGTextChunk>& chunks = const_cast<Vector<SVGTextChunk>& >(rootBox->svgTextChunks()); - - Vector<SVGTextChunk>::iterator it = chunks.begin(); - Vector<SVGTextChunk>::iterator end = chunks.end(); + RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer()); + ASSERT(textRenderer); - // Write text chunks - unsigned int i = 1; - for (; it != end; ++it) { - SVGTextChunk& cur = *it; - - // Write inline box character ranges - Vector<SVGInlineBoxCharacterRange>::iterator boxIt = cur.boxes.begin(); - Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = cur.boxes.end(); - - if (!containsInlineTextBox(cur, textBox)) { - i++; - continue; - } + const SVGRenderStyle* svgStyle = textRenderer->style()->svgStyle(); + String text = textBox->textRenderer()->text(); + unsigned fragmentsSize = fragments.size(); + for (unsigned i = 0; i < fragmentsSize; ++i) { + SVGTextFragment& fragment = fragments.at(i); writeIndent(ts, indent + 1); - unsigned int j = 1; - ts << "chunk " << i << " "; + unsigned startOffset = fragment.positionListOffset; + unsigned endOffset = fragment.positionListOffset + fragment.length; - if (cur.anchor == TA_MIDDLE) { + // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now. + ts << "chunk 1 "; + ETextAnchor anchor = svgStyle->textAnchor(); + bool isVerticalText = svgStyle->isVerticalWritingMode(); + if (anchor == TA_MIDDLE) { ts << "(middle anchor"; - if (cur.isVerticalText) + if (isVerticalText) ts << ", vertical"; ts << ") "; - } else if (cur.anchor == TA_END) { + } else if (anchor == TA_END) { ts << "(end anchor"; - if (cur.isVerticalText) + if (isVerticalText) ts << ", vertical"; ts << ") "; - } else if (cur.isVerticalText) + } else if (isVerticalText) ts << "(vertical) "; + startOffset -= textBox->start(); + endOffset -= textBox->start(); + // </hack> + + ts << "text run " << i + 1 << " at (" << fragment.x << "," << fragment.y << ")"; + ts << " startOffset " << startOffset << " endOffset " << endOffset; + if (isVerticalText) + ts << " height " << fragment.height; + else + ts << " width " << fragment.width; - unsigned int totalOffset = 0; - - for (; boxIt != boxEnd; ++boxIt) { - SVGInlineBoxCharacterRange& range = *boxIt; - - unsigned int offset = range.endOffset - range.startOffset; - ASSERT(cur.start + totalOffset <= cur.end); - - totalOffset += offset; - - if (textBox != static_cast<SVGInlineTextBox*>(range.box)) { - j++; - continue; - } - - FloatPoint topLeft = topLeftPositionOfCharacterRange(cur.start + totalOffset - offset, cur.start + totalOffset); - - ts << "text run " << j << " at (" << topLeft.x() << "," << topLeft.y() << ") "; - ts << "startOffset " << range.startOffset << " endOffset " << range.endOffset; - - if (cur.isVerticalText) - ts << " height " << cummulatedHeightOfInlineBoxCharacterRange(range); - else - ts << " width " << cummulatedWidthOfInlineBoxCharacterRange(range); - - if (textBox->direction() == RTL || textBox->m_dirOverride) { - ts << (textBox->direction() == RTL ? " RTL" : " LTR"); - - if (textBox->m_dirOverride) - ts << " override"; - } - - ts << ": " << quoteAndEscapeNonPrintables(String(textBox->textRenderer()->text()).substring(textBox->start() + range.startOffset, offset)) << "\n"; - - j++; + if (!textBox->isLeftToRightDirection() || textBox->m_dirOverride) { + ts << (textBox->isLeftToRightDirection() ? " LTR" : " RTL"); + if (textBox->m_dirOverride) + ts << " override"; } - i++; + ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.positionListOffset, fragment.length)) << "\n"; } } @@ -582,7 +581,9 @@ void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int i // Dump final results that are used for rendering. No use in asking SVGPatternElement for its patternUnits(), as it may // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties() - PatternAttributes attributes = static_cast<SVGPatternElement*>(pattern->node())->collectPatternProperties(); + PatternAttributes attributes; + static_cast<SVGPatternElement*>(pattern->node())->collectPatternAttributes(attributes); + writeNameValuePair(ts, "patternUnits", boundingBoxModeString(attributes.boundingBoxMode())); writeNameValuePair(ts, "patternContentUnits", boundingBoxModeString(attributes.boundingBoxModeContent())); @@ -597,7 +598,8 @@ void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int i // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() SVGLinearGradientElement* linearGradientElement = static_cast<SVGLinearGradientElement*>(gradient->node()); - LinearGradientAttributes attributes = linearGradientElement->collectGradientProperties(); + LinearGradientAttributes attributes; + linearGradientElement->collectGradientAttributes(attributes); writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.boundingBoxMode()); FloatPoint startPoint; @@ -612,7 +614,8 @@ void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int i // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties() SVGRadialGradientElement* radialGradientElement = static_cast<SVGRadialGradientElement*>(gradient->node()); - RadialGradientAttributes attributes = radialGradientElement->collectGradientProperties(); + RadialGradientAttributes attributes; + radialGradientElement->collectGradientAttributes(attributes); writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.boundingBoxMode()); FloatPoint focalPoint; @@ -672,7 +675,7 @@ void writeSVGImage(TextStream& ts, const RenderSVGImage& image, int indent) writeResources(ts, image, indent); } -void write(TextStream& ts, const RenderPath& path, int indent) +void write(TextStream& ts, const RenderSVGPath& path, int indent) { writeStandardPrefix(ts, path, indent); ts << path << "\n"; diff --git a/WebCore/rendering/SVGRenderTreeAsText.h b/WebCore/rendering/SVGRenderTreeAsText.h index e279cfe..4e9ba5d 100644 --- a/WebCore/rendering/SVGRenderTreeAsText.h +++ b/WebCore/rendering/SVGRenderTreeAsText.h @@ -39,16 +39,16 @@ namespace WebCore { class RenderBlock; class RenderImage; class RenderObject; - class RenderPath; class RenderSVGGradientStop; class RenderSVGImage; + class RenderSVGPath; class RenderSVGRoot; class RenderText; class AffineTransform; class SVGUnitTypes; // functions used by the main RenderTreeAsText code -void write(TextStream&, const RenderPath&, int indent); +void write(TextStream&, const RenderSVGPath&, int indent); void write(TextStream&, const RenderSVGRoot&, int indent); void writeSVGGradientStop(TextStream&, const RenderSVGGradientStop&, int indent); void writeSVGResourceContainer(TextStream&, const RenderObject&, int indent); diff --git a/WebCore/rendering/SVGResources.cpp b/WebCore/rendering/SVGResources.cpp index 799301b..f796f3b 100644 --- a/WebCore/rendering/SVGResources.cpp +++ b/WebCore/rendering/SVGResources.cpp @@ -160,11 +160,17 @@ static inline RenderSVGResourceContainer* paintingResourceFromSVGPaint(Document* return 0; id = SVGURIReference::getTarget(paint->uri()); - if (RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(document, id)) - return container; + RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(document, id); + if (!container) { + hasPendingResource = true; + return 0; + } + + RenderSVGResourceType resourceType = container->resourceType(); + if (resourceType != PatternResourceType && resourceType != LinearGradientResourceType && resourceType != RadialGradientResourceType) + return 0; - hasPendingResource = true; - return 0; + return container; } static inline void registerPendingResource(SVGDocumentExtensions* extensions, const AtomicString& id, SVGElement* element) @@ -445,6 +451,8 @@ bool SVGResources::setClipper(RenderSVGResourceClipper* clipper) if (!clipper) return false; + ASSERT(clipper->resourceType() == ClipperResourceType); + if (!m_clipperFilterMaskerData) m_clipperFilterMaskerData = ClipperFilterMaskerData::create(); @@ -465,6 +473,8 @@ bool SVGResources::setFilter(RenderSVGResourceFilter* filter) if (!filter) return false; + ASSERT(filter->resourceType() == FilterResourceType); + if (!m_clipperFilterMaskerData) m_clipperFilterMaskerData = ClipperFilterMaskerData::create(); @@ -485,6 +495,8 @@ bool SVGResources::setMarkerStart(RenderSVGResourceMarker* markerStart) if (!markerStart) return false; + ASSERT(markerStart->resourceType() == MarkerResourceType); + if (!m_markerData) m_markerData = MarkerData::create(); @@ -504,6 +516,8 @@ bool SVGResources::setMarkerMid(RenderSVGResourceMarker* markerMid) if (!markerMid) return false; + ASSERT(markerMid->resourceType() == MarkerResourceType); + if (!m_markerData) m_markerData = MarkerData::create(); @@ -523,6 +537,8 @@ bool SVGResources::setMarkerEnd(RenderSVGResourceMarker* markerEnd) if (!markerEnd) return false; + ASSERT(markerEnd->resourceType() == MarkerResourceType); + if (!m_markerData) m_markerData = MarkerData::create(); @@ -542,6 +558,8 @@ bool SVGResources::setMasker(RenderSVGResourceMasker* masker) if (!masker) return false; + ASSERT(masker->resourceType() == MaskerResourceType); + if (!m_clipperFilterMaskerData) m_clipperFilterMaskerData = ClipperFilterMaskerData::create(); @@ -561,6 +579,10 @@ bool SVGResources::setFill(RenderSVGResourceContainer* fill) if (!fill) return false; + ASSERT(fill->resourceType() == PatternResourceType + || fill->resourceType() == LinearGradientResourceType + || fill->resourceType() == RadialGradientResourceType); + if (!m_fillStrokeData) m_fillStrokeData = FillStrokeData::create(); @@ -580,6 +602,10 @@ bool SVGResources::setStroke(RenderSVGResourceContainer* stroke) if (!stroke) return false; + ASSERT(stroke->resourceType() == PatternResourceType + || stroke->resourceType() == LinearGradientResourceType + || stroke->resourceType() == RadialGradientResourceType); + if (!m_fillStrokeData) m_fillStrokeData = FillStrokeData::create(); diff --git a/WebCore/rendering/SVGRootInlineBox.cpp b/WebCore/rendering/SVGRootInlineBox.cpp deleted file mode 100644 index 715003f..0000000 --- a/WebCore/rendering/SVGRootInlineBox.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> - * (C) 2006 Apple Computer Inc. - * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) Research In Motion Limited 2010. 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. - * - */ - -#include "config.h" -#include "SVGRootInlineBox.h" - -#if ENABLE(SVG) -#include "GraphicsContext.h" -#include "RenderBlock.h" -#include "SVGInlineFlowBox.h" -#include "SVGInlineTextBox.h" -#include "SVGRenderSupport.h" -#include "SVGTextLayoutUtilities.h" -#include "SVGTextPositioningElement.h" - -// Text chunk part propagation can be traced by setting this variable > 0. -#define DEBUG_CHUNK_PART_PROPAGATION 0 - -namespace WebCore { - -void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int) -{ - ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); - ASSERT(!paintInfo.context->paintingDisabled()); - - RenderObject* boxRenderer = renderer(); - ASSERT(boxRenderer); - - PaintInfo childPaintInfo(paintInfo); - childPaintInfo.context->save(); - - if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) { - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) - child->paint(childPaintInfo, 0, 0); - } - - SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context); - childPaintInfo.context->restore(); -} - -void SVGRootInlineBox::computePerCharacterLayoutInformation() -{ - // Clean up any previous layout information - m_svgChars.clear(); - m_svgTextChunks.clear(); - - // Build layout information for all contained render objects - SVGCharacterLayoutInfo charInfo; - buildLayoutInformation(this, charInfo); - m_svgChars = charInfo.svgChars; - - // Now all layout information are available for every character - // contained in any of our child inline/flow boxes. Build list - // of text chunks now, to be able to apply text-anchor shifts. - SVGTextChunkLayoutInfo chunkInfo; - chunkInfo.buildTextChunks(m_svgChars.begin(), m_svgChars.end(), this); - - // Layout all text chunks - // text-anchor needs to be applied to individual chunks. - chunkInfo.layoutTextChunks(); - m_svgTextChunks = chunkInfo.textChunks(); - - // Propagate text chunk part information to all SVGInlineTextBoxes, see SVGTextChunkLayoutInfo.h for details - propagateTextChunkPartInformation(); - - // Layout all child boxes. - layoutChildBoxes(this); - - // Resize our root box and our RenderSVGText parent block - layoutRootBox(); -} - -void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info) -{ - if (start->isRootInlineBox()) { - ASSERT(start->renderer()->node()->hasTagName(SVGNames::textTag)); - - SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->renderer()->node()); - ASSERT(positioningElement); - ASSERT(positioningElement->parentNode()); - - info.addLayoutInformation(positioningElement); - } - - SVGLastGlyphInfo lastGlyph; - - for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer()->isText()) - static_cast<SVGInlineTextBox*>(curr)->buildLayoutInformation(info, lastGlyph); - else { - ASSERT(curr->isInlineFlowBox()); - InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); - - // Skip generated content. - if (!flowBox->renderer()->node()) - continue; - - bool isAnchor = flowBox->renderer()->node()->hasTagName(SVGNames::aTag); - bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag); - - if (!isTextPath && !isAnchor) { - SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->renderer()->node()); - ASSERT(positioningElement); - ASSERT(positioningElement->parentNode()); - - info.addLayoutInformation(positioningElement); - } else if (!isAnchor) { - info.setInPathLayout(true); - - // Handle text-anchor/textLength on path, which is special. - SVGTextContentElement* textContent = 0; - Node* node = flowBox->renderer()->node(); - if (node && node->isSVGElement()) - textContent = static_cast<SVGTextContentElement*>(node); - ASSERT(textContent); - - ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); - ETextAnchor anchor = flowBox->renderer()->style()->svgStyle()->textAnchor(); - float textAnchorStartOffset = 0.0f; - - // Initialize sub-layout. We need to create text chunks from the textPath - // children using our standard layout code, to be able to measure the - // text length using our normal methods and not textPath specific hacks. - Vector<SVGTextChunk> tempChunks; - - SVGCharacterLayoutInfo tempCharInfo; - buildLayoutInformation(flowBox, tempCharInfo); - - SVGTextChunkLayoutInfo tempChunkInfo; - tempChunkInfo.buildTextChunks(tempCharInfo.svgChars.begin(), tempCharInfo.svgChars.end(), flowBox); - tempChunks = tempChunkInfo.textChunks(); - - Vector<SVGTextChunk>::iterator it = tempChunks.begin(); - Vector<SVGTextChunk>::iterator end = tempChunks.end(); - - float computedLength = 0.0f; - - for (; it != end; ++it) { - SVGTextChunk& chunk = *it; - - // Apply text-length calculation - info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength); - - if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { - info.pathTextLength += computedLength; - info.pathChunkLength += chunk.textLength; - } - - // Calculate text-anchor start offset - if (anchor == TA_START) - continue; - - textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor); - } - - info.addLayoutInformation(flowBox, textAnchorStartOffset); - } - - float shiftxSaved = info.shiftx; - float shiftySaved = info.shifty; - - buildLayoutInformation(flowBox, info); - info.processedChunk(shiftxSaved, shiftySaved); - - if (isTextPath) - info.setInPathLayout(false); - } - } -} - -void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start) -{ - for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { - if (child->renderer()->isText()) { - SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child); - IntRect boxRect = textBox->calculateBoundaries(); - textBox->setX(boxRect.x()); - textBox->setY(boxRect.y()); - textBox->setLogicalWidth(boxRect.width()); - textBox->setLogicalHeight(boxRect.height()); - } else { - ASSERT(child->isInlineFlowBox()); - - // Skip generated content. - if (!child->renderer()->node()) - continue; - - SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child); - layoutChildBoxes(flowBox); - - IntRect boxRect = flowBox->calculateBoundaries(); - flowBox->setX(boxRect.x()); - flowBox->setY(boxRect.y()); - flowBox->setLogicalWidth(boxRect.width()); - flowBox->setLogicalHeight(boxRect.height()); - } - } -} - -void SVGRootInlineBox::layoutRootBox() -{ - RenderBlock* parentBlock = block(); - ASSERT(parentBlock); - - IntRect childRect; - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { - // Skip generated content. - if (!child->renderer()->node()) - continue; - childRect.unite(child->calculateBoundaries()); - } - - int xBlock = childRect.x(); - int yBlock = childRect.y(); - int widthBlock = childRect.width(); - int heightBlock = childRect.height(); - - // Finally, assign the root block position, now that all content is laid out. - parentBlock->setLocation(xBlock, yBlock); - parentBlock->setWidth(widthBlock); - parentBlock->setHeight(heightBlock); - - // Position all children relative to the parent block. - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { - // Skip generated content. - if (!child->renderer()->node()) - continue; - child->adjustPosition(-xBlock, -yBlock); - } - - // Position ourselves. - setX(0); - setY(0); - setLogicalWidth(widthBlock); - setLogicalHeight(heightBlock); - setBlockHeight(heightBlock); - setLineTopBottomPositions(0, heightBlock); -} - -void SVGRootInlineBox::propagateTextChunkPartInformation() -{ -#if DEBUG_CHUNK_PART_PROPAGATION > 0 - ListHashSet<SVGInlineTextBox*> boxes; -#endif - - // Loop through all text chunks - const Vector<SVGTextChunk>::const_iterator end = m_svgTextChunks.end(); - for (Vector<SVGTextChunk>::const_iterator it = m_svgTextChunks.begin(); it != end; ++it) { - const SVGTextChunk& chunk = *it; - int processedChunkCharacters = 0; - - // Loop through all ranges contained in this chunk - const Vector<SVGInlineBoxCharacterRange>::const_iterator boxEnd = chunk.boxes.end(); - for (Vector<SVGInlineBoxCharacterRange>::const_iterator boxIt = chunk.boxes.begin(); boxIt != boxEnd; ++boxIt) { - const SVGInlineBoxCharacterRange& range = *boxIt; - ASSERT(range.box->isSVGInlineTextBox()); - - // Access style & font information of this text box - SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box); - rangeTextBox->setChunkTransformation(chunk.ctm); - - RenderText* text = rangeTextBox->textRenderer(); - ASSERT(text); - - RenderStyle* style = text->style(); - ASSERT(style); - - const Font& font = style->font(); - - // Figure out first and last character of this range in this chunk - int rangeLength = range.endOffset - range.startOffset; - Vector<SVGChar>::iterator itCharBegin = chunk.start + processedChunkCharacters; - Vector<SVGChar>::iterator itCharEnd = chunk.start + processedChunkCharacters + rangeLength; - ASSERT(itCharEnd <= chunk.end); - - // Loop through all characters in range - int processedRangeCharacters = 0; - for (Vector<SVGChar>::iterator itChar = itCharBegin; itChar != itCharEnd; ++itChar) { - if (itChar->isHidden()) { - ++processedRangeCharacters; - continue; - } - - // Determine how many characters - starting from the current - can be drawn at once. - Vector<SVGChar>::iterator itSearch = itChar + 1; - while (itSearch != itCharEnd) { - if (itSearch->drawnSeperated || itSearch->isHidden()) - break; - - ++itSearch; - } - - // Calculate text chunk part information for this chunk sub-range - const UChar* partStart = text->characters() + rangeTextBox->start() + range.startOffset + processedRangeCharacters; - - SVGTextChunkPart part; - part.firstCharacter = itChar; - part.length = itSearch - itChar; - part.width = font.floatWidth(svgTextRunForInlineTextBox(partStart, part.length, style, rangeTextBox)); - part.height = font.height(); - part.offset = range.startOffset + processedRangeCharacters; - rangeTextBox->addChunkPartInformation(part); - processedRangeCharacters += part.length; - - // Skip processed characters - itChar = itSearch - 1; - } - - ASSERT(processedRangeCharacters == rangeLength); - processedChunkCharacters += rangeLength; - -#if DEBUG_CHUNK_PART_PROPAGATION > 0 - boxes.add(rangeTextBox); -#endif - } - } - -#if DEBUG_CHUNK_PART_PROPAGATION > 0 - { - fprintf(stderr, "Propagated text chunk part information:\n"); - - ListHashSet<SVGInlineTextBox*>::const_iterator it = boxes.begin(); - const ListHashSet<SVGInlineTextBox*>::const_iterator end = boxes.end(); - - for (; it != end; ++it) { - const SVGInlineTextBox* box = *it; - const Vector<SVGTextChunkPart>& parts = box->svgTextChunkParts(); - - fprintf(stderr, " Box %p contains %i text chunk parts:\n", box, static_cast<int>(parts.size())); - Vector<SVGTextChunkPart>::const_iterator partIt = parts.begin(); - const Vector<SVGTextChunkPart>::const_iterator partEnd = parts.end(); - for (; partIt != partEnd; ++partIt) { - const SVGTextChunkPart& part = *partIt; - fprintf(stderr, " -> firstCharacter x=%lf, y=%lf, offset=%i, length=%i, width=%lf, height=%lf, textRenderer=%p\n" - , part.firstCharacter->x, part.firstCharacter->y, part.offset, part.length, part.width, part.height, box->textRenderer()); - } - } - } -#endif -} - -} // namespace WebCore - -#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGTextChunkLayoutInfo.cpp b/WebCore/rendering/SVGTextChunkLayoutInfo.cpp deleted file mode 100644 index a7a6d87..0000000 --- a/WebCore/rendering/SVGTextChunkLayoutInfo.cpp +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) Research In Motion Limited 2010. 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. - * - */ - -#include "config.h" -#include "SVGTextChunkLayoutInfo.h" - -#if ENABLE(SVG) -#include "InlineFlowBox.h" -#include "SVGInlineTextBox.h" -#include "SVGRenderStyle.h" - -// Text chunk creation is complex and the whole process -// can easily be traced by setting this variable > 0. -#define DEBUG_CHUNK_BUILDING 0 - -namespace WebCore { - -static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly) -{ - float length = 0.0f; - Vector<SVGChar>::iterator charIt = chunk.start; - - Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin(); - Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end(); - - for (; it != end; ++it) { - SVGInlineBoxCharacterRange& range = *it; - - SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box); - RenderStyle* style = box->renderer()->style(); - - for (int i = range.startOffset; i < range.endOffset; ++i) { - ASSERT(charIt <= chunk.end); - - // Determine how many characters - starting from the current - can be measured at once. - // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width - // of a string is not the sum of the boundaries of all contained glyphs. - Vector<SVGChar>::iterator itSearch = charIt + 1; - Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i; - while (itSearch != endSearch) { - // No need to check for 'isHidden()' here as this function is not called for text paths. - if (itSearch->drawnSeperated) - break; - - itSearch++; - } - - unsigned int positionOffset = itSearch - charIt; - - // Calculate width/height of subrange - SVGInlineBoxCharacterRange subRange; - subRange.box = range.box; - subRange.startOffset = i; - subRange.endOffset = i + positionOffset; - - if (calcWidthOnly) - length += cummulatedWidthOfInlineBoxCharacterRange(subRange); - else - length += cummulatedHeightOfInlineBoxCharacterRange(subRange); - - // Calculate gap between the previous & current range - // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account - // so add "40" as width, and analogous for B & C, add "20" as width. - if (itSearch > chunk.start && itSearch < chunk.end) { - SVGChar& lastCharacter = *(itSearch - 1); - SVGChar& currentCharacter = *itSearch; - - int charsConsumed = 0; - float glyphWidth = 0.0f; - float glyphHeight = 0.0f; - String glyphName; - String unicodeString; - box->measureCharacter(style, i + positionOffset - 1, charsConsumed, glyphName, unicodeString, glyphWidth, glyphHeight); - - if (calcWidthOnly) - length += currentCharacter.x - lastCharacter.x - glyphWidth; - else - length += currentCharacter.y - lastCharacter.y - glyphHeight; - } - - // Advance processed characters - i += positionOffset - 1; - charIt = itSearch; - } - } - - ASSERT(charIt == chunk.end); - return length; -} - -static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk) -{ - return cummulatedWidthOrHeightOfTextChunk(chunk, true); -} - -static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk) -{ - return cummulatedWidthOrHeightOfTextChunk(chunk, false); -} - -float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor) -{ - float shift = 0.0f; - - if (chunk.isVerticalText) - shift = cummulatedHeightOfTextChunk(chunk); - else - shift = cummulatedWidthOfTextChunk(chunk); - - if (anchor == TA_MIDDLE) - shift *= -0.5f; - else - shift *= -1.0f; - - return shift; -} - -static void applyTextAnchorToTextChunk(SVGTextChunk& chunk) -{ - // This method is not called for chunks containing chars aligned on a path. - // -> all characters are visible, no need to check for "isHidden()" anywhere. - - if (chunk.anchor == TA_START) - return; - - float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor); - - // Apply correction to chunk - Vector<SVGChar>::iterator chunkIt = chunk.start; - for (; chunkIt != chunk.end; ++chunkIt) { - SVGChar& curChar = *chunkIt; - - if (chunk.isVerticalText) - curChar.y += shift; - else - curChar.x += shift; - } - - // Move inline boxes - Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); - Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); - - for (; boxIt != boxEnd; ++boxIt) { - SVGInlineBoxCharacterRange& range = *boxIt; - - InlineBox* curBox = range.box; - ASSERT(curBox->isSVGInlineTextBox()); - - // Move target box - if (chunk.isVerticalText) - curBox->setY(curBox->y() + static_cast<int>(shift)); - else - curBox->setX(curBox->x() + static_cast<int>(shift)); - } -} - -float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength) -{ - if (chunk.textLength <= 0.0f) - return 0.0f; - - computedLength = 0.0f; - - float computedWidth = cummulatedWidthOfTextChunk(chunk); - float computedHeight = cummulatedHeightOfTextChunk(chunk); - if ((computedWidth <= 0.0f && !chunk.isVerticalText) - || (computedHeight <= 0.0f && chunk.isVerticalText)) - return 0.0f; - - computedLength = chunk.isVerticalText ? computedHeight : computedWidth; - if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { - if (chunk.isVerticalText) - chunk.ctm.scaleNonUniform(1.0f, chunk.textLength / computedLength); - else - chunk.ctm.scaleNonUniform(chunk.textLength / computedLength, 1.0f); - - return 0.0f; - } - - return (chunk.textLength - computedLength) / float(chunk.end - chunk.start); -} - -static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk) -{ - // This method is not called for chunks containing chars aligned on a path. - // -> all characters are visible, no need to check for "isHidden()" anywhere. - - // lengthAdjust="spacingAndGlyphs" is handled by setting a scale factor for the whole chunk - float textLength = 0.0f; - float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, textLength); - - if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { - SVGChar& firstChar = *(chunk.start); - - // Assure we apply the chunk scaling in the right origin - AffineTransform newChunkCtm(chunk.ctm); - newChunkCtm.translateRight(firstChar.x, firstChar.y); - newChunkCtm.translate(-firstChar.x, -firstChar.y); - - chunk.ctm = newChunkCtm; - } - - // Apply correction to chunk - if (spacingToApply != 0.0f) { - Vector<SVGChar>::iterator chunkIt = chunk.start; - for (; chunkIt != chunk.end; ++chunkIt) { - SVGChar& curChar = *chunkIt; - curChar.drawnSeperated = true; - - if (chunk.isVerticalText) - curChar.y += (chunkIt - chunk.start) * spacingToApply; - else - curChar.x += (chunkIt - chunk.start) * spacingToApply; - } - } -} - -void SVGTextChunkLayoutInfo::startTextChunk() -{ - m_chunk.boxes.clear(); - m_chunk.boxes.append(SVGInlineBoxCharacterRange()); - - m_chunk.start = m_charsIt; - m_assignChunkProperties = true; -} - -void SVGTextChunkLayoutInfo::closeTextChunk() -{ - ASSERT(!m_chunk.boxes.last().isOpen()); - ASSERT(m_chunk.boxes.last().isClosed()); - - m_chunk.end = m_charsIt; - ASSERT(m_chunk.end >= m_chunk.start); - - m_svgTextChunks.append(m_chunk); -} - -void SVGTextChunkLayoutInfo::buildTextChunks(Vector<SVGChar>::iterator begin, Vector<SVGChar>::iterator end, InlineFlowBox* start) -{ - m_charsBegin = begin; - m_charsEnd = end; - - m_charsIt = begin; - m_chunk = SVGTextChunk(begin); - - recursiveBuildTextChunks(start); - ASSERT(m_charsIt == m_charsEnd); -} - -void SVGTextChunkLayoutInfo::recursiveBuildTextChunks(InlineFlowBox* start) -{ -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> buildTextChunks(start=%p)\n", start); -#endif - - for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer()->isText()) { - InlineTextBox* textBox = static_cast<InlineTextBox*>(curr); - - unsigned length = textBox->len(); - ASSERT(length > 0); - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n", - textBox, length, textBox->start(), textBox->end(), (int) m_handlingTextPath); -#endif - - RenderText* text = textBox->textRenderer(); - ASSERT(text); - ASSERT(text->node()); - - SVGTextContentElement* textContent = 0; - Node* node = text->node()->parent(); - while (node && node->isSVGElement() && !textContent) { - if (static_cast<SVGElement*>(node)->isTextContent()) - textContent = static_cast<SVGTextContentElement*>(node); - else - node = node->parentNode(); - } - ASSERT(textContent); - - // Start new character range for the first chunk - bool isFirstCharacter = m_svgTextChunks.isEmpty() && m_chunk.start == m_charsIt && m_chunk.start == m_chunk.end; - if (isFirstCharacter) { - ASSERT(m_chunk.boxes.isEmpty()); - m_chunk.boxes.append(SVGInlineBoxCharacterRange()); - } else - ASSERT(!m_chunk.boxes.isEmpty()); - - // Walk string to find out new chunk positions, if existent - for (unsigned i = 0; i < length; ++i) { - ASSERT(m_charsIt != m_charsEnd); - - SVGInlineBoxCharacterRange& range = m_chunk.boxes.last(); - if (range.isOpen()) { - range.box = curr; - range.startOffset = !i ? 0 : i - 1; - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset); -#endif - } - - // If a new (or the first) chunk has been started, record it's text-anchor and writing mode. - if (m_assignChunkProperties) { - m_assignChunkProperties = false; - - m_chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); - m_chunk.isTextPath = m_handlingTextPath; - m_chunk.anchor = text->style()->svgStyle()->textAnchor(); - m_chunk.textLength = textContent->textLength().value(textContent); - m_chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", m_chunk.isVerticalText, m_chunk.anchor); -#endif - } - - if (i > 0 && !isFirstCharacter && m_charsIt->newTextChunk) { - // Close mid chunk & character range - ASSERT(!range.isOpen()); - ASSERT(!range.isClosed()); - - range.endOffset = i; - closeTextChunk(); - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset); -#endif - - // Prepare for next chunk, if we're not at the end - startTextChunk(); - if (i + 1 == length) { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " | -> Record last chunk of inline text box!\n"); -#endif - - startTextChunk(); - SVGInlineBoxCharacterRange& range = m_chunk.boxes.last(); - - m_assignChunkProperties = false; - m_chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); - m_chunk.isTextPath = m_handlingTextPath; - m_chunk.anchor = text->style()->svgStyle()->textAnchor(); - m_chunk.textLength = textContent->textLength().value(textContent); - m_chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); - - range.box = curr; - range.startOffset = i; - - ASSERT(!range.isOpen()); - ASSERT(!range.isClosed()); - } - } - - // This should only hold true for the first character of the first chunk - if (isFirstCharacter) - isFirstCharacter = false; - - ++m_charsIt; - } - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Finished inline text box!\n"); -#endif - - SVGInlineBoxCharacterRange& range = m_chunk.boxes.last(); - if (!range.isOpen() && !range.isClosed()) { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length); -#endif - - // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk. - range.endOffset = length; - - if (m_charsIt != m_charsEnd) { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Not at last character yet!\n"); -#endif - - // If we're not at the end of the last box to be processed, and if the next - // character starts a new chunk, then close the current chunk and start a new one. - if (m_charsIt->newTextChunk) { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n"); -#endif - - closeTextChunk(); - startTextChunk(); - } else { - // Just start a new character range - m_chunk.boxes.append(SVGInlineBoxCharacterRange()); - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n"); -#endif - } - } else { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Closing final chunk! Finished processing!\n"); -#endif - - // Close final chunk, once we're at the end of the last box - closeTextChunk(); - } - } - } else { - ASSERT(curr->isInlineFlowBox()); - InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); - - // Skip generated content. - if (!flowBox->renderer()->node()) - continue; - - bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag); - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath); -#endif - - if (isTextPath) - m_handlingTextPath = true; - - recursiveBuildTextChunks(flowBox); - - if (isTextPath) - m_handlingTextPath = false; - } - } - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " <- buildTextChunks(start=%p)\n", start); -#endif -} - -void SVGTextChunkLayoutInfo::layoutTextChunks() -{ - Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin(); - Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end(); - - for (; it != end; ++it) { - SVGTextChunk& chunk = *it; - -#if DEBUG_CHUNK_BUILDING > 0 - { - fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n", - (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText, - (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start)); - - Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); - Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); - - unsigned int i = 0; - for (; boxIt != boxEnd; ++boxIt) { - SVGInlineBoxCharacterRange& range = *boxIt; - ++i; - fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box); - } - } -#endif - - if (chunk.isTextPath) - continue; - - // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts. - applyTextLengthCorrectionToTextChunk(chunk); - - // text-anchor is already handled for textPath layouts. - applyTextAnchorToTextChunk(chunk); - } -} - -} // namespace WebCore - -#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGTextChunkLayoutInfo.h b/WebCore/rendering/SVGTextChunkLayoutInfo.h deleted file mode 100644 index e06dd1a..0000000 --- a/WebCore/rendering/SVGTextChunkLayoutInfo.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) Research In Motion Limited 2010. 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 SVGTextChunkLayoutInfo_h -#define SVGTextChunkLayoutInfo_h - -#if ENABLE(SVG) -#include "AffineTransform.h" -#include "SVGCharacterData.h" -#include "SVGRenderStyle.h" -#include "SVGTextContentElement.h" - -#include <wtf/Assertions.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class InlineBox; -class InlineFlowBox; -class SVGInlineTextBox; - -// A SVGTextChunk directly corresponds to the definition of a "text chunk" per SVG 1.1 specification -// For example, each absolute positioned character starts a text chunk (much more to respect, see spec). -// Each SVGTextChunk contains a Vector of SVGInlineBoxCharacterRange, describing how many boxes are spanned -// by this chunk. Following two examples should clarify the code a bit: -// -// 1. <text x="10 20 30">ABC</text> - one InlineTextBox is created, three SVGTextChunks each with one SVGInlineBoxCharaterRange -// [SVGTextChunk 1] -// [SVGInlineBoxCharacterRange 1, startOffset=0, endOffset=1, box=0x1] -// [SVGTextChunk 2] -// [SVGInlineBoxCharacterRange 1, startOffset=1, endOffset=2, box=0x1] -// [SVGTextChunk 3] -// [SVGInlineBoxCharacterRange 1, startOffset=2, endOffset=3, box=0x1] -// -// 2. <text x="10">A<tspan>B</tspan>C</text> - three InlineTextBoxs are created, one SVGTextChunk, with three SVGInlineBoxCharacterRanges -// [SVGTextChunk 1] -// [SVGInlineBoxCharacterRange 1, startOffset=0, endOffset=1, box=0x1] -// [SVGInlineBoxCharacterRange 2, startOffset=0, endOffset=1, box=0x2] -// [SVGInlineBoxCharacterRange 3, startOffset=0, endOffset=1, box=0x3] -// -// High level overview of the SVG text layout code: -// Step #1) - Build Vector of SVGChar objects starting from root <text> diving into children -// Step #2) - Build Vector of SVGTextChunk objects, containing offsets into the InlineTextBoxes and SVGChar vectors -// Step #3) - Apply chunk post processing (text-anchor / textLength support, which operate on text chunks!) -// Step #4) - Propagate information, how many chunk "parts" are associated with each SVGInlineTextBox (see below) -// Step #5) - Layout all InlineBoxes, only by measuring their context rect (x/y/width/height defined through SVGChars and transformations) -// Step #6) - Layout SVGRootInlineBox, it's parent RenderSVGText block and fixup child positions, to be relative to the root box -// -// When painting a range of characters, we have to determine how many can be drawn in a row. Each absolute postioned -// character is drawn individually. After step #2) we know all text chunks, and how they span across the SVGInlineTextBoxes. -// In step #4) we build a list of text chunk "parts" and store it in each SVGInlineTextBox. A chunk "part" is a part of a -// text chunk that lives in a SVGInlineTextBox (consists of a length, width, height and a monotonic offset from the chunk begin) -// The SVGTextChunkPart object describes this information. -// When painting we can follow the regular InlineBox flow, we start painting the SVGRootInlineBox, which just asks its children -// to paint. They can paint on their own because all position information are known. Previously we used to draw _all_ characters -// from the SVGRootInlineBox, which violates the whole concept of the multiple InlineBoxes, and made text selection very hard to -// implement. - -struct SVGTextChunkPart { - SVGTextChunkPart() - : offset(-1) - , length(-1) - , width(0) - , height(0) - { - } - - bool isValid() const - { - return offset != -1 - && length != -1 - && width - && height; - } - - // First character of this text chunk part, defining the origin to be drawn - Vector<SVGChar>::const_iterator firstCharacter; - - // Start offset in textRenderer()->characters() buffer. - int offset; - - // length/width/height of chunk part - int length; - float width; - float height; -}; - -struct SVGInlineBoxCharacterRange { - SVGInlineBoxCharacterRange() - : startOffset(INT_MIN) - , endOffset(INT_MIN) - , box(0) - { - } - - bool isOpen() const { return (startOffset == endOffset) && (endOffset == INT_MIN); } - bool isClosed() const { return startOffset != INT_MIN && endOffset != INT_MIN; } - - int startOffset; - int endOffset; - - InlineBox* box; -}; - -struct SVGChar; - -// Convenience typedef -typedef SVGTextContentElement::SVGLengthAdjustType ELengthAdjust; - -struct SVGTextChunk { - SVGTextChunk(Vector<SVGChar>::iterator it) - : anchor(TA_START) - , textLength(0.0f) - , lengthAdjust(SVGTextContentElement::LENGTHADJUST_SPACING) - , isVerticalText(false) - , isTextPath(false) - , start(it) - , end(it) - { - } - - // text-anchor support - ETextAnchor anchor; - - // textLength & lengthAdjust support - float textLength; - ELengthAdjust lengthAdjust; - AffineTransform ctm; - - // status flags - bool isVerticalText : 1; - bool isTextPath : 1; - - // main chunk data - Vector<SVGChar>::iterator start; - Vector<SVGChar>::iterator end; - - Vector<SVGInlineBoxCharacterRange> boxes; -}; - -struct SVGTextChunkLayoutInfo { - SVGTextChunkLayoutInfo() - : m_assignChunkProperties(true) - , m_handlingTextPath(false) - , m_charsIt(0) - , m_charsBegin(0) - , m_charsEnd(0) - , m_chunk(0) - { - } - - const Vector<SVGTextChunk>& textChunks() const { return m_svgTextChunks; } - - void buildTextChunks(Vector<SVGChar>::iterator charsBegin, Vector<SVGChar>::iterator charsEnd, InlineFlowBox* start); - void layoutTextChunks(); - -private: - void startTextChunk(); - void closeTextChunk(); - void recursiveBuildTextChunks(InlineFlowBox* start); - - bool m_assignChunkProperties : 1; - bool m_handlingTextPath : 1; - - Vector<SVGChar>::iterator m_charsIt; - Vector<SVGChar>::iterator m_charsBegin; - Vector<SVGChar>::iterator m_charsEnd; - - Vector<SVGTextChunk> m_svgTextChunks; - SVGTextChunk m_chunk; -}; - -// Helper functions -float calculateTextAnchorShiftForTextChunk(SVGTextChunk&, ETextAnchor); -float calculateTextLengthCorrectionForTextChunk(SVGTextChunk&, ELengthAdjust, float& computedLength); - -} // namespace WebCore - -#endif // ENABLE(SVG) -#endif // SVGTextChunkLayoutInfo_h diff --git a/WebCore/rendering/SVGTextLayoutUtilities.cpp b/WebCore/rendering/SVGTextLayoutUtilities.cpp deleted file mode 100644 index 2debf28..0000000 --- a/WebCore/rendering/SVGTextLayoutUtilities.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/* - Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - Copyright (C) Research In Motion Limited 2010. 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. -*/ - -#include "config.h" -#include "SVGTextLayoutUtilities.h" - -#if ENABLE(SVG) -#include "FloatPoint.h" -#include "InlineTextBox.h" -#include "RenderObject.h" -#include "SVGCharacterData.h" -#include "SVGCharacterLayoutInfo.h" -#include "SVGFontElement.h" -#include "SVGRenderStyle.h" -#include "SVGTextChunkLayoutInfo.h" -#include "TextRun.h" -#include "UnicodeRange.h" - -#include <float.h> - -namespace WebCore { - -bool isVerticalWritingMode(const SVGRenderStyle* style) -{ - return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB; -} - -static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font) -{ - ASSERT(text); - - const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0; - ASSERT(style); - - const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0; - - EDominantBaseline baseline = style->dominantBaseline(); - if (baseline == DB_AUTO) { - if (isVerticalText) - baseline = DB_CENTRAL; - else - baseline = DB_ALPHABETIC; - } - - switch (baseline) { - case DB_USE_SCRIPT: - // TODO: The dominant-baseline and the baseline-table components are set by - // determining the predominant script of the character data content. - return AB_ALPHABETIC; - case DB_NO_CHANGE: - { - if (parentStyle) - return dominantBaselineToShift(isVerticalText, text->parent(), font); - - ASSERT_NOT_REACHED(); - return AB_AUTO; - } - case DB_RESET_SIZE: - { - if (parentStyle) - return dominantBaselineToShift(isVerticalText, text->parent(), font); - - ASSERT_NOT_REACHED(); - return AB_AUTO; - } - case DB_IDEOGRAPHIC: - return AB_IDEOGRAPHIC; - case DB_ALPHABETIC: - return AB_ALPHABETIC; - case DB_HANGING: - return AB_HANGING; - case DB_MATHEMATICAL: - return AB_MATHEMATICAL; - case DB_CENTRAL: - return AB_CENTRAL; - case DB_MIDDLE: - return AB_MIDDLE; - case DB_TEXT_AFTER_EDGE: - return AB_TEXT_AFTER_EDGE; - case DB_TEXT_BEFORE_EDGE: - return AB_TEXT_BEFORE_EDGE; - default: - ASSERT_NOT_REACHED(); - return AB_AUTO; - } -} - -float alignmentBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font) -{ - ASSERT(text); - - const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0; - ASSERT(style); - - const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0; - - EAlignmentBaseline baseline = style->alignmentBaseline(); - if (baseline == AB_AUTO) { - if (parentStyle && style->dominantBaseline() == DB_AUTO) - baseline = dominantBaselineToShift(isVerticalText, text->parent(), font); - else - baseline = dominantBaselineToShift(isVerticalText, text, font); - - ASSERT(baseline != AB_AUTO); - } - - // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling - switch (baseline) { - case AB_BASELINE: - { - if (parentStyle) - return dominantBaselineToShift(isVerticalText, text->parent(), font); - - return 0.0f; - } - case AB_BEFORE_EDGE: - case AB_TEXT_BEFORE_EDGE: - return font.ascent(); - case AB_MIDDLE: - return font.xHeight() / 2.0f; - case AB_CENTRAL: - // Not needed, we're taking this into account already for vertical text! - // return (font.ascent() - font.descent()) / 2.0f; - return 0.0f; - case AB_AFTER_EDGE: - case AB_TEXT_AFTER_EDGE: - case AB_IDEOGRAPHIC: - return font.descent(); - case AB_ALPHABETIC: - return 0.0f; - case AB_HANGING: - return font.ascent() * 8.0f / 10.0f; - case AB_MATHEMATICAL: - return font.ascent() / 2.0f; - default: - ASSERT_NOT_REACHED(); - return 0.0f; - } -} - -float glyphOrientationToAngle(const SVGRenderStyle* svgStyle, bool isVerticalText, const UChar& character) -{ - switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) { - case GO_AUTO: - { - // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees. - // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees. - unsigned int unicodeRange = findCharUnicodeRange(character); - if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic) - return 90.0f; - - return 0.0f; - } - case GO_90DEG: - return 90.0f; - case GO_180DEG: - return 180.0f; - case GO_270DEG: - return 270.0f; - case GO_0DEG: - default: - return 0.0f; - } -} - -static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle) -{ - return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f; -} - -float applyGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font& font, SVGChar& svgChar, float& xOrientationShift, float& yOrientationShift) -{ - bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle); - - // The function is based on spec requirements: - // - // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of - // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph. - // - // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of - // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph. - - // vertical orientation handling - if (isVerticalText) { - if (orientationAngle == 0.0f) { - xOrientationShift = -glyphWidth / 2.0f; - yOrientationShift = font.ascent(); - } else if (orientationAngle == 90.0f) { - xOrientationShift = -glyphHeight; - yOrientationShift = font.descent(); - svgChar.orientationShiftY = -font.ascent(); - } else if (orientationAngle == 270.0f) { - xOrientationShift = glyphHeight; - yOrientationShift = font.descent(); - svgChar.orientationShiftX = -glyphWidth; - svgChar.orientationShiftY = -font.ascent(); - } else if (orientationAngle == 180.0f) { - yOrientationShift = font.ascent(); - svgChar.orientationShiftX = -glyphWidth / 2.0f; - svgChar.orientationShiftY = font.ascent() - font.descent(); - } - - // vertical advance calculation - if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) - return glyphWidth; - - return glyphHeight; - } - - // horizontal orientation handling - if (orientationAngle == 90.0f) { - xOrientationShift = glyphWidth / 2.0f; - yOrientationShift = -font.descent(); - svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent(); - svgChar.orientationShiftY = font.descent(); - } else if (orientationAngle == 270.0f) { - xOrientationShift = -glyphWidth / 2.0f; - yOrientationShift = -font.descent(); - svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent(); - svgChar.orientationShiftY = glyphHeight; - } else if (orientationAngle == 180.0f) { - xOrientationShift = glyphWidth / 2.0f; - svgChar.orientationShiftX = -glyphWidth / 2.0f; - svgChar.orientationShiftY = font.ascent() - font.descent(); - } - - // horizontal advance calculation - if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) - return glyphHeight; - - return glyphWidth; -} - -FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end) -{ - float lowX = FLT_MAX, lowY = FLT_MAX; - for (; it != end; ++it) { - if (it->isHidden()) - continue; - - float x = (*it).x; - float y = (*it).y; - - if (x < lowX) - lowX = x; - - if (y < lowY) - lowY = y; - } - - return FloatPoint(lowX, lowY); -} - -float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range) -{ - ASSERT(!range.isOpen()); - ASSERT(range.isClosed()); - ASSERT(range.box->isSVGInlineTextBox()); - - InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); - RenderText* text = textBox->textRenderer(); - RenderStyle* style = text->style(); - return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox)); -} - -float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range) -{ - ASSERT(!range.isOpen()); - ASSERT(range.isClosed()); - ASSERT(range.box->isSVGInlineTextBox()); - - InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); - return (range.endOffset - range.startOffset) * textBox->textRenderer()->style()->font().height(); -} - -TextRun svgTextRunForInlineTextBox(const UChar* characters, int length, const RenderStyle* style, const InlineTextBox* textBox) -{ - ASSERT(textBox); - ASSERT(style); - - TextRun run(characters - , length - , false /* allowTabs */ - , 0 /* xPos, only relevant with allowTabs=true */ - , 0 /* padding, only relevant for justified text, not relevant for SVG */ - , textBox->direction() == RTL - , textBox->m_dirOverride || style->visuallyOrdered() /* directionalOverride */); - -#if ENABLE(SVG_FONTS) - run.setReferencingRenderObject(textBox->textRenderer()->parent()); -#endif - - // Disable any word/character rounding. - run.disableRoundingHacks(); - - // We handle letter & word spacing ourselves. - run.disableSpacing(); - return run; -} - -float calculateCSSKerning(SVGElement* context, const RenderStyle* style) -{ - const Font& font = style->font(); - const SVGRenderStyle* svgStyle = style->svgStyle(); - - SVGLength kerningLength = svgStyle->kerning(); - if (kerningLength.unitType() == LengthTypePercentage) - return kerningLength.valueAsPercentage() * font.pixelSize(); - - return kerningLength.value(context); -} - -bool applySVGKerning(SVGCharacterLayoutInfo& info, const RenderStyle* style, SVGLastGlyphInfo& lastGlyph, const String& unicodeString, const String& glyphName, bool isVerticalText) -{ -#if ENABLE(SVG_FONTS) - float kerning = 0.0f; - - const Font& font = style->font(); - if (!font.isSVGFont()) { - lastGlyph.isValid = false; - return false; - } - - SVGFontElement* svgFont = font.svgFont(); - ASSERT(svgFont); - - if (lastGlyph.isValid) { - if (isVerticalText) - kerning = svgFont->verticalKerningForPairOfStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeString, glyphName); - else - kerning = svgFont->horizontalKerningForPairOfStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeString, glyphName); - } - - lastGlyph.unicode = unicodeString; - lastGlyph.glyphName = glyphName; - lastGlyph.isValid = true; - kerning *= style->font().size() / style->font().primaryFont()->unitsPerEm(); - - if (kerning != 0.0f) { - if (isVerticalText) - info.cury -= kerning; - else - info.curx -= kerning; - return true; - } -#else - UNUSED_PARAM(info); - UNUSED_PARAM(item); - UNUSED_PARAM(lastGlyph); - UNUSED_PARAM(unicodeString); - UNUSED_PARAM(glyphName); -#endif - return false; -} - -} - -#endif diff --git a/WebCore/rendering/SVGTextLayoutUtilities.h b/WebCore/rendering/SVGTextLayoutUtilities.h deleted file mode 100644 index 459d682..0000000 --- a/WebCore/rendering/SVGTextLayoutUtilities.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - Copyright (C) Research In Motion Limited 2010. 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 SVGTextLayoutUtilities_h -#define SVGTextLayoutUtilities_h - -#if ENABLE(SVG) -#include <wtf/Vector.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class FloatPoint; -class Font; -class InlineTextBox; -class RenderObject; -class RenderStyle; -class SVGElement; -class SVGRenderStyle; -class TextRun; - -struct SVGChar; -struct SVGCharacterLayoutInfo; -struct SVGInlineBoxCharacterRange; - -struct SVGLastGlyphInfo { - SVGLastGlyphInfo() - : isValid(false) - { - } - - bool isValid; - String unicode; - String glyphName; -}; - -bool isVerticalWritingMode(const SVGRenderStyle*); -float alignmentBaselineToShift(bool isVerticalText, const RenderObject* text, const Font&); -float glyphOrientationToAngle(const SVGRenderStyle*, bool isVerticalText, const UChar&); -float applyGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font&, - SVGChar&, float& xOrientationShift, float& yOrientationShift); -FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator start, Vector<SVGChar>::iterator end); -float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange&); -float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange&); -TextRun svgTextRunForInlineTextBox(const UChar*, int length, const RenderStyle*, const InlineTextBox*); - -float calculateCSSKerning(SVGElement* context, const RenderStyle*); -bool applySVGKerning(SVGCharacterLayoutInfo&, const RenderStyle*, SVGLastGlyphInfo&, const String& unicodeString, const String& glyphName, bool isVerticalText); - -} - -#endif -#endif diff --git a/WebCore/rendering/SVGTextQuery.cpp b/WebCore/rendering/SVGTextQuery.cpp deleted file mode 100644 index 35ca690..0000000 --- a/WebCore/rendering/SVGTextQuery.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/* - Copyright (C) Research In Motion Limited 2010. 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. -*/ - -#include "config.h" -#include "SVGTextQuery.h" - -#if ENABLE(SVG) -#include "FloatConversion.h" -#include "InlineFlowBox.h" -#include "RenderBlock.h" -#include "RenderInline.h" -#include "SVGInlineTextBox.h" -#include "VisiblePosition.h" - -namespace WebCore { - -// Base structure for callback user data -struct SVGTextQuery::Data { - Data() - : processedChunkCharacters(0) - { - } - - unsigned processedChunkCharacters; -}; - -static inline InlineFlowBox* flowBoxForRenderer(RenderObject* renderer) -{ - if (!renderer) - return 0; - - if (renderer->isRenderBlock()) { - // If we're given a block element, it has to be a RenderSVGText. - ASSERT(renderer->isSVGText()); - RenderBlock* renderBlock = toRenderBlock(renderer); - - // RenderSVGText only ever contains a single line box. - InlineFlowBox* flowBox = renderBlock->firstLineBox(); - ASSERT(flowBox == renderBlock->lastLineBox()); - return flowBox; - } - - if (renderer->isRenderInline()) { - // We're given a RenderSVGInline or objects that derive from it (RenderSVGTSpan / RenderSVGTextPath) - RenderInline* renderInline = toRenderInline(renderer); - - // RenderSVGInline only ever contains a single line box. - InlineFlowBox* flowBox = renderInline->firstLineBox(); - ASSERT(flowBox == renderInline->lastLineBox()); - return flowBox; - } - - ASSERT_NOT_REACHED(); - return 0; -} - -static inline float mapLengthThroughChunkTransformation(const SVGInlineTextBox* textBox, bool isVerticalText, float length) -{ - const AffineTransform& transform = textBox->chunkTransformation(); - if (transform.isIdentity()) - return length; - - return narrowPrecisionToFloat(static_cast<double>(length) * (isVerticalText ? transform.d() : transform.a())); -} - -SVGTextQuery::SVGTextQuery(RenderObject* renderer) -{ - collectTextBoxesInFlowBox(flowBoxForRenderer(renderer)); -} - -void SVGTextQuery::collectTextBoxesInFlowBox(InlineFlowBox* flowBox) -{ - if (!flowBox) - return; - - for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnLine()) { - if (child->isInlineFlowBox()) { - // Skip generated content. - if (!child->renderer()->node()) - continue; - - collectTextBoxesInFlowBox(static_cast<InlineFlowBox*>(child)); - continue; - } - - ASSERT(child->isSVGInlineTextBox()); - m_textBoxes.append(static_cast<SVGInlineTextBox*>(child)); - } -} - -bool SVGTextQuery::executeQuery(Data* queryData, ProcessTextChunkPartCallback chunkPartCallback) const -{ - ASSERT(!m_textBoxes.isEmpty()); - bool finished = false; - - // Loop over all text boxes - const Vector<SVGInlineTextBox*>::const_iterator end = m_textBoxes.end(); - for (Vector<SVGInlineTextBox*>::const_iterator it = m_textBoxes.begin(); it != end; ++it) { - const SVGInlineTextBox* textBox = *it; - const Vector<SVGTextChunkPart>& parts = textBox->svgTextChunkParts(); - - int processedCharacters = 0; - - // Loop over all text chunk parts in this text box, firing a callback for each chunk part. - const Vector<SVGTextChunkPart>::const_iterator partEnd = parts.end(); - for (Vector<SVGTextChunkPart>::const_iterator partIt = parts.begin(); partIt != partEnd; ++partIt) { - if ((this->*chunkPartCallback)(queryData, textBox, *partIt)) { - finished = true; - break; - } - - processedCharacters += partIt->length; - } - - if (finished) - break; - - queryData->processedChunkCharacters += processedCharacters; - } - - return finished; -} - -bool SVGTextQuery::mapStartAndLengthIntoChunkPartCoordinates(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part, int& startPosition, int& endPosition) const -{ - // Reuse the same logic used for text selection & painting, to map our query start/length into start/endPositions of the current text chunk part. - startPosition -= queryData->processedChunkCharacters; - endPosition -= queryData->processedChunkCharacters; - textBox->mapStartEndPositionsIntoChunkPartCoordinates(startPosition, endPosition, part); - - // If startPosition < endPosition, then the position we're supposed to measure lies in this chunk part. - return startPosition < endPosition; -} - -float SVGTextQuery::measureCharacterRange(const SVGInlineTextBox* textBox, RenderStyle* style, bool isVerticalText, int startPosition, int length) const -{ - // FIXME: Vertical writing mode needs to be handled more accurate. - if (isVerticalText) - return length * style->font().height(); - - const UChar* startCharacter = textBox->textRenderer()->characters() + textBox->start() + startPosition; - return style->font().floatWidth(svgTextRunForInlineTextBox(startCharacter, length, style, textBox)); -} - -// numberOfCharacters() implementation -struct NumberOfCharactersData : SVGTextQuery::Data { - NumberOfCharactersData() - : characters(0) - { - } - - unsigned characters; -}; - -bool SVGTextQuery::numberOfCharactersCallback(Data* queryData, const SVGInlineTextBox*, const SVGTextChunkPart& part) const -{ - NumberOfCharactersData* data = static_cast<NumberOfCharactersData*>(queryData); - data->characters += part.length; - return false; -} - -unsigned SVGTextQuery::numberOfCharacters() const -{ - if (m_textBoxes.isEmpty()) - return 0; - - NumberOfCharactersData data; - executeQuery(&data, &SVGTextQuery::numberOfCharactersCallback); - return data.characters; -} - -// textLength() implementation -struct TextLengthData : SVGTextQuery::Data { - TextLengthData() - : textLength(0.0f) - { - } - - float textLength; -}; - -bool SVGTextQuery::textLengthCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const -{ - TextLengthData* data = static_cast<TextLengthData*>(queryData); - - RenderStyle* style = textBox->textRenderer()->style(); - ASSERT(style); - - bool isVerticalText = isVerticalWritingMode(style->svgStyle()); - float partLength = isVerticalText ? part.height : part.width; - - data->textLength += mapLengthThroughChunkTransformation(textBox, isVerticalText, partLength); - return false; -} - -float SVGTextQuery::textLength() const -{ - if (m_textBoxes.isEmpty()) - return 0.0f; - - TextLengthData data; - executeQuery(&data, &SVGTextQuery::textLengthCallback); - return data.textLength; -} - -// subStringLength() implementation -struct SubStringLengthData : SVGTextQuery::Data { - SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) - : startPosition(queryStartPosition) - , length(queryLength) - , subStringLength(0.0f) - { - } - - unsigned startPosition; - unsigned length; - - float subStringLength; -}; - -bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const -{ - SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData); - - int startPosition = data->startPosition; - int endPosition = startPosition + data->length; - if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) - return false; - - RenderStyle* style = textBox->textRenderer()->style(); - ASSERT(style); - - bool isVerticalText = isVerticalWritingMode(style->svgStyle()); - float partLength = measureCharacterRange(textBox, style, isVerticalWritingMode(style->svgStyle()), part.offset + startPosition, endPosition - startPosition); - - data->subStringLength += mapLengthThroughChunkTransformation(textBox, isVerticalText, partLength); - return false; -} - -float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) const -{ - if (m_textBoxes.isEmpty()) - return 0.0f; - - SubStringLengthData data(startPosition, length); - executeQuery(&data, &SVGTextQuery::subStringLengthCallback); - return data.subStringLength; -} - -// startPositionOfCharacter() implementation -struct StartPositionOfCharacterData : SVGTextQuery::Data { - StartPositionOfCharacterData(unsigned queryPosition) - : position(queryPosition) - { - } - - unsigned position; - FloatPoint startPosition; -}; - -bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const -{ - StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterData*>(queryData); - - int startPosition = data->position; - int endPosition = startPosition + 1; - if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) - return false; - - const SVGChar& character = *(part.firstCharacter + startPosition); - data->startPosition = textBox->chunkTransformation().mapPoint(FloatPoint(character.x, character.y)); - return true; -} - -FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const -{ - if (m_textBoxes.isEmpty()) - return FloatPoint(); - - StartPositionOfCharacterData data(position); - executeQuery(&data, &SVGTextQuery::startPositionOfCharacterCallback); - return data.startPosition; -} - -// endPositionOfCharacter() implementation -struct EndPositionOfCharacterData : SVGTextQuery::Data { - EndPositionOfCharacterData(unsigned queryPosition) - : position(queryPosition) - { - } - - unsigned position; - FloatPoint endPosition; -}; - -bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const -{ - EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(queryData); - - int startPosition = data->position; - int endPosition = startPosition + 1; - if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) - return false; - - const SVGChar& character = *(part.firstCharacter + startPosition); - data->endPosition = FloatPoint(character.x, character.y); - - RenderStyle* style = textBox->textRenderer()->style(); - ASSERT(style); - - bool isVerticalText = isVerticalWritingMode(style->svgStyle()); - float glyphAdvance = measureCharacterRange(textBox, style, isVerticalText, part.offset + startPosition, 1); - - if (isVerticalText) - data->endPosition.move(0.0f, glyphAdvance); - else - data->endPosition.move(glyphAdvance, 0.0f); - - data->endPosition = textBox->chunkTransformation().mapPoint(data->endPosition); - return true; -} - -FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const -{ - if (m_textBoxes.isEmpty()) - return FloatPoint(); - - EndPositionOfCharacterData data(position); - executeQuery(&data, &SVGTextQuery::endPositionOfCharacterCallback); - return data.endPosition; -} - -// rotationOfCharacter() implementation -struct RotationOfCharacterData : SVGTextQuery::Data { - RotationOfCharacterData(unsigned queryPosition) - : position(queryPosition) - , rotation(0.0f) - { - } - - unsigned position; - float rotation; -}; - -bool SVGTextQuery::rotationOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const -{ - RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryData); - - int startPosition = data->position; - int endPosition = startPosition + 1; - if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) - return false; - - const SVGChar& character = *(part.firstCharacter + startPosition); - data->rotation = character.angle; - return true; -} - -float SVGTextQuery::rotationOfCharacter(unsigned position) const -{ - if (m_textBoxes.isEmpty()) - return 0.0f; - - RotationOfCharacterData data(position); - executeQuery(&data, &SVGTextQuery::rotationOfCharacterCallback); - return data.rotation; -} - -// extentOfCharacter() implementation -struct ExtentOfCharacterData : SVGTextQuery::Data { - ExtentOfCharacterData(unsigned queryPosition) - : position(queryPosition) - { - } - - unsigned position; - FloatRect extent; -}; - -bool SVGTextQuery::extentOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const -{ - ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData); - - int startPosition = data->position; - int endPosition = startPosition + 1; - if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) - return false; - - RenderStyle* style = textBox->textRenderer()->style(); - ASSERT(style); - - const SVGChar& character = *(part.firstCharacter + startPosition); - data->extent = textBox->calculateGlyphBoundaries(style, part.offset + startPosition, character); - return true; -} - -FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const -{ - if (m_textBoxes.isEmpty()) - return FloatRect(); - - ExtentOfCharacterData data(position); - executeQuery(&data, &SVGTextQuery::extentOfCharacterCallback); - return data.extent; -} - -// characterNumberAtPosition() implementation -struct CharacterNumberAtPositionData : SVGTextQuery::Data { - CharacterNumberAtPositionData(const FloatPoint& queryPosition) - : characterNumber(0) - , position(queryPosition) - { - } - - unsigned characterNumber; - FloatPoint position; -}; - -bool SVGTextQuery::characterNumberAtPositionCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const -{ - CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionData*>(queryData); - - RenderStyle* style = textBox->textRenderer()->style(); - ASSERT(style); - - for (int i = 0; i < part.length; ++i) { - FloatRect extent(textBox->calculateGlyphBoundaries(style, part.offset + i, *(part.firstCharacter + i))); - if (extent.contains(data->position)) - return true; - - ++data->characterNumber; - } - - return false; -} - -int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const -{ - if (m_textBoxes.isEmpty()) - return -1; - - CharacterNumberAtPositionData data(position); - if (!executeQuery(&data, &SVGTextQuery::characterNumberAtPositionCallback)) - return -1; - - return data.characterNumber; -} - -} - -#endif diff --git a/WebCore/rendering/TextControlInnerElements.cpp b/WebCore/rendering/TextControlInnerElements.cpp index e5228f0..5d5b8b3 100644 --- a/WebCore/rendering/TextControlInnerElements.cpp +++ b/WebCore/rendering/TextControlInnerElements.cpp @@ -479,7 +479,7 @@ void InputFieldSpeechButtonElement::setRecognitionResult(int, const String& resu // here, we take a temporary reference. RefPtr<HTMLInputElement> holdRef(input); input->setValue(result); - input->dispatchFormControlChangeEvent(); + input->dispatchWebkitSpeechChangeEvent(); renderer()->repaint(); } diff --git a/WebCore/rendering/style/RenderStyle.cpp b/WebCore/rendering/style/RenderStyle.cpp index 623a298..b56bb1e 100644 --- a/WebCore/rendering/style/RenderStyle.cpp +++ b/WebCore/rendering/style/RenderStyle.cpp @@ -297,8 +297,12 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon changedContextSensitiveProperties = ContextSensitivePropertyNone; #if ENABLE(SVG) - if (m_svgStyle != other->m_svgStyle) - return m_svgStyle->diff(other->m_svgStyle.get()); + 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() || @@ -442,7 +446,7 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon return StyleDifferenceLayout; // Check block flow direction. - if (inherited_flags._blockFlow != other->inherited_flags._blockFlow) + if (inherited_flags.m_writingMode != other->inherited_flags.m_writingMode) return StyleDifferenceLayout; // Overflow returns a layout hint. @@ -475,6 +479,15 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon if ((visibility() == COLLAPSE) != (other->visibility() == COLLAPSE)) 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 // Make sure these left/top/right/bottom checks stay below all layout checks and above // all visible checks. @@ -922,52 +935,52 @@ void RenderStyle::setBlendedFontSize(int size) font().update(currentFontSelector); } -void RenderStyle::getBoxShadowExtent(int &top, int &right, int &bottom, int &left) const +void RenderStyle::getShadowExtent(const ShadowData* shadow, int &top, int &right, int &bottom, int &left) const { top = 0; right = 0; bottom = 0; left = 0; - for (const ShadowData* boxShadow = this->boxShadow(); boxShadow; boxShadow = boxShadow->next()) { - if (boxShadow->style() == Inset) + for ( ; shadow; shadow = shadow->next()) { + if (shadow->style() == Inset) continue; - int blurAndSpread = boxShadow->blur() + boxShadow->spread(); + int blurAndSpread = shadow->blur() + shadow->spread(); - top = min(top, boxShadow->y() - blurAndSpread); - right = max(right, boxShadow->x() + blurAndSpread); - bottom = max(bottom, boxShadow->y() + blurAndSpread); - left = min(left, boxShadow->x() - blurAndSpread); + top = min(top, shadow->y() - blurAndSpread); + right = max(right, shadow->x() + blurAndSpread); + bottom = max(bottom, shadow->y() + blurAndSpread); + left = min(left, shadow->x() - blurAndSpread); } } -void RenderStyle::getBoxShadowHorizontalExtent(int &left, int &right) const +void RenderStyle::getShadowHorizontalExtent(const ShadowData* shadow, int &left, int &right) const { left = 0; right = 0; - for (const ShadowData* boxShadow = this->boxShadow(); boxShadow; boxShadow = boxShadow->next()) { - if (boxShadow->style() == Inset) + for ( ; shadow; shadow = shadow->next()) { + if (shadow->style() == Inset) continue; - int blurAndSpread = boxShadow->blur() + boxShadow->spread(); + int blurAndSpread = shadow->blur() + shadow->spread(); - left = min(left, boxShadow->x() - blurAndSpread); - right = max(right, boxShadow->x() + blurAndSpread); + left = min(left, shadow->x() - blurAndSpread); + right = max(right, shadow->x() + blurAndSpread); } } -void RenderStyle::getBoxShadowVerticalExtent(int &top, int &bottom) const +void RenderStyle::getShadowVerticalExtent(const ShadowData* shadow, int &top, int &bottom) const { top = 0; bottom = 0; - for (const ShadowData* boxShadow = this->boxShadow(); boxShadow; boxShadow = boxShadow->next()) { - if (boxShadow->style() == Inset) + for ( ; shadow; shadow = shadow->next()) { + if (shadow->style() == Inset) continue; - int blurAndSpread = boxShadow->blur() + boxShadow->spread(); + int blurAndSpread = shadow->blur() + shadow->spread(); - top = min(top, boxShadow->y() - blurAndSpread); - bottom = max(bottom, boxShadow->y() + blurAndSpread); + top = min(top, shadow->y() - blurAndSpread); + bottom = max(bottom, shadow->y() + blurAndSpread); } } @@ -1066,56 +1079,56 @@ const Color RenderStyle::visitedDependentColor(int colorProperty) const Length RenderStyle::logicalWidth() const { - if (isVerticalBlockFlow()) + if (isHorizontalWritingMode()) return width(); return height(); } Length RenderStyle::logicalHeight() const { - if (isVerticalBlockFlow()) + if (isHorizontalWritingMode()) return height(); return width(); } Length RenderStyle::logicalMinWidth() const { - if (isVerticalBlockFlow()) + if (isHorizontalWritingMode()) return minWidth(); return minHeight(); } Length RenderStyle::logicalMaxWidth() const { - if (isVerticalBlockFlow()) + if (isHorizontalWritingMode()) return maxWidth(); return maxHeight(); } Length RenderStyle::logicalMinHeight() const { - if (isVerticalBlockFlow()) + if (isHorizontalWritingMode()) return minHeight(); return minWidth(); } Length RenderStyle::logicalMaxHeight() const { - if (isVerticalBlockFlow()) + if (isHorizontalWritingMode()) return maxHeight(); return maxWidth(); } unsigned short RenderStyle::borderBeforeWidth() const { - switch (blockFlow()) { - case TopToBottomBlockFlow: + switch (writingMode()) { + case TopToBottomWritingMode: return borderTopWidth(); - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return borderBottomWidth(); - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return borderLeftWidth(); - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return borderRightWidth(); } ASSERT_NOT_REACHED(); @@ -1124,14 +1137,14 @@ unsigned short RenderStyle::borderBeforeWidth() const unsigned short RenderStyle::borderAfterWidth() const { - switch (blockFlow()) { - case TopToBottomBlockFlow: + switch (writingMode()) { + case TopToBottomWritingMode: return borderBottomWidth(); - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return borderTopWidth(); - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return borderRightWidth(); - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return borderLeftWidth(); } ASSERT_NOT_REACHED(); @@ -1140,28 +1153,28 @@ unsigned short RenderStyle::borderAfterWidth() const unsigned short RenderStyle::borderStartWidth() const { - if (isVerticalBlockFlow()) - return direction() == LTR ? borderLeftWidth() : borderRightWidth(); - return direction() == LTR ? borderTopWidth() : borderBottomWidth(); + if (isHorizontalWritingMode()) + return isLeftToRightDirection() ? borderLeftWidth() : borderRightWidth(); + return isLeftToRightDirection() ? borderTopWidth() : borderBottomWidth(); } unsigned short RenderStyle::borderEndWidth() const { - if (isVerticalBlockFlow()) - return direction() == LTR ? borderRightWidth() : borderLeftWidth(); - return direction() == LTR ? borderBottomWidth() : borderTopWidth(); + if (isHorizontalWritingMode()) + return isLeftToRightDirection() ? borderRightWidth() : borderLeftWidth(); + return isLeftToRightDirection() ? borderBottomWidth() : borderTopWidth(); } Length RenderStyle::marginBefore() const { - switch (blockFlow()) { - case TopToBottomBlockFlow: + switch (writingMode()) { + case TopToBottomWritingMode: return marginTop(); - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return marginBottom(); - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return marginLeft(); - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return marginRight(); } ASSERT_NOT_REACHED(); @@ -1170,14 +1183,14 @@ Length RenderStyle::marginBefore() const Length RenderStyle::marginAfter() const { - switch (blockFlow()) { - case TopToBottomBlockFlow: + switch (writingMode()) { + case TopToBottomWritingMode: return marginBottom(); - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return marginTop(); - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return marginRight(); - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return marginLeft(); } ASSERT_NOT_REACHED(); @@ -1186,14 +1199,14 @@ Length RenderStyle::marginAfter() const Length RenderStyle::marginBeforeUsing(const RenderStyle* otherStyle) const { - switch (otherStyle->blockFlow()) { - case TopToBottomBlockFlow: + switch (otherStyle->writingMode()) { + case TopToBottomWritingMode: return marginTop(); - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return marginBottom(); - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return marginLeft(); - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return marginRight(); } ASSERT_NOT_REACHED(); @@ -1202,14 +1215,14 @@ Length RenderStyle::marginBeforeUsing(const RenderStyle* otherStyle) const Length RenderStyle::marginAfterUsing(const RenderStyle* otherStyle) const { - switch (otherStyle->blockFlow()) { - case TopToBottomBlockFlow: + switch (otherStyle->writingMode()) { + case TopToBottomWritingMode: return marginBottom(); - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return marginTop(); - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return marginRight(); - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return marginLeft(); } ASSERT_NOT_REACHED(); @@ -1218,42 +1231,42 @@ Length RenderStyle::marginAfterUsing(const RenderStyle* otherStyle) const Length RenderStyle::marginStart() const { - if (isVerticalBlockFlow()) - return direction() == LTR ? marginLeft() : marginRight(); - return direction() == LTR ? marginTop() : marginBottom(); + if (isHorizontalWritingMode()) + return isLeftToRightDirection() ? marginLeft() : marginRight(); + return isLeftToRightDirection() ? marginTop() : marginBottom(); } Length RenderStyle::marginEnd() const { - if (isVerticalBlockFlow()) - return direction() == LTR ? marginRight() : marginLeft(); - return direction() == LTR ? marginBottom() : marginTop(); + if (isHorizontalWritingMode()) + return isLeftToRightDirection() ? marginRight() : marginLeft(); + return isLeftToRightDirection() ? marginBottom() : marginTop(); } Length RenderStyle::marginStartUsing(const RenderStyle* otherStyle) const { - if (otherStyle->isVerticalBlockFlow()) - return otherStyle->direction() == LTR ? marginLeft() : marginRight(); - return otherStyle->direction() == LTR ? marginTop() : marginBottom(); + if (otherStyle->isHorizontalWritingMode()) + return otherStyle->isLeftToRightDirection() ? marginLeft() : marginRight(); + return otherStyle->isLeftToRightDirection() ? marginTop() : marginBottom(); } Length RenderStyle::marginEndUsing(const RenderStyle* otherStyle) const { - if (otherStyle->isVerticalBlockFlow()) - return otherStyle->direction() == LTR ? marginRight() : marginLeft(); - return otherStyle->direction() == LTR ? marginBottom() : marginTop(); + if (otherStyle->isHorizontalWritingMode()) + return otherStyle->isLeftToRightDirection() ? marginRight() : marginLeft(); + return otherStyle->isLeftToRightDirection() ? marginBottom() : marginTop(); } Length RenderStyle::paddingBefore() const { - switch (blockFlow()) { - case TopToBottomBlockFlow: + switch (writingMode()) { + case TopToBottomWritingMode: return paddingTop(); - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return paddingBottom(); - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return paddingLeft(); - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return paddingRight(); } ASSERT_NOT_REACHED(); @@ -1262,14 +1275,14 @@ Length RenderStyle::paddingBefore() const Length RenderStyle::paddingAfter() const { - switch (blockFlow()) { - case TopToBottomBlockFlow: + switch (writingMode()) { + case TopToBottomWritingMode: return paddingBottom(); - case BottomToTopBlockFlow: + case BottomToTopWritingMode: return paddingTop(); - case LeftToRightBlockFlow: + case LeftToRightWritingMode: return paddingRight(); - case RightToLeftBlockFlow: + case RightToLeftWritingMode: return paddingLeft(); } ASSERT_NOT_REACHED(); @@ -1278,16 +1291,16 @@ Length RenderStyle::paddingAfter() const Length RenderStyle::paddingStart() const { - if (isVerticalBlockFlow()) - return direction() == LTR ? paddingLeft() : paddingRight(); - return direction() == LTR ? paddingTop() : paddingBottom(); + if (isHorizontalWritingMode()) + return isLeftToRightDirection() ? paddingLeft() : paddingRight(); + return isLeftToRightDirection() ? paddingTop() : paddingBottom(); } Length RenderStyle::paddingEnd() const { - if (isVerticalBlockFlow()) - return direction() == LTR ? paddingRight() : paddingLeft(); - return direction() == LTR ? paddingBottom() : paddingTop(); + if (isHorizontalWritingMode()) + return isLeftToRightDirection() ? paddingRight() : paddingLeft(); + return isLeftToRightDirection() ? paddingBottom() : paddingTop(); } } // namespace WebCore diff --git a/WebCore/rendering/style/RenderStyle.h b/WebCore/rendering/style/RenderStyle.h index 6ecbd56..587b473 100644 --- a/WebCore/rendering/style/RenderStyle.h +++ b/WebCore/rendering/style/RenderStyle.h @@ -29,7 +29,6 @@ #include "AnimationList.h" #include "BorderData.h" #include "BorderValue.h" -#include "CSSHelper.h" #include "CSSImageGeneratorValue.h" #include "CSSPrimitiveValue.h" #include "CSSPropertyNames.h" @@ -179,7 +178,7 @@ protected: (_force_backgrounds_to_white == other._force_backgrounds_to_white) && (_pointerEvents == other._pointerEvents) && (_insideLink == other._insideLink) && - (_blockFlow == other._blockFlow); + (m_writingMode == other.m_writingMode); } bool operator!=(const InheritedFlags& other) const { return !(*this == other); } @@ -207,7 +206,7 @@ protected: // 43 bits // CSS Text Layout Module Level 3: Vertical writing support - unsigned _blockFlow : 2; // EBlockFlowDirection + unsigned m_writingMode : 2; // WritingMode // 45 bits } inherited_flags; @@ -284,7 +283,7 @@ protected: inherited_flags._force_backgrounds_to_white = false; inherited_flags._pointerEvents = initialPointerEvents(); inherited_flags._insideLink = NotInsideLink; - inherited_flags._blockFlow = initialBlockFlow(); + inherited_flags.m_writingMode = initialWritingMode(); noninherited_flags._effectiveDisplay = noninherited_flags._originalDisplay = initialDisplay(); noninherited_flags._overflowX = initialOverflowX(); @@ -307,10 +306,10 @@ protected: } private: - RenderStyle(); + ALWAYS_INLINE RenderStyle(); // used to create the default style. - RenderStyle(bool); - RenderStyle(const RenderStyle&); + ALWAYS_INLINE RenderStyle(bool); + ALWAYS_INLINE RenderStyle(const RenderStyle&); public: static PassRefPtr<RenderStyle> create(); @@ -476,6 +475,8 @@ public: float effectiveZoom() const { return rareInheritedData->m_effectiveZoom; } TextDirection direction() const { return static_cast<TextDirection>(inherited_flags._direction); } + bool isLeftToRightDirection() const { return direction() == LTR; } + Length lineHeight() const { return inherited->line_height; } int computedLineHeight() const { @@ -636,6 +637,11 @@ public: } const ShadowData* textShadow() const { return rareInheritedData->textShadow; } + void getTextShadowExtent(int& top, int& right, int& bottom, int& left) const { getShadowExtent(textShadow(), top, right, bottom, left); } + void getTextShadowHorizontalExtent(int& left, int& right) const { getShadowHorizontalExtent(textShadow(), left, right); } + void getTextShadowVerticalExtent(int& top, int& bottom) const { getShadowVerticalExtent(textShadow(), top, bottom); } + void getTextShadowInlineDirectionExtent(int& logicalLeft, int& logicalRight) { getShadowInlineDirectionExtent(textShadow(), logicalLeft, logicalRight); } + float textStrokeWidth() const { return rareInheritedData->textStrokeWidth; } ColorSpace colorSpace() const { return static_cast<ColorSpace>(rareInheritedData->colorSpace); } float opacity() const { return rareNonInheritedData->opacity; } @@ -650,9 +656,10 @@ public: EBoxAlignment boxPack() const { return static_cast<EBoxAlignment>(rareNonInheritedData->flexibleBox->pack); } const ShadowData* boxShadow() const { return rareNonInheritedData->m_boxShadow.get(); } - void getBoxShadowExtent(int &top, int &right, int &bottom, int &left) const; - void getBoxShadowHorizontalExtent(int &left, int &right) const; - void getBoxShadowVerticalExtent(int &top, int &bottom) const; + void getBoxShadowExtent(int& top, int& right, int& bottom, int& left) const { getShadowExtent(boxShadow(), top, right, bottom, left); } + void getBoxShadowHorizontalExtent(int& left, int& right) const { getShadowHorizontalExtent(boxShadow(), left, right); } + void getBoxShadowVerticalExtent(int& top, int& bottom) const { getShadowVerticalExtent(boxShadow(), top, bottom); } + void getBoxShadowInlineDirectionExtent(int& logicalLeft, int& logicalRight) { getShadowInlineDirectionExtent(boxShadow(), logicalLeft, logicalRight); } StyleReflection* boxReflect() const { return rareNonInheritedData->m_boxReflect.get(); } EBoxSizing boxSizing() const { return m_box->boxSizing(); } @@ -742,9 +749,11 @@ public: bool textSizeAdjust() const { return rareInheritedData->textSizeAdjust; } ETextSecurity textSecurity() const { return static_cast<ETextSecurity>(rareInheritedData->textSecurity); } - EBlockFlowDirection blockFlow() const { return static_cast<EBlockFlowDirection>(inherited_flags._blockFlow); } - bool isVerticalBlockFlow() const { return blockFlow() == TopToBottomBlockFlow || blockFlow() == BottomToTopBlockFlow; } + WritingMode writingMode() const { return static_cast<WritingMode>(inherited_flags.m_writingMode); } + bool isHorizontalWritingMode() const { return writingMode() == TopToBottomWritingMode || writingMode() == BottomToTopWritingMode; } + bool isFlippedLinesWritingMode() const { return writingMode() == LeftToRightWritingMode || writingMode() == BottomToTopWritingMode; } +<<<<<<< HEAD #ifdef ANDROID_CSS_RING // called when building nav cache to determine if the ring data is unchanged const void* ringData() const { return reinterpret_cast<const void*>(rareInheritedData.get()); } @@ -762,6 +771,10 @@ public: Color tapHighlightColor() const { return rareInheritedData->tapHighlightColor; } #endif +======= + ESpeak speak() { return static_cast<ESpeak>(rareInheritedData->speak); } + +>>>>>>> webkit.org at r70209 // attribute setter methods void setDisplay(EDisplay v) { noninherited_flags._effectiveDisplay = v; } @@ -1054,6 +1067,7 @@ public: void setTransformOriginX(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_x, l); } void setTransformOriginY(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_y, l); } void setTransformOriginZ(float f) { SET_VAR(rareNonInheritedData.access()->m_transform, m_z, f); } + void setSpeak(ESpeak s) { SET_VAR(rareInheritedData, speak, s); } // End CSS3 Setters // Apple-specific property setters @@ -1156,7 +1170,7 @@ public: originalDisplay() == INLINE_BOX || originalDisplay() == INLINE_TABLE; } - void setBlockFlow(EBlockFlowDirection v) { inherited_flags._blockFlow = v; } + void setWritingMode(WritingMode v) { inherited_flags.m_writingMode = v; } // To tell if this style matched attribute selectors. This makes it impossible to share. bool affectedByAttributeSelectors() const { return m_affectedByAttributeSelectors; } @@ -1197,7 +1211,7 @@ public: static ECaptionSide initialCaptionSide() { return CAPTOP; } static EClear initialClear() { return CNONE; } static TextDirection initialDirection() { return LTR; } - static EBlockFlowDirection initialBlockFlow() { return TopToBottomBlockFlow; } + static WritingMode initialWritingMode() { return TopToBottomWritingMode; } static EDisplay initialDisplay() { return INLINE; } static EEmptyCell initialEmptyCells() { return SHOW; } static EFloat initialFloating() { return FNONE; } @@ -1262,6 +1276,7 @@ public: static EKHTMLLineBreak initialKHTMLLineBreak() { return LBNORMAL; } static EMatchNearestMailBlockquoteColor initialMatchNearestMailBlockquoteColor() { return BCNORMAL; } static const AtomicString& initialHighlight() { return nullAtom; } + static ESpeak initialSpeak() { return SpeakNormal; } static Hyphens initialHyphens() { return HyphensManual; } static const AtomicString& initialHyphenationString() { return nullAtom; } static const AtomicString& initialHyphenationLocale() { return nullAtom; } @@ -1309,6 +1324,14 @@ public: #endif private: + void getShadowExtent(const ShadowData*, int& top, int& right, int& bottom, int& left) const; + void getShadowHorizontalExtent(const ShadowData*, int& left, int& right) const; + void getShadowVerticalExtent(const ShadowData*, int& top, int& bottom) const; + void getShadowInlineDirectionExtent(const ShadowData* shadow, int& logicalLeft, int& logicalRight) const + { + return isHorizontalWritingMode() ? getShadowHorizontalExtent(shadow, logicalLeft, logicalRight) : getShadowVerticalExtent(shadow, logicalLeft, logicalRight); + } + // Color accessors are all private to make sure callers use visitedDependentColor instead to access them. const Color& borderLeftColor() const { return surround->border.left().color(); } const Color& borderRightColor() const { return surround->border.right().color(); } diff --git a/WebCore/rendering/style/RenderStyleConstants.h b/WebCore/rendering/style/RenderStyleConstants.h index 94d30d5..0112c03 100644 --- a/WebCore/rendering/style/RenderStyleConstants.h +++ b/WebCore/rendering/style/RenderStyleConstants.h @@ -129,8 +129,8 @@ enum EUnicodeBidi { }; // CSS Text Layout Module Level 3: Vertical writing support -enum EBlockFlowDirection { - TopToBottomBlockFlow, RightToLeftBlockFlow, LeftToRightBlockFlow, BottomToTopBlockFlow +enum WritingMode { + TopToBottomWritingMode, RightToLeftWritingMode, LeftToRightWritingMode, BottomToTopWritingMode }; enum EFillAttachment { @@ -415,6 +415,8 @@ enum ELineClampType { LineClampLineCount, LineClampPercentage }; enum Hyphens { HyphensNone, HyphensManual, HyphensAuto }; +enum ESpeak { SpeakNone, SpeakNormal, SpeakSpellOut, SpeakDigits, SpeakLiteralPunctuation, SpeakNoPunctuation }; + } // namespace WebCore #endif // RenderStyleConstants_h diff --git a/WebCore/rendering/style/SVGRenderStyle.cpp b/WebCore/rendering/style/SVGRenderStyle.cpp index 7d1ad3b..28f80f2 100644 --- a/WebCore/rendering/style/SVGRenderStyle.cpp +++ b/WebCore/rendering/style/SVGRenderStyle.cpp @@ -138,7 +138,7 @@ StyleDifference SVGRenderStyle::diff(const SVGRenderStyle* other) const if (resources != other->resources) return StyleDifferenceLayout; - // If markers change, we need a relayout, as marker boundaries are cached in RenderPath. + // If markers change, we need a relayout, as marker boundaries are cached in RenderSVGPath. if (inheritedResources != other->inheritedResources) return StyleDifferenceLayout; @@ -190,7 +190,7 @@ StyleDifference SVGRenderStyle::diff(const SVGRenderStyle* other) const return StyleDifferenceRepaint; } - // If fill changes, we just need to repaint. Fill boundaries are not influenced by this, only by the Path, that RenderPath contains. + // If fill changes, we just need to repaint. Fill boundaries are not influenced by this, only by the Path, that RenderSVGPath contains. if (fill != other->fill) return StyleDifferenceRepaint; diff --git a/WebCore/rendering/style/SVGRenderStyle.h b/WebCore/rendering/style/SVGRenderStyle.h index d57e4cf..8f0be39 100644 --- a/WebCore/rendering/style/SVGRenderStyle.h +++ b/WebCore/rendering/style/SVGRenderStyle.h @@ -67,7 +67,7 @@ public: static LineJoin initialJoinStyle() { return MiterJoin; } static EShapeRendering initialShapeRendering() { return SR_AUTO; } static ETextAnchor initialTextAnchor() { return TA_START; } - static EWritingMode initialWritingMode() { return WM_LRTB; } + static SVGWritingMode initialWritingMode() { return WM_LRTB; } static EGlyphOrientation initialGlyphOrientationHorizontal() { return GO_0DEG; } static EGlyphOrientation initialGlyphOrientationVertical() { return GO_AUTO; } static float initialFillOpacity() { return 1.0f; } @@ -132,7 +132,7 @@ public: void setJoinStyle(LineJoin val) { svg_inherited_flags._joinStyle = val; } void setShapeRendering(EShapeRendering val) { svg_inherited_flags._shapeRendering = val; } void setTextAnchor(ETextAnchor val) { svg_inherited_flags._textAnchor = val; } - void setWritingMode(EWritingMode val) { svg_inherited_flags._writingMode = val; } + void setWritingMode(SVGWritingMode val) { svg_inherited_flags._writingMode = val; } void setGlyphOrientationHorizontal(EGlyphOrientation val) { svg_inherited_flags._glyphOrientationHorizontal = val; } void setGlyphOrientationVertical(EGlyphOrientation val) { svg_inherited_flags._glyphOrientationVertical = val; } @@ -281,7 +281,7 @@ public: LineJoin joinStyle() const { return (LineJoin) svg_inherited_flags._joinStyle; } EShapeRendering shapeRendering() const { return (EShapeRendering) svg_inherited_flags._shapeRendering; } ETextAnchor textAnchor() const { return (ETextAnchor) svg_inherited_flags._textAnchor; } - EWritingMode writingMode() const { return (EWritingMode) svg_inherited_flags._writingMode; } + SVGWritingMode writingMode() const { return (SVGWritingMode) svg_inherited_flags._writingMode; } EGlyphOrientation glyphOrientationHorizontal() const { return (EGlyphOrientation) svg_inherited_flags._glyphOrientationHorizontal; } EGlyphOrientation glyphOrientationVertical() const { return (EGlyphOrientation) svg_inherited_flags._glyphOrientationVertical; } float fillOpacity() const { return fill->opacity; } @@ -314,6 +314,7 @@ public: bool hasMarkers() const { return !markerStartResource().isEmpty() || !markerMidResource().isEmpty() || !markerEndResource().isEmpty(); } bool hasStroke() const { return strokePaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE; } bool hasFill() const { return fillPaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE; } + bool isVerticalWritingMode() const { return writingMode() == WM_TBRL || writingMode() == WM_TB; } protected: // inherit @@ -350,7 +351,7 @@ protected: unsigned _textAnchor : 2; // ETextAnchor unsigned _colorInterpolation : 2; // EColorInterpolation unsigned _colorInterpolationFilters : 2; // EColorInterpolation - unsigned _writingMode : 3; // EWritingMode + unsigned _writingMode : 3; // SVGWritingMode unsigned _glyphOrientationHorizontal : 3; // EGlyphOrientation unsigned _glyphOrientationVertical : 3; // EGlyphOrientation } svg_inherited_flags; diff --git a/WebCore/rendering/style/SVGRenderStyleDefs.h b/WebCore/rendering/style/SVGRenderStyleDefs.h index 339bb77..de058a2 100644 --- a/WebCore/rendering/style/SVGRenderStyleDefs.h +++ b/WebCore/rendering/style/SVGRenderStyleDefs.h @@ -64,7 +64,7 @@ namespace WebCore { SR_AUTO, SR_OPTIMIZESPEED, SR_CRISPEDGES, SR_GEOMETRICPRECISION }; - enum EWritingMode { + enum SVGWritingMode { WM_LRTB, WM_LR, WM_RLTB, WM_RL, WM_TBRL, WM_TB }; diff --git a/WebCore/rendering/style/StyleAllInOne.cpp b/WebCore/rendering/style/StyleAllInOne.cpp new file mode 100644 index 0000000..25b539f --- /dev/null +++ b/WebCore/rendering/style/StyleAllInOne.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Apple 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. + */ + +// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build. + +#include "ContentData.cpp" +#include "CounterDirectives.cpp" +#include "FillLayer.cpp" +#include "KeyframeList.cpp" +#include "NinePieceImage.cpp" +#include "RenderStyle.cpp" +#include "SVGRenderStyle.cpp" +#include "SVGRenderStyleDefs.cpp" +#include "ShadowData.cpp" +#include "StyleBackgroundData.cpp" +#include "StyleBoxData.cpp" +#include "StyleCachedImage.cpp" +#include "StyleFlexibleBoxData.cpp" +#include "StyleGeneratedImage.cpp" +#include "StyleInheritedData.cpp" +#include "StyleMarqueeData.cpp" +#include "StyleMultiColData.cpp" +#include "StyleRareInheritedData.cpp" +#include "StyleRareNonInheritedData.cpp" +#include "StyleSurroundData.cpp" +#include "StyleTransformData.cpp" +#include "StyleVisualData.cpp" diff --git a/WebCore/rendering/style/StyleRareInheritedData.cpp b/WebCore/rendering/style/StyleRareInheritedData.cpp index 42f2030..af2b555 100644 --- a/WebCore/rendering/style/StyleRareInheritedData.cpp +++ b/WebCore/rendering/style/StyleRareInheritedData.cpp @@ -57,7 +57,8 @@ StyleRareInheritedData::StyleRareInheritedData() , textSizeAdjust(RenderStyle::initialTextSizeAdjust()) , resize(RenderStyle::initialResize()) , userSelect(RenderStyle::initialUserSelect()) - , colorSpace(DeviceColorSpace) + , colorSpace(ColorSpaceDeviceRGB) + , speak(SpeakNormal) , hyphens(HyphensManual) { } @@ -98,6 +99,7 @@ StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o) , resize(o.resize) , userSelect(o.userSelect) , colorSpace(o.colorSpace) + , speak(o.speak) , hyphens(o.hyphens) , hyphenationString(o.hyphenationString) , hyphenationLocale(o.hyphenationLocale) @@ -154,6 +156,7 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const && resize == o.resize && userSelect == o.userSelect && colorSpace == o.colorSpace + && speak == o.speak && hyphens == o.hyphens && hyphenationString == o.hyphenationString && hyphenationLocale == o.hyphenationLocale; diff --git a/WebCore/rendering/style/StyleRareInheritedData.h b/WebCore/rendering/style/StyleRareInheritedData.h index ba914d4..4ebbf88 100644 --- a/WebCore/rendering/style/StyleRareInheritedData.h +++ b/WebCore/rendering/style/StyleRareInheritedData.h @@ -91,6 +91,7 @@ public: unsigned resize : 2; // EResize unsigned userSelect : 1; // EUserSelect unsigned colorSpace : 1; // ColorSpace + unsigned speak : 3; // ESpeak unsigned hyphens : 2; // Hyphens AtomicString hyphenationString; diff --git a/WebCore/rendering/RenderSVGInline.cpp b/WebCore/rendering/svg/RenderSVGInline.cpp index 5d12a61..4d0c533 100644 --- a/WebCore/rendering/RenderSVGInline.cpp +++ b/WebCore/rendering/svg/RenderSVGInline.cpp @@ -1,8 +1,7 @@ /* - * This file is part of the WebKit project. - * * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) Research In Motion Limited 2010. 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 @@ -27,6 +26,7 @@ #include "RenderSVGInline.h" #include "RenderSVGResource.h" +#include "RenderSVGText.h" #include "SVGInlineFlowBox.h" namespace WebCore { @@ -45,7 +45,7 @@ InlineFlowBox* RenderSVGInline::createInlineFlowBox() FloatRect RenderSVGInline::objectBoundingBox() const { - if (const RenderObject* object = SVGRenderSupport::findTextRootObject(this)) + if (const RenderObject* object = RenderSVGText::locateRenderSVGTextAncestor(this)) return object->objectBoundingBox(); return FloatRect(); @@ -53,7 +53,7 @@ FloatRect RenderSVGInline::objectBoundingBox() const FloatRect RenderSVGInline::strokeBoundingBox() const { - if (const RenderObject* object = SVGRenderSupport::findTextRootObject(this)) + if (const RenderObject* object = RenderSVGText::locateRenderSVGTextAncestor(this)) return object->strokeBoundingBox(); return FloatRect(); @@ -61,7 +61,7 @@ FloatRect RenderSVGInline::strokeBoundingBox() const FloatRect RenderSVGInline::repaintRectInLocalCoordinates() const { - if (const RenderObject* object = SVGRenderSupport::findTextRootObject(this)) + if (const RenderObject* object = RenderSVGText::locateRenderSVGTextAncestor(this)) return object->repaintRectInLocalCoordinates(); return FloatRect(); @@ -84,7 +84,7 @@ void RenderSVGInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer void RenderSVGInline::absoluteQuads(Vector<FloatQuad>& quads) { - const RenderObject* object = SVGRenderSupport::findTextRootObject(this); + RenderObject* object = RenderSVGText::locateRenderSVGTextAncestor(this); if (!object) return; diff --git a/WebCore/rendering/RenderSVGInline.h b/WebCore/rendering/svg/RenderSVGInline.h index 92b6fe7..d7b7e66 100644 --- a/WebCore/rendering/RenderSVGInline.h +++ b/WebCore/rendering/svg/RenderSVGInline.h @@ -33,7 +33,7 @@ namespace WebCore { class RenderSVGInline : public RenderInline { public: - RenderSVGInline(Node*); + explicit RenderSVGInline(Node*); virtual const char* renderName() const { return "RenderSVGInline"; } virtual bool requiresLayer() const { return false; } diff --git a/WebCore/rendering/svg/RenderSVGInlineText.cpp b/WebCore/rendering/svg/RenderSVGInlineText.cpp new file mode 100644 index 0000000..49727ee --- /dev/null +++ b/WebCore/rendering/svg/RenderSVGInlineText.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * Copyright (C) 2006 Apple Computer Inc. + * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Rob Buis <buis@kde.org> + * Copyright (C) Research In Motion Limited 2010. 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. + * + */ + +#include "config.h" + +#if ENABLE(SVG) +#include "RenderSVGInlineText.h" + +#include "FloatConversion.h" +#include "FloatQuad.h" +#include "RenderBlock.h" +#include "RenderSVGRoot.h" +#include "RenderSVGText.h" +#include "SVGInlineTextBox.h" +#include "SVGRootInlineBox.h" +#include "VisiblePosition.h" + +namespace WebCore { + +static PassRefPtr<StringImpl> applySVGWhitespaceRules(PassRefPtr<StringImpl> string, bool preserveWhiteSpace) +{ + if (preserveWhiteSpace) { + // Spec: When xml:space="preserve", the SVG user agent will do the following using a + // copy of the original character data content. It will convert all newline and tab + // characters into space characters. Then, it will draw all space characters, including + // leading, trailing and multiple contiguous space characters. + RefPtr<StringImpl> newString = string->replace('\t', ' '); + newString = newString->replace('\n', ' '); + newString = newString->replace('\r', ' '); + return newString.release(); + } + + // Spec: When xml:space="default", the SVG user agent will do the following using a + // copy of the original character data content. First, it will remove all newline + // characters. Then it will convert all tab characters into space characters. + // Then, it will strip off all leading and trailing space characters. + // Then, all contiguous space characters will be consolidated. + RefPtr<StringImpl> newString = string->replace('\n', StringImpl::empty()); + newString = newString->replace('\r', StringImpl::empty()); + newString = newString->replace('\t', ' '); + return newString.release(); +} + +RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> string) + : RenderText(n, applySVGWhitespaceRules(string, false)) +{ +} + +void RenderSVGInlineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderText::styleDidChange(diff, oldStyle); + + if (diff == StyleDifferenceLayout) { + // The text metrics may be influenced by style changes. + if (RenderSVGText* textRenderer = RenderSVGText::locateRenderSVGTextAncestor(this)) + textRenderer->setNeedsPositioningValuesUpdate(); + } + + const RenderStyle* newStyle = style(); + if (!newStyle || newStyle->whiteSpace() != PRE) + return; + + if (!oldStyle || oldStyle->whiteSpace() != PRE) + setText(applySVGWhitespaceRules(originalText(), true), true); +} + +InlineTextBox* RenderSVGInlineText::createTextBox() +{ + InlineTextBox* box = new (renderArena()) SVGInlineTextBox(this); + box->setHasVirtualLogicalHeight(); + return box; +} + +IntRect RenderSVGInlineText::localCaretRect(InlineBox*, int, int*) +{ + return IntRect(); +} + +IntRect RenderSVGInlineText::linesBoundingBox() const +{ + IntRect boundingBox; + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + boundingBox.unite(box->calculateBoundaries()); + return boundingBox; +} + +bool RenderSVGInlineText::characterStartsNewTextChunk(int position) const +{ + ASSERT(m_attributes.xValues().size() == textLength()); + ASSERT(m_attributes.yValues().size() == textLength()); + ASSERT(position >= 0); + ASSERT(position < static_cast<int>(textLength())); + + // Each <textPath> element starts a new text chunk, regardless of any x/y values. + if (!position && parent()->isSVGTextPath() && !previousSibling()) + return true; + + int currentPosition = 0; + unsigned size = m_attributes.textMetricsValues().size(); + for (unsigned i = 0; i < size; ++i) { + const SVGTextMetrics& metrics = m_attributes.textMetricsValues().at(i); + + // We found the desired character. + if (currentPosition == position) { + return m_attributes.xValues().at(position) != SVGTextLayoutAttributes::emptyValue() + || m_attributes.yValues().at(position) != SVGTextLayoutAttributes::emptyValue(); + } + + currentPosition += metrics.length(); + if (currentPosition > position) + break; + } + + // The desired position is available in the x/y list, but not in the character data values list. + // That means the previous character data described a single glyph, consisting of multiple unicode characters. + // The consequence is that the desired character does not define a new absolute x/y position, even if present in the x/y test. + // This code is tested by svg/W3C-SVG-1.1/text-text-06-t.svg (and described in detail, why this influences chunk detection). + return false; +} + +VisiblePosition RenderSVGInlineText::positionForPoint(const IntPoint& point) +{ + if (!firstTextBox() || !textLength()) + return createVisiblePosition(0, DOWNSTREAM); + + RenderStyle* style = this->style(); + ASSERT(style); + int baseline = style->font().ascent(); + + RenderBlock* containingBlock = this->containingBlock(); + ASSERT(containingBlock); + + // Map local point to absolute point, as the character origins stored in the text fragments use absolute coordinates. + FloatPoint absolutePoint(point); + absolutePoint.move(containingBlock->x(), containingBlock->y()); + + float closestDistance = std::numeric_limits<float>::max(); + float closestDistancePosition = 0; + const SVGTextFragment* closestDistanceFragment = 0; + SVGInlineTextBox* closestDistanceBox = 0; + + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { + ASSERT(box->isSVGInlineTextBox()); + SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(box); + Vector<SVGTextFragment>& fragments = textBox->textFragments(); + + unsigned textFragmentsSize = fragments.size(); + for (unsigned i = 0; i < textFragmentsSize; ++i) { + const SVGTextFragment& fragment = fragments.at(i); + FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height); + if (!fragment.transform.isIdentity()) + fragmentRect = fragment.transform.mapRect(fragmentRect); + + float distance = powf(fragmentRect.x() - absolutePoint.x(), 2) + + powf(fragmentRect.y() + fragmentRect.height() / 2 - absolutePoint.y(), 2); + + if (distance < closestDistance) { + closestDistance = distance; + closestDistanceBox = textBox; + closestDistanceFragment = &fragment; + closestDistancePosition = fragmentRect.x(); + } + } + } + + if (!closestDistanceFragment) + return createVisiblePosition(0, DOWNSTREAM); + + int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanceFragment, absolutePoint.x() - closestDistancePosition, true); + return createVisiblePosition(offset + closestDistanceBox->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); +} + +} + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/RenderSVGInlineText.h b/WebCore/rendering/svg/RenderSVGInlineText.h index f606918..926ec43 100644 --- a/WebCore/rendering/RenderSVGInlineText.h +++ b/WebCore/rendering/svg/RenderSVGInlineText.h @@ -31,11 +31,16 @@ namespace WebCore { +class SVGInlineTextBox; + class RenderSVGInlineText : public RenderText { public: RenderSVGInlineText(Node*, PassRefPtr<StringImpl>); bool characterStartsNewTextChunk(int position) const; + + SVGTextLayoutAttributes& layoutAttributes() { return m_attributes; } + const SVGTextLayoutAttributes& layoutAttributes() const { return m_attributes; } void storeLayoutAttributes(const SVGTextLayoutAttributes& attributes) { m_attributes = attributes; } private: @@ -50,6 +55,7 @@ private: virtual bool requiresLayer() const { return false; } virtual bool isSVGInlineText() const { return true; } + virtual VisiblePosition positionForPoint(const IntPoint&); virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); virtual IntRect linesBoundingBox() const; virtual InlineTextBox* createTextBox(); @@ -57,8 +63,22 @@ private: SVGTextLayoutAttributes m_attributes; }; +inline RenderSVGInlineText* toRenderSVGInlineText(RenderObject* object) +{ + ASSERT(!object || object->isSVGInlineText()); + return static_cast<RenderSVGInlineText*>(object); } -#endif // ENABLE(SVG) +inline const RenderSVGInlineText* toRenderSVGInlineText(const RenderObject* object) +{ + ASSERT(!object || object->isSVGInlineText()); + return static_cast<const RenderSVGInlineText*>(object); +} -#endif // !RenderSVGInlineText_h +// This will catch anyone doing an unnecessary cast. +void toRenderSVGInlineText(const RenderSVGInlineText*); + +} + +#endif // ENABLE(SVG) +#endif // RenderSVGInlineText_h diff --git a/WebCore/rendering/RenderPath.cpp b/WebCore/rendering/svg/RenderSVGPath.cpp index cbe1900..483303f 100644 --- a/WebCore/rendering/RenderPath.cpp +++ b/WebCore/rendering/svg/RenderSVGPath.cpp @@ -26,7 +26,7 @@ #include "config.h" #if ENABLE(SVG) -#include "RenderPath.h" +#include "RenderSVGPath.h" #include "FloatPoint.h" #include "FloatQuad.h" @@ -35,6 +35,7 @@ #include "PointerEventsHitRules.h" #include "RenderSVGContainer.h" #include "RenderSVGResourceMarker.h" +#include "RenderSVGResourceSolidColor.h" #include "SVGRenderSupport.h" #include "SVGResources.h" #include "SVGStyledTransformableElement.h" @@ -65,7 +66,7 @@ private: RenderStyle* m_style; }; -RenderPath::RenderPath(SVGStyledTransformableElement* node) +RenderSVGPath::RenderSVGPath(SVGStyledTransformableElement* node) : RenderSVGModelObject(node) , m_needsBoundariesUpdate(false) // default is false, the cached rects are empty from the beginning , m_needsPathUpdate(true) // default is true, so we grab a Path object once from SVGStyledTransformableElement @@ -73,30 +74,36 @@ RenderPath::RenderPath(SVGStyledTransformableElement* node) { } -bool RenderPath::fillContains(const FloatPoint& point, bool requiresFill, WindRule fillRule) +RenderSVGPath::~RenderSVGPath() +{ +} + +bool RenderSVGPath::fillContains(const FloatPoint& point, bool requiresFill, WindRule fillRule) { if (!m_fillBoundingBox.contains(point)) return false; - if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style())) + Color fallbackColor; + if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor)) return false; return m_path.contains(point, fillRule); } -bool RenderPath::strokeContains(const FloatPoint& point, bool requiresStroke) +bool RenderSVGPath::strokeContains(const FloatPoint& point, bool requiresStroke) { if (!m_strokeAndMarkerBoundingBox.contains(point)) return false; - if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style())) + Color fallbackColor; + if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor)) return false; BoundingRectStrokeStyleApplier strokeStyle(this, style()); return m_path.strokeContains(&strokeStyle, point); } -void RenderPath::layout() +void RenderSVGPath::layout() { LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout()); SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node()); @@ -105,7 +112,8 @@ void RenderPath::layout() bool needsPathUpdate = m_needsPathUpdate; if (needsPathUpdate) { - m_path = element->toPathData(); + m_path.clear(); + element->toPathData(m_path); m_needsPathUpdate = false; updateCachedBoundariesInParents = true; } @@ -138,18 +146,26 @@ void RenderPath::layout() setNeedsLayout(false); } -void RenderPath::fillAndStrokePath(GraphicsContext* context) +void RenderSVGPath::fillAndStrokePath(GraphicsContext* context) { context->beginPath(); RenderStyle* style = this->style(); - if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style)) { + Color fallbackColor; + if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) { context->addPath(m_path); if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode)) fillPaintingResource->postApplyResource(this, context, ApplyToFillMode); + else if (fallbackColor.isValid()) { + RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource(); + fallbackResource->setColor(fallbackColor); + if (fallbackResource->applyResource(this, style, context, ApplyToFillMode)) + fallbackResource->postApplyResource(this, context, ApplyToFillMode); + } } - RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style); + fallbackColor = Color(); + RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor); if (!strokePaintingResource) return; @@ -172,12 +188,18 @@ void RenderPath::fillAndStrokePath(GraphicsContext* context) if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode)) strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode); + else if (fallbackColor.isValid()) { + RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource(); + fallbackResource->setColor(fallbackColor); + if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode)) + fallbackResource->postApplyResource(this, context, ApplyToStrokeMode); + } if (restoreContext) context->restore(); } -void RenderPath::paint(PaintInfo& paintInfo, int, int) +void RenderSVGPath::paint(PaintInfo& paintInfo, int, int) { if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty()) return; @@ -219,14 +241,14 @@ void RenderPath::paint(PaintInfo& paintInfo, int, int) // This method is called from inside paintOutline() since we call paintOutline() // while transformed to our coord system, return local coords -void RenderPath::addFocusRingRects(Vector<IntRect>& rects, int, int) +void RenderSVGPath::addFocusRingRects(Vector<IntRect>& rects, int, int) { IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates()); if (!rect.isEmpty()) rects.append(rect); } -bool RenderPath::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) +bool RenderSVGPath::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) { // We only draw in the forground phase, so we only hit-test then. if (hitTestAction != HitTestForeground) @@ -253,7 +275,7 @@ bool RenderPath::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& return false; } -FloatRect RenderPath::calculateMarkerBoundsIfNeeded() +FloatRect RenderSVGPath::calculateMarkerBoundsIfNeeded() { SVGElement* svgElement = static_cast<SVGElement*>(node()); ASSERT(svgElement && svgElement->document()); @@ -280,7 +302,7 @@ FloatRect RenderPath::calculateMarkerBoundsIfNeeded() return m_markerLayoutInfo.calculateBoundaries(markerStart, markerMid, markerEnd, svgStyle->strokeWidth().value(svgElement), m_path); } -void RenderPath::updateCachedBoundaries() +void RenderSVGPath::updateCachedBoundaries() { if (m_path.isEmpty()) { m_fillBoundingBox = FloatRect(); diff --git a/WebCore/rendering/RenderPath.h b/WebCore/rendering/svg/RenderSVGPath.h index 1d35a01..41b0e51 100644 --- a/WebCore/rendering/RenderPath.h +++ b/WebCore/rendering/svg/RenderSVGPath.h @@ -21,8 +21,8 @@ Boston, MA 02110-1301, USA. */ -#ifndef RenderPath_h -#define RenderPath_h +#ifndef RenderSVGPath_h +#define RenderSVGPath_h #if ENABLE(SVG) #include "AffineTransform.h" @@ -36,9 +36,10 @@ class FloatPoint; class RenderSVGContainer; class SVGStyledTransformableElement; -class RenderPath : public RenderSVGModelObject { +class RenderSVGPath : public RenderSVGModelObject { public: - RenderPath(SVGStyledTransformableElement*); + explicit RenderSVGPath(SVGStyledTransformableElement*); + virtual ~RenderSVGPath(); const Path& path() const { return m_path; } void setNeedsPathUpdate() { m_needsPathUpdate = true; } @@ -55,8 +56,8 @@ private: virtual FloatRect repaintRectInLocalCoordinates() const { return m_repaintBoundingBox; } virtual const AffineTransform& localToParentTransform() const { return m_localTransform; } - virtual bool isRenderPath() const { return true; } - virtual const char* renderName() const { return "RenderPath"; } + virtual bool isSVGPath() const { return true; } + virtual const char* renderName() const { return "RenderSVGPath"; } virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); @@ -83,20 +84,20 @@ private: AffineTransform m_localTransform; }; -inline RenderPath* toRenderPath(RenderObject* object) +inline RenderSVGPath* toRenderSVGPath(RenderObject* object) { - ASSERT(!object || object->isRenderPath()); - return static_cast<RenderPath*>(object); + ASSERT(!object || object->isSVGPath()); + return static_cast<RenderSVGPath*>(object); } -inline const RenderPath* toRenderPath(const RenderObject* object) +inline const RenderSVGPath* toRenderSVGPath(const RenderObject* object) { - ASSERT(!object || object->isRenderPath()); - return static_cast<const RenderPath*>(object); + ASSERT(!object || object->isSVGPath()); + return static_cast<const RenderSVGPath*>(object); } // This will catch anyone doing an unnecessary cast. -void toRenderPath(const RenderPath*); +void toRenderSVGPath(const RenderSVGPath*); } diff --git a/WebCore/rendering/RenderSVGTSpan.cpp b/WebCore/rendering/svg/RenderSVGTSpan.cpp index 90ff36c..90ff36c 100644 --- a/WebCore/rendering/RenderSVGTSpan.cpp +++ b/WebCore/rendering/svg/RenderSVGTSpan.cpp diff --git a/WebCore/rendering/RenderSVGTSpan.h b/WebCore/rendering/svg/RenderSVGTSpan.h index 931bd8c..97e0eb0 100644 --- a/WebCore/rendering/RenderSVGTSpan.h +++ b/WebCore/rendering/svg/RenderSVGTSpan.h @@ -29,7 +29,7 @@ namespace WebCore { class RenderSVGTSpan : public RenderSVGInline { public: - RenderSVGTSpan(Node*); + explicit RenderSVGTSpan(Node*); virtual const char* renderName() const { return "RenderSVGTSpan"; } }; } diff --git a/WebCore/rendering/RenderSVGText.cpp b/WebCore/rendering/svg/RenderSVGText.cpp index 92091af..01a92b0 100644 --- a/WebCore/rendering/RenderSVGText.cpp +++ b/WebCore/rendering/svg/RenderSVGText.cpp @@ -1,12 +1,11 @@ /* - * This file is part of the WebKit project. - * * Copyright (C) 2006 Apple Computer, Inc. - * 2006 Alexander Kellett <lypanov@kde.org> - * 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> - * 2007 Nikolas Zimmermann <zimmermann@kde.org> - * 2008 Rob Buis <buis@kde.org> - * 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org> + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Rob Buis <buis@kde.org> + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) Research In Motion Limited 2010. 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 @@ -42,20 +41,42 @@ #include "SVGRenderSupport.h" #include "SVGRootInlineBox.h" #include "SVGTextElement.h" -#include "SVGTextLayoutBuilder.h" +#include "SVGTextLayoutAttributesBuilder.h" #include "SVGTransformList.h" #include "SVGURIReference.h" #include "SimpleFontData.h" #include "TransformState.h" +#include "VisiblePosition.h" namespace WebCore { RenderSVGText::RenderSVGText(SVGTextElement* node) : RenderSVGBlock(node) + , m_needsPositioningValuesUpdate(true) , m_needsTransformUpdate(true) { } +RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(RenderObject* start) +{ + ASSERT(start); + while (start && !start->isSVGText()) + start = start->parent(); + if (!start || !start->isSVGText()) + return 0; + return toRenderSVGText(start); +} + +const RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(const RenderObject* start) +{ + ASSERT(start); + while (start && !start->isSVGText()) + start = start->parent(); + if (!start || !start->isSVGText()) + return 0; + return toRenderSVGText(start); +} + IntRect RenderSVGText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer); @@ -84,8 +105,13 @@ void RenderSVGText::layout() updateCachedBoundariesInParents = true; } - SVGTextLayoutBuilder layoutBuilder; - layoutBuilder.buildLayoutAttributesForTextSubtree(this); + if (m_needsPositioningValuesUpdate) { + // Perform SVG text layout phase one (see SVGTextLayoutAttributesBuilder for details). + SVGTextLayoutAttributesBuilder layoutAttributesBuilder; + layoutAttributesBuilder.buildLayoutAttributesForTextSubtree(this); + m_needsPositioningValuesUpdate = false; + updateCachedBoundariesInParents = true; + } // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text. // All if branches that could cause early exit in RenderBlocks layoutBlock() method are turned into assertions. @@ -153,6 +179,23 @@ bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, return false; } +VisiblePosition RenderSVGText::positionForPoint(const IntPoint& pointInContents) +{ + RootInlineBox* rootBox = firstRootBox(); + if (!rootBox) + return createVisiblePosition(0, DOWNSTREAM); + + ASSERT(rootBox->isSVGRootInlineBox()); + ASSERT(!rootBox->nextRootBox()); + ASSERT(childrenInline()); + + InlineBox* closestBox = static_cast<SVGRootInlineBox*>(rootBox)->closestLeafChildForPosition(pointInContents); + if (!closestBox) + return createVisiblePosition(0, DOWNSTREAM); + + return closestBox->renderer()->positionForPoint(IntPoint(pointInContents.x(), closestBox->m_y)); +} + void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads) { quads.append(localToAbsoluteQuad(strokeBoundingBox())); diff --git a/WebCore/rendering/RenderSVGText.h b/WebCore/rendering/svg/RenderSVGText.h index be19419..deae78c 100644 --- a/WebCore/rendering/RenderSVGText.h +++ b/WebCore/rendering/svg/RenderSVGText.h @@ -1,8 +1,7 @@ /* - * This file is part of the WebKit project. - * * Copyright (C) 2006 Apple Computer, Inc. - * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) Research In Motion Limited 2010. 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 @@ -37,9 +36,13 @@ class RenderSVGText : public RenderSVGBlock { public: RenderSVGText(SVGTextElement* node); + void setNeedsPositioningValuesUpdate() { m_needsPositioningValuesUpdate = true; } virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } virtual FloatRect repaintRectInLocalCoordinates() const; + static RenderSVGText* locateRenderSVGTextAncestor(RenderObject*); + static const RenderSVGText* locateRenderSVGTextAncestor(const RenderObject*); + private: virtual const char* renderName() const { return "RenderSVGText"; } virtual bool isSVGText() const { return true; } @@ -47,6 +50,7 @@ private: virtual void paint(PaintInfo&, int tx, int ty); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); + virtual VisiblePosition positionForPoint(const IntPoint&); virtual bool requiresLayer() const { return false; } virtual void layout(); @@ -68,10 +72,26 @@ private: virtual RenderBlock* firstLineBlock() const; virtual void updateFirstLetter(); + bool m_needsPositioningValuesUpdate : 1; bool m_needsTransformUpdate : 1; AffineTransform m_localTransform; }; +inline RenderSVGText* toRenderSVGText(RenderObject* object) +{ + ASSERT(!object || object->isSVGText()); + return static_cast<RenderSVGText*>(object); +} + +inline const RenderSVGText* toRenderSVGText(const RenderObject* object) +{ + ASSERT(!object || object->isSVGText()); + return static_cast<const RenderSVGText*>(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderSVGText(const RenderSVGText*); + } #endif // ENABLE(SVG) diff --git a/WebCore/rendering/RenderSVGTextPath.cpp b/WebCore/rendering/svg/RenderSVGTextPath.cpp index 5d977f9..64ebc6d 100644 --- a/WebCore/rendering/RenderSVGTextPath.cpp +++ b/WebCore/rendering/svg/RenderSVGTextPath.cpp @@ -53,7 +53,8 @@ Path RenderSVGTextPath::layoutPath() const SVGPathElement* pathElement = static_cast<SVGPathElement*>(targetElement); - Path pathData = pathElement->toPathData(); + Path pathData; + pathElement->toPathData(pathData); // Spec: The transform attribute on the referenced 'path' element represents a // supplemental transformation relative to the current user coordinate system for // the current 'text' element, including any adjustments to the current user coordinate diff --git a/WebCore/rendering/RenderSVGTextPath.h b/WebCore/rendering/svg/RenderSVGTextPath.h index 8e6ff42..a71edf5 100644 --- a/WebCore/rendering/RenderSVGTextPath.h +++ b/WebCore/rendering/svg/RenderSVGTextPath.h @@ -29,34 +29,36 @@ namespace WebCore { - class RenderSVGTextPath : public RenderSVGInline { - public: - RenderSVGTextPath(Node*); +class RenderSVGTextPath : public RenderSVGInline { +public: + RenderSVGTextPath(Node*); - Path layoutPath() const; - float startOffset() const; - bool exactAlignment() const; - bool stretchMethod() const; + Path layoutPath() const; + float startOffset() const; + bool exactAlignment() const; + bool stretchMethod() const; - private: - virtual const char* renderName() const { return "RenderSVGTextPath"; } + virtual bool isSVGTextPath() const { return true; } - float m_startOffset; +private: + virtual const char* renderName() const { return "RenderSVGTextPath"; } - bool m_exactAlignment : 1; - bool m_stretchMethod : 1; + float m_startOffset; - Path m_layoutPath; - }; + bool m_exactAlignment : 1; + bool m_stretchMethod : 1; - inline RenderSVGTextPath* toRenderSVGTextPath(RenderObject* object) - { - ASSERT(!object || !strcmp(object->renderName(), "RenderSVGTextPath")); - return static_cast<RenderSVGTextPath*>(object); - } + Path m_layoutPath; +}; - // This will catch anyone doing an unnecessary cast. - void toRenderSVGTextPath(const RenderSVGTextPath*); +inline RenderSVGTextPath* toRenderSVGTextPath(RenderObject* object) +{ + ASSERT(!object || !strcmp(object->renderName(), "RenderSVGTextPath")); + return static_cast<RenderSVGTextPath*>(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderSVGTextPath(const RenderSVGTextPath*); } diff --git a/WebCore/rendering/svg/SVGInlineFlowBox.cpp b/WebCore/rendering/svg/SVGInlineFlowBox.cpp new file mode 100644 index 0000000..b35c7e2 --- /dev/null +++ b/WebCore/rendering/svg/SVGInlineFlowBox.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) Research In Motion Limited 2010. 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. + * + */ + +#include "config.h" +#include "SVGInlineFlowBox.h" + +#if ENABLE(SVG) +#include "GraphicsContext.h" +#include "RenderSVGInlineText.h" +#include "SVGInlineTextBox.h" +#include "SVGRenderSupport.h" + +using namespace std; + +namespace WebCore { + +void SVGInlineFlowBox::paintSelectionBackground(PaintInfo& paintInfo) +{ + ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); + ASSERT(!paintInfo.context->paintingDisabled()); + + PaintInfo childPaintInfo(paintInfo); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + if (child->isSVGInlineTextBox()) + static_cast<SVGInlineTextBox*>(child)->paintSelectionBackground(childPaintInfo); + else if (child->isSVGInlineFlowBox()) + static_cast<SVGInlineFlowBox*>(child)->paintSelectionBackground(childPaintInfo); + } +} + +void SVGInlineFlowBox::paint(PaintInfo& paintInfo, int, int) +{ + ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); + ASSERT(!paintInfo.context->paintingDisabled()); + + RenderObject* boxRenderer = renderer(); + ASSERT(boxRenderer); + + PaintInfo childPaintInfo(paintInfo); + childPaintInfo.context->save(); + + if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) { + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + if (child->isSVGInlineTextBox()) + computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast<SVGInlineTextBox*>(child)->textRenderer())); + + child->paint(childPaintInfo, 0, 0); + } + } + + SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context); + childPaintInfo.context->restore(); +} + +IntRect SVGInlineFlowBox::calculateBoundaries() const +{ + IntRect childRect; + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + childRect.unite(child->calculateBoundaries()); + return childRect; +} + +void SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(RenderSVGInlineText* textRenderer) +{ + ASSERT(textRenderer); + + Node* node = textRenderer->node(); + if (!node || !node->inDocument()) + return; + + RenderStyle* style = textRenderer->style(); + ASSERT(style); + + Document* document = textRenderer->document(); + Vector<DocumentMarker> markers = document->markers()->markersForNode(textRenderer->node()); + + Vector<DocumentMarker>::iterator markerEnd = markers.end(); + for (Vector<DocumentMarker>::iterator markerIt = markers.begin(); markerIt != markerEnd; ++markerIt) { + const DocumentMarker& marker = *markerIt; + + // SVG is only interessted in the TextMatch marker, for now. + if (marker.type != DocumentMarker::TextMatch) + continue; + + FloatRect markerRect; + for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + ASSERT(box->isSVGInlineTextBox()); + SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(box); + + int markerStartPosition = max<int>(marker.startOffset - textBox->start(), 0); + int markerEndPosition = min<int>(marker.endOffset - textBox->start(), textBox->len()); + + if (markerStartPosition >= markerEndPosition) + continue; + + int fragmentStartPosition = 0; + int fragmentEndPosition = 0; + + const Vector<SVGTextFragment>& fragments = textBox->textFragments(); + unsigned textFragmentsSize = fragments.size(); + for (unsigned i = 0; i < textFragmentsSize; ++i) { + const SVGTextFragment& fragment = fragments.at(i); + + fragmentStartPosition = markerStartPosition; + fragmentEndPosition = markerEndPosition; + if (!textBox->mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition)) + continue; + + FloatRect fragmentRect = textBox->selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style); + if (!fragment.transform.isIdentity()) + fragmentRect = fragment.transform.mapRect(fragmentRect); + + markerRect.unite(fragmentRect); + } + } + + document->markers()->setRenderedRectForMarker(node, marker, textRenderer->localToAbsoluteQuad(markerRect).enclosingBoundingBox()); + } +} + +} // namespace WebCore + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGInlineFlowBox.h b/WebCore/rendering/svg/SVGInlineFlowBox.h index 80600f7..2358f2d 100644 --- a/WebCore/rendering/SVGInlineFlowBox.h +++ b/WebCore/rendering/svg/SVGInlineFlowBox.h @@ -29,6 +29,8 @@ namespace WebCore { +class RenderSVGInlineText; + class SVGInlineFlowBox : public InlineFlowBox { public: SVGInlineFlowBox(RenderObject* obj) @@ -37,13 +39,16 @@ public: { } + virtual bool isSVGInlineFlowBox() const { return true; } virtual int virtualLogicalHeight() const { return m_logicalHeight; } void setLogicalHeight(int h) { m_logicalHeight = h; } + void paintSelectionBackground(PaintInfo&); virtual void paint(PaintInfo&, int tx, int ty); virtual IntRect calculateBoundaries() const; - void layoutFlowBox(); + + static void computeTextMatchMarkerRectForRenderer(RenderSVGInlineText*); private: int m_logicalHeight; diff --git a/WebCore/rendering/svg/SVGInlineTextBox.cpp b/WebCore/rendering/svg/SVGInlineTextBox.cpp new file mode 100644 index 0000000..0458af5 --- /dev/null +++ b/WebCore/rendering/svg/SVGInlineTextBox.cpp @@ -0,0 +1,610 @@ +/** + * Copyright (C) 2007 Rob Buis <buis@kde.org> + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) Research In Motion Limited 2010. 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. + * + */ + +#include "config.h" +#include "SVGInlineTextBox.h" + +#if ENABLE(SVG) +#include "FloatConversion.h" +#include "GraphicsContext.h" +#include "InlineFlowBox.h" +#include "RenderBlock.h" +#include "RenderSVGInlineText.h" +#include "RenderSVGResource.h" +#include "RenderSVGResourceSolidColor.h" +#include "SVGRootInlineBox.h" + +using namespace std; + +namespace WebCore { + +SVGInlineTextBox::SVGInlineTextBox(RenderObject* object) + : InlineTextBox(object) + , m_logicalHeight(0) + , m_paintingResourceMode(ApplyToDefaultMode) + , m_startsNewTextChunk(false) + , m_paintingResource(0) +{ +} + +int SVGInlineTextBox::offsetForPosition(int, bool) const +{ + // SVG doesn't use the standard offset <-> position selection system, as it's not suitable for SVGs complex needs. + // vertical text selection, inline boxes spanning multiple lines (contrary to HTML, etc.) + ASSERT_NOT_REACHED(); + return 0; +} + +int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const +{ + RenderText* textRenderer = this->textRenderer(); + ASSERT(textRenderer); + + RenderStyle* style = textRenderer->style(); + ASSERT(style); + + TextRun textRun(constructTextRun(style, fragment)); + + // Eventually handle lengthAdjust="spacingAndGlyphs". + // FIXME: Handle vertical text. + if (!fragment.transform.isIdentity()) + textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragment.transform.xScale())); + + return fragment.positionListOffset - start() + style->font().offsetForPosition(textRun, position, includePartialGlyphs); +} + +int SVGInlineTextBox::positionForOffset(int) const +{ + // SVG doesn't use the offset <-> position selection system. + ASSERT_NOT_REACHED(); + return 0; +} + +FloatRect SVGInlineTextBox::selectionRectForTextFragment(const SVGTextFragment& fragment, int startPosition, int endPosition, RenderStyle* style) +{ + ASSERT(startPosition < endPosition); + + const Font& font = style->font(); + FloatPoint textOrigin(fragment.x, fragment.y - font.ascent()); + return font.selectionRectForText(constructTextRun(style, fragment), textOrigin, fragment.height, startPosition, endPosition); +} + +IntRect SVGInlineTextBox::selectionRect(int, int, int startPosition, int endPosition) +{ + int boxStart = start(); + startPosition = max(startPosition - boxStart, 0); + endPosition = min(endPosition - boxStart, static_cast<int>(len())); + if (startPosition >= endPosition) + return IntRect(); + + RenderText* text = textRenderer(); + ASSERT(text); + + RenderStyle* style = text->style(); + ASSERT(style); + + FloatRect selectionRect; + int fragmentStartPosition = 0; + int fragmentEndPosition = 0; + + unsigned textFragmentsSize = m_textFragments.size(); + for (unsigned i = 0; i < textFragmentsSize; ++i) { + const SVGTextFragment& fragment = m_textFragments.at(i); + + fragmentStartPosition = startPosition; + fragmentEndPosition = endPosition; + if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition)) + continue; + + FloatRect fragmentRect = selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style); + if (!fragment.transform.isIdentity()) + fragmentRect = fragment.transform.mapRect(fragmentRect); + + selectionRect.unite(fragmentRect); + } + + return enclosingIntRect(selectionRect); +} + +void SVGInlineTextBox::paintSelectionBackground(PaintInfo& paintInfo) +{ + ASSERT(paintInfo.shouldPaintWithinRoot(renderer())); + ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); + ASSERT(truncation() == cNoTruncation); + + if (renderer()->style()->visibility() != VISIBLE) + return; + + RenderObject* parentRenderer = parent()->renderer(); + ASSERT(parentRenderer); + ASSERT(!parentRenderer->document()->printing()); + + // Determine whether or not we're selected. + bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; + bool hasSelection = selectionState() != RenderObject::SelectionNone; + if (!hasSelection || paintSelectedTextOnly) + return; + + Color backgroundColor = renderer()->selectionBackgroundColor(); + if (!backgroundColor.isValid() || !backgroundColor.alpha()) + return; + + RenderStyle* style = parentRenderer->style(); + ASSERT(style); + + const SVGRenderStyle* svgStyle = style->svgStyle(); + ASSERT(svgStyle); + + bool hasFill = svgStyle->hasFill(); + bool hasStroke = svgStyle->hasStroke(); + + RenderStyle* selectionStyle = style; + if (hasSelection) { + selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION); + if (selectionStyle) { + const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle(); + ASSERT(svgSelectionStyle); + + if (!hasFill) + hasFill = svgSelectionStyle->hasFill(); + if (!hasStroke) + hasStroke = svgSelectionStyle->hasStroke(); + } else + selectionStyle = style; + } + + int startPosition, endPosition; + selectionStartEnd(startPosition, endPosition); + + int fragmentStartPosition = 0; + int fragmentEndPosition = 0; + unsigned textFragmentsSize = m_textFragments.size(); + for (unsigned i = 0; i < textFragmentsSize; ++i) { + SVGTextFragment& fragment = m_textFragments.at(i); + ASSERT(!m_paintingResource); + + fragmentStartPosition = startPosition; + fragmentEndPosition = endPosition; + if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition)) + continue; + + paintInfo.context->save(); + + if (!fragment.transform.isIdentity()) + paintInfo.context->concatCTM(fragment.transform); + + paintInfo.context->setFillColor(backgroundColor, style->colorSpace()); + paintInfo.context->fillRect(selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style), backgroundColor, style->colorSpace()); + + m_paintingResourceMode = ApplyToDefaultMode; + paintInfo.context->restore(); + } + + ASSERT(!m_paintingResource); +} + +void SVGInlineTextBox::paint(PaintInfo& paintInfo, int, int) +{ + ASSERT(paintInfo.shouldPaintWithinRoot(renderer())); + ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); + ASSERT(truncation() == cNoTruncation); + + if (renderer()->style()->visibility() != VISIBLE) + return; + + // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox. + // If we ever need that for SVG, it's very easy to refactor and reuse the code. + + RenderObject* parentRenderer = parent()->renderer(); + ASSERT(parentRenderer); + + bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; + bool hasSelection = !parentRenderer->document()->printing() && selectionState() != RenderObject::SelectionNone; + if (!hasSelection && paintSelectedTextOnly) + return; + + RenderStyle* style = parentRenderer->style(); + ASSERT(style); + + const SVGRenderStyle* svgStyle = style->svgStyle(); + ASSERT(svgStyle); + + bool hasFill = svgStyle->hasFill(); + bool hasStroke = svgStyle->hasStroke(); + + RenderStyle* selectionStyle = style; + if (hasSelection) { + selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION); + if (selectionStyle) { + const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle(); + ASSERT(svgSelectionStyle); + + if (!hasFill) + hasFill = svgSelectionStyle->hasFill(); + if (!hasStroke) + hasStroke = svgSelectionStyle->hasStroke(); + } else + selectionStyle = style; + } + + unsigned textFragmentsSize = m_textFragments.size(); + for (unsigned i = 0; i < textFragmentsSize; ++i) { + SVGTextFragment& fragment = m_textFragments.at(i); + ASSERT(!m_paintingResource); + + paintInfo.context->save(); + + if (!fragment.transform.isIdentity()) + paintInfo.context->concatCTM(fragment.transform); + + // 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); + + // Fill text + if (hasFill) { + m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode; + paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly); + } + + // Stroke text + if (hasStroke) { + m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode; + paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly); + } + + // 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); + + m_paintingResourceMode = ApplyToDefaultMode; + paintInfo.context->restore(); + } + + ASSERT(!m_paintingResource); +} + +bool SVGInlineTextBox::acquirePaintingResource(GraphicsContext*& context, RenderObject* renderer, RenderStyle* style) +{ + ASSERT(renderer); + ASSERT(style); + ASSERT(m_paintingResourceMode != ApplyToDefaultMode); + + Color fallbackColor; + if (m_paintingResourceMode & ApplyToFillMode) + m_paintingResource = RenderSVGResource::fillPaintingResource(renderer, style, fallbackColor); + else if (m_paintingResourceMode & ApplyToStrokeMode) + m_paintingResource = RenderSVGResource::strokePaintingResource(renderer, style, fallbackColor); + else { + // We're either called for stroking or filling. + ASSERT_NOT_REACHED(); + } + + if (!m_paintingResource) + return false; + + if (!m_paintingResource->applyResource(renderer, style, context, m_paintingResourceMode)) { + if (fallbackColor.isValid()) { + RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource(); + fallbackResource->setColor(fallbackColor); + + m_paintingResource = fallbackResource; + m_paintingResource->applyResource(renderer, style, context, m_paintingResourceMode); + } + } + + return true; +} + +void SVGInlineTextBox::releasePaintingResource(GraphicsContext*& context) +{ + ASSERT(m_paintingResource); + + RenderObject* parentRenderer = parent()->renderer(); + ASSERT(parentRenderer); + + m_paintingResource->postApplyResource(parentRenderer, context, m_paintingResourceMode); + m_paintingResource = 0; +} + +bool SVGInlineTextBox::prepareGraphicsContextForTextPainting(GraphicsContext*& context, TextRun& textRun, RenderStyle* style) +{ + bool acquiredResource = acquirePaintingResource(context, parent()->renderer(), style); + +#if ENABLE(SVG_FONTS) + // SVG Fonts need access to the painting resource used to draw the current text chunk. + if (acquiredResource) + textRun.setActivePaintingResource(m_paintingResource); +#endif + + return acquiredResource; +} + +void SVGInlineTextBox::restoreGraphicsContextAfterTextPainting(GraphicsContext*& context, TextRun& textRun) +{ + releasePaintingResource(context); + +#if ENABLE(SVG_FONTS) + textRun.setActivePaintingResource(0); +#endif +} + +TextRun SVGInlineTextBox::constructTextRun(RenderStyle* style, const SVGTextFragment& fragment) const +{ + ASSERT(style); + ASSERT(textRenderer()); + + RenderText* text = textRenderer(); + ASSERT(text); + + TextRun run(text->characters() + fragment.positionListOffset + , fragment.length + , false /* allowTabs */ + , 0 /* xPos, only relevant with allowTabs=true */ + , 0 /* padding, only relevant for justified text, not relevant for SVG */ + , direction() == RTL + , m_dirOverride || style->visuallyOrdered() /* directionalOverride */); + +#if ENABLE(SVG_FONTS) + RenderObject* parentRenderer = parent()->renderer(); + ASSERT(parentRenderer); + + run.setReferencingRenderObject(parentRenderer); +#endif + + // Disable any word/character rounding. + run.disableRoundingHacks(); + + // We handle letter & word spacing ourselves. + run.disableSpacing(); + return run; +} + +bool SVGInlineTextBox::mapStartEndPositionsIntoFragmentCoordinates(const SVGTextFragment& fragment, int& startPosition, int& endPosition) const +{ + if (startPosition >= endPosition) + return false; + + int offset = static_cast<int>(fragment.positionListOffset) - start(); + int length = static_cast<int>(fragment.length); + + if (startPosition >= offset + length || endPosition <= offset) + return false; + + if (startPosition < offset) + startPosition = 0; + else + startPosition -= offset; + + if (endPosition > offset + length) + endPosition = length; + else { + ASSERT(endPosition >= offset); + endPosition -= offset; + } + + ASSERT(startPosition < endPosition); + return true; +} + +static inline float positionOffsetForDecoration(ETextDecoration decoration, const Font& font, 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) + return font.ascent() + thickness * 1.5f; + if (decoration == OVERLINE) + return thickness; + if (decoration == LINE_THROUGH) + return font.ascent() * 5.0f / 8.0f; + + ASSERT_NOT_REACHED(); + return 0.0f; +} + +static inline float thicknessForDecoration(ETextDecoration, const Font& font) +{ + // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified. + // Compatible with Batik/Opera + return font.size() / 20.0f; +} + +static inline RenderObject* findRenderObjectDefininingTextDecoration(InlineFlowBox* parentBox) +{ + // Lookup first render object in parent hierarchy which has text-decoration set. + RenderObject* renderer = 0; + while (parentBox) { + renderer = parentBox->renderer(); + + if (renderer->style() && renderer->style()->textDecoration() != TDNONE) + break; + + parentBox = parentBox->parent(); + } + + ASSERT(renderer); + return renderer; +} + +void SVGInlineTextBox::paintDecoration(GraphicsContext* context, ETextDecoration decoration, const SVGTextFragment& fragment) +{ + if (textRenderer()->style()->textDecorationsInEffect() == TDNONE) + return; + + // Find out which render style defined the text-decoration, as its fill/stroke properties have to be used for drawing instead of ours. + RenderObject* decorationRenderer = findRenderObjectDefininingTextDecoration(parent()); + RenderStyle* decorationStyle = decorationRenderer->style(); + ASSERT(decorationStyle); + + if (decorationStyle->visibility() == HIDDEN) + return; + + const SVGRenderStyle* svgDecorationStyle = decorationStyle->svgStyle(); + ASSERT(svgDecorationStyle); + + bool hasDecorationFill = svgDecorationStyle->hasFill(); + bool hasDecorationStroke = svgDecorationStyle->hasStroke(); + + if (hasDecorationFill) { + m_paintingResourceMode = ApplyToFillMode; + paintDecorationWithStyle(context, decoration, fragment, decorationRenderer); + } + + if (hasDecorationStroke) { + m_paintingResourceMode = ApplyToStrokeMode; + paintDecorationWithStyle(context, decoration, fragment, decorationRenderer); + } +} + +void SVGInlineTextBox::paintDecorationWithStyle(GraphicsContext* context, ETextDecoration decoration, const SVGTextFragment& fragment, RenderObject* decorationRenderer) +{ + ASSERT(!m_paintingResource); + ASSERT(m_paintingResourceMode != ApplyToDefaultMode); + + RenderStyle* decorationStyle = decorationRenderer->style(); + ASSERT(decorationStyle); + + const Font& font = decorationStyle->font(); + + // The initial y value refers to overline position. + float thickness = thicknessForDecoration(decoration, font); + + if (fragment.width <= 0 && thickness <= 0) + return; + + float y = fragment.y - font.ascent() + positionOffsetForDecoration(decoration, font, thickness); + + Path path; + path.addRect(FloatRect(fragment.x, y, fragment.width, thickness)); + + context->save(); + context->beginPath(); + context->addPath(path); + + if (acquirePaintingResource(context, decorationRenderer, decorationStyle)) + releasePaintingResource(context); + + context->restore(); +} + +void SVGInlineTextBox::paintTextWithShadows(GraphicsContext* context, RenderStyle* style, TextRun& textRun, const SVGTextFragment& fragment, int startPosition, int endPosition) +{ + const Font& font = style->font(); + const ShadowData* shadow = style->textShadow(); + + FloatPoint textOrigin(fragment.x, fragment.y); + FloatRect shadowRect(FloatPoint(textOrigin.x(), textOrigin.y() - font.ascent()), FloatSize(fragment.width, fragment.height)); + + do { + if (!prepareGraphicsContextForTextPainting(context, textRun, style)) + break; + + FloatSize extraOffset; + if (shadow) + extraOffset = applyShadowToGraphicsContext(context, shadow, shadowRect, false /* stroked */, true /* opaque */); + + font.drawText(context, textRun, textOrigin + extraOffset, startPosition, endPosition); + restoreGraphicsContextAfterTextPainting(context, textRun); + + if (!shadow) + break; + + if (shadow->next()) + context->restore(); + else + context->clearShadow(); + + shadow = shadow->next(); + } while (shadow); +} + +void SVGInlineTextBox::paintText(GraphicsContext* context, RenderStyle* style, RenderStyle* selectionStyle, const SVGTextFragment& fragment, bool hasSelection, bool paintSelectedTextOnly) +{ + ASSERT(style); + ASSERT(selectionStyle); + + int startPosition = 0; + int endPosition = 0; + if (hasSelection) { + selectionStartEnd(startPosition, endPosition); + hasSelection = mapStartEndPositionsIntoFragmentCoordinates(fragment, startPosition, endPosition); + } + + // Fast path if there is no selection, just draw the whole chunk part using the regular style + TextRun textRun(constructTextRun(style, fragment)); + if (!hasSelection || startPosition >= endPosition) { + paintTextWithShadows(context, style, textRun, fragment, 0, fragment.length); + return; + } + + // Eventually draw text using regular style until the start position of the selection + if (startPosition > 0 && !paintSelectedTextOnly) + paintTextWithShadows(context, style, textRun, fragment, 0, startPosition); + + // Draw text using selection style from the start to the end position of the selection + if (style != selectionStyle) + SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, selectionStyle); + + TextRun selectionTextRun(constructTextRun(selectionStyle, fragment)); + paintTextWithShadows(context, selectionStyle, textRun, fragment, startPosition, endPosition); + + if (style != selectionStyle) + SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, style); + + // Eventually draw text using regular style from the end position of the selection to the end of the current chunk part + if (endPosition < static_cast<int>(fragment.length) && !paintSelectedTextOnly) + paintTextWithShadows(context, style, textRun, fragment, endPosition, fragment.length); +} + +IntRect SVGInlineTextBox::calculateBoundaries() const +{ + FloatRect textRect; + + RenderText* textRenderer = this->textRenderer(); + ASSERT(textRenderer); + + RenderStyle* style = textRenderer->style(); + ASSERT(style); + + int baseline = baselinePosition(); + int heightDifference = baseline - style->font().ascent(); + + unsigned textFragmentsSize = m_textFragments.size(); + for (unsigned i = 0; i < textFragmentsSize; ++i) { + const SVGTextFragment& fragment = m_textFragments.at(i); + FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height + heightDifference); + + if (!fragment.transform.isIdentity()) + fragmentRect = fragment.transform.mapRect(fragmentRect); + + textRect.unite(fragmentRect); + } + + return enclosingIntRect(textRect); +} + +} // namespace WebCore + +#endif diff --git a/WebCore/rendering/SVGInlineTextBox.h b/WebCore/rendering/svg/SVGInlineTextBox.h index 24957cf..8e82dda 100644 --- a/WebCore/rendering/SVGInlineTextBox.h +++ b/WebCore/rendering/svg/SVGInlineTextBox.h @@ -25,17 +25,13 @@ #if ENABLE(SVG) #include "InlineTextBox.h" -#include "SVGTextChunkLayoutInfo.h" -#include "SVGTextLayoutUtilities.h" +#include "SVGTextLayoutEngine.h" namespace WebCore { class RenderSVGResource; class SVGRootInlineBox; -struct SVGCharacterLayoutInfo; -struct SVGLastGlyphInfo; - class SVGInlineTextBox : public InlineTextBox { public: SVGInlineTextBox(RenderObject*); @@ -50,30 +46,26 @@ public: virtual int offsetForPosition(int x, bool includePartialGlyphs = true) const; virtual int positionForOffset(int offset) const; + void paintSelectionBackground(PaintInfo&); virtual void paint(PaintInfo&, int tx, int ty); - virtual IntRect selectionRect(int absx, int absy, int startPos, int endPos); - - virtual void selectionStartEnd(int& startPos, int& endPos); - void mapStartEndPositionsIntoChunkPartCoordinates(int& startPos, int& endPos, const SVGTextChunkPart&) const; + virtual IntRect selectionRect(int absx, int absy, int startPosition, int endPosition); - SVGRootInlineBox* svgRootInlineBox() const; + bool mapStartEndPositionsIntoFragmentCoordinates(const SVGTextFragment&, int& startPosition, int& endPosition) const; - // Helper functions shared with SVGRootInlineBox - void measureCharacter(RenderStyle*, int position, int& charsConsumed, String& glyphName, String& unicodeString, float& glyphWidth, float& glyphHeight) const; - FloatRect calculateGlyphBoundaries(RenderStyle*, int position, const SVGChar&) const; + virtual IntRect calculateBoundaries() const; - void buildLayoutInformation(SVGCharacterLayoutInfo&, SVGLastGlyphInfo&); + void clearTextFragments() { m_textFragments.clear(); } + Vector<SVGTextFragment>& textFragments() { return m_textFragments; } + const Vector<SVGTextFragment>& textFragments() const { return m_textFragments; } - const AffineTransform& chunkTransformation() const { return m_chunkTransformation; } - void setChunkTransformation(const AffineTransform& transform) { m_chunkTransformation = transform; } - void addChunkPartInformation(const SVGTextChunkPart& part) { m_svgTextChunkParts.append(part); } - const Vector<SVGTextChunkPart>& svgTextChunkParts() const { return m_svgTextChunkParts; } + bool startsNewTextChunk() const { return m_startsNewTextChunk; } + void setStartsNewTextChunk(bool newTextChunk) { m_startsNewTextChunk = newTextChunk; } - virtual IntRect calculateBoundaries() const; + int offsetForPositionInFragment(const SVGTextFragment&, float position, bool includePartialGlyphs) const; + FloatRect selectionRectForTextFragment(const SVGTextFragment&, int fragmentStartPosition, int fragmentEndPosition, RenderStyle*); private: - TextRun constructTextRun(RenderStyle*) const; - AffineTransform buildChunkTransformation(SVGChar& firstCharacter) const; + TextRun constructTextRun(RenderStyle*, const SVGTextFragment&) const; bool acquirePaintingResource(GraphicsContext*&, RenderObject*, RenderStyle*); void releasePaintingResource(GraphicsContext*&); @@ -81,22 +73,17 @@ private: bool prepareGraphicsContextForTextPainting(GraphicsContext*&, TextRun&, RenderStyle*); void restoreGraphicsContextAfterTextPainting(GraphicsContext*&, TextRun&); - void computeTextMatchMarkerRect(RenderStyle*); - void paintDecoration(GraphicsContext*, const FloatPoint& textOrigin, ETextDecoration, bool hasSelection); - void paintDecorationWithStyle(GraphicsContext*, const FloatPoint& textOrigin, RenderObject*, ETextDecoration); - void paintSelection(GraphicsContext*, const FloatPoint& textOrigin, RenderStyle*); - void paintText(GraphicsContext*, const FloatPoint& textOrigin, RenderStyle*, RenderStyle* selectionStyle, bool hasSelection, bool paintSelectedTextOnly); - void paintTextWithShadows(GraphicsContext*, const FloatPoint& textOrigin, RenderStyle*, TextRun&, int startPos, int endPos); - - FloatRect selectionRectForTextChunkPart(const SVGTextChunkPart&, int partStartPos, int partEndPos, RenderStyle*); + void paintDecoration(GraphicsContext*, ETextDecoration, const SVGTextFragment&); + void paintDecorationWithStyle(GraphicsContext*, ETextDecoration, 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); private: int m_logicalHeight; - AffineTransform m_chunkTransformation; - Vector<SVGTextChunkPart> m_svgTextChunkParts; - mutable SVGTextChunkPart m_currentChunkPart; - RenderSVGResource* m_paintingResource; int m_paintingResourceMode; + bool m_startsNewTextChunk : 1; + RenderSVGResource* m_paintingResource; + Vector<SVGTextFragment> m_textFragments; }; } // namespace WebCore diff --git a/WebCore/rendering/svg/SVGRootInlineBox.cpp b/WebCore/rendering/svg/SVGRootInlineBox.cpp new file mode 100644 index 0000000..7109e1f --- /dev/null +++ b/WebCore/rendering/svg/SVGRootInlineBox.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * Copyright (C) 2006 Apple Computer Inc. + * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) Research In Motion Limited 2010. 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. + * + */ + +#include "config.h" +#include "SVGRootInlineBox.h" + +#if ENABLE(SVG) +#include "GraphicsContext.h" +#include "RenderBlock.h" +#include "RenderSVGInlineText.h" +#include "SVGInlineFlowBox.h" +#include "SVGInlineTextBox.h" +#include "SVGRenderSupport.h" +#include "SVGTextPositioningElement.h" + +namespace WebCore { + +void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int) +{ + ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); + ASSERT(!paintInfo.context->paintingDisabled()); + + RenderObject* boxRenderer = renderer(); + ASSERT(boxRenderer); + + bool isPrinting = renderer()->document()->printing(); + bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone; + + PaintInfo childPaintInfo(paintInfo); + if (hasSelection) { + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + if (child->isSVGInlineTextBox()) + static_cast<SVGInlineTextBox*>(child)->paintSelectionBackground(childPaintInfo); + else if (child->isSVGInlineFlowBox()) + static_cast<SVGInlineFlowBox*>(child)->paintSelectionBackground(childPaintInfo); + } + } + + childPaintInfo.context->save(); + + if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) { + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + if (child->isSVGInlineTextBox()) + SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast<SVGInlineTextBox*>(child)->textRenderer())); + + child->paint(childPaintInfo, 0, 0); + } + } + + SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context); + childPaintInfo.context->restore(); +} + +void SVGRootInlineBox::computePerCharacterLayoutInformation() +{ + // Perform SVG text layout phase two (see SVGTextLayoutEngine for details). + SVGTextLayoutEngine characterLayout; + layoutCharactersInTextBoxes(this, characterLayout); + + // Perform SVG text layout phase three (see SVGTextChunkBuilder for details). + characterLayout.finishLayout(); + + // Perform SVG text layout phase four + // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block. + layoutChildBoxes(this); + layoutRootBox(); +} + +void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGTextLayoutEngine& characterLayout) +{ + for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { + if (child->isSVGInlineTextBox()) { + ASSERT(child->renderer()); + ASSERT(child->renderer()->isSVGInlineText()); + + SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child); + characterLayout.layoutInlineTextBox(textBox); + } else { + ASSERT(child->isInlineFlowBox()); + + // Skip generated content. + Node* node = child->renderer()->node(); + if (!node) + continue; + + SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child); + bool isTextPath = node->hasTagName(SVGNames::textPathTag); + if (isTextPath) { + // Build text chunks for all <textPath> children, using the line layout algorithm. + // This is needeed as text-anchor is just an additional startOffset for text paths. + SVGTextLayoutEngine lineLayout; + layoutCharactersInTextBoxes(flowBox, lineLayout); + characterLayout.beginTextPathLayout(child->renderer(), lineLayout); + } + + layoutCharactersInTextBoxes(flowBox, characterLayout); + + if (isTextPath) + characterLayout.endTextPathLayout(); + } + } +} + +void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start) +{ + for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { + if (child->isSVGInlineTextBox()) { + ASSERT(child->renderer()); + ASSERT(child->renderer()->isSVGInlineText()); + + SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child); + IntRect boxRect = textBox->calculateBoundaries(); + textBox->setX(boxRect.x()); + textBox->setY(boxRect.y()); + textBox->setLogicalWidth(boxRect.width()); + textBox->setLogicalHeight(boxRect.height()); + } else { + ASSERT(child->isInlineFlowBox()); + + // Skip generated content. + if (!child->renderer()->node()) + continue; + + SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child); + layoutChildBoxes(flowBox); + + IntRect boxRect = flowBox->calculateBoundaries(); + flowBox->setX(boxRect.x()); + flowBox->setY(boxRect.y()); + flowBox->setLogicalWidth(boxRect.width()); + flowBox->setLogicalHeight(boxRect.height()); + } + } +} + +void SVGRootInlineBox::layoutRootBox() +{ + RenderBlock* parentBlock = block(); + ASSERT(parentBlock); + + IntRect childRect; + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + // Skip generated content. + if (!child->renderer()->node()) + continue; + childRect.unite(child->calculateBoundaries()); + } + + int xBlock = childRect.x(); + int yBlock = childRect.y(); + int widthBlock = childRect.width(); + int heightBlock = childRect.height(); + + // Finally, assign the root block position, now that all content is laid out. + parentBlock->setLocation(xBlock, yBlock); + parentBlock->setWidth(widthBlock); + parentBlock->setHeight(heightBlock); + + // Position all children relative to the parent block. + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + // Skip generated content. + if (!child->renderer()->node()) + continue; + child->adjustPosition(-xBlock, -yBlock); + } + + // Position ourselves. + setX(0); + setY(0); + setLogicalWidth(widthBlock); + setLogicalHeight(heightBlock); + setBlockLogicalHeight(heightBlock); + setLineTopBottomPositions(0, heightBlock); +} + +InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const IntPoint& point) +{ + InlineBox* firstLeaf = firstLeafChild(); + InlineBox* lastLeaf = lastLeafChild(); + if (firstLeaf == lastLeaf) + return firstLeaf; + + // FIXME: Check for vertical text! + InlineBox* closestLeaf = 0; + for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) { + if (point.y() < leaf->m_y) + continue; + if (point.y() > leaf->m_y + leaf->virtualLogicalHeight()) + continue; + + closestLeaf = leaf; + if (point.x() < leaf->m_x + leaf->m_logicalWidth) + return leaf; + } + + return closestLeaf ? closestLeaf : lastLeaf; +} + +} // namespace WebCore + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGRootInlineBox.h b/WebCore/rendering/svg/SVGRootInlineBox.h index 77e7fcb..418c289 100644 --- a/WebCore/rendering/SVGRootInlineBox.h +++ b/WebCore/rendering/svg/SVGRootInlineBox.h @@ -26,10 +26,8 @@ #if ENABLE(SVG) #include "RootInlineBox.h" -#include "SVGCharacterData.h" -#include "SVGCharacterLayoutInfo.h" #include "SVGRenderSupport.h" -#include "SVGTextChunkLayoutInfo.h" +#include "SVGTextLayoutEngine.h" namespace WebCore { @@ -55,20 +53,15 @@ public: virtual FloatRect objectBoundingBox() const { return FloatRect(); } virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); } - // Used by SVGRenderTreeAsText - const Vector<SVGTextChunk>& svgTextChunks() const { return m_svgTextChunks; } + InlineBox* closestLeafChildForPosition(const IntPoint&); private: - void propagateTextChunkPartInformation(); - void buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo&); - + void layoutCharactersInTextBoxes(InlineFlowBox*, SVGTextLayoutEngine&); + void layoutChildBoxes(InlineFlowBox*); void layoutRootBox(); - void layoutChildBoxes(InlineFlowBox* start); private: int m_logicalHeight; - Vector<SVGChar> m_svgChars; - Vector<SVGTextChunk> m_svgTextChunks; }; } // namespace WebCore diff --git a/WebCore/rendering/svg/SVGTextChunk.cpp b/WebCore/rendering/svg/SVGTextChunk.cpp new file mode 100644 index 0000000..5dea6ad --- /dev/null +++ b/WebCore/rendering/svg/SVGTextChunk.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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. + */ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGTextChunk.h" + +#include "SVGInlineTextBox.h" +#include "SVGTextFragment.h" + +namespace WebCore { + +SVGTextChunk::SVGTextChunk(bool isVerticalText, ETextAnchor textAnchor, SVGTextContentElement::SVGLengthAdjustType lengthAdjust, float desiredTextLength) + : m_isVerticalText(isVerticalText) + , m_textAnchor(textAnchor) + , m_lengthAdjust(lengthAdjust) + , m_desiredTextLength(desiredTextLength) +{ +} + +void SVGTextChunk::calculateLength(float& length, unsigned& characters) const +{ + SVGTextFragment* lastFragment = 0; + + unsigned boxCount = m_boxes.size(); + for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { + SVGInlineTextBox* textBox = m_boxes.at(boxPosition); + Vector<SVGTextFragment>& fragments = textBox->textFragments(); + + unsigned size = fragments.size(); + if (!size) + continue; + + for (unsigned i = 0; i < size; ++i) { + SVGTextFragment& fragment = fragments.at(i); + characters += fragment.length; + + if (m_isVerticalText) + length += fragment.height; + else + length += fragment.width; + + if (!lastFragment) { + lastFragment = &fragment; + continue; + } + + // Resepect gap between chunks. + if (m_isVerticalText) + length += fragment.y - (lastFragment->y + lastFragment->height); + else + length += fragment.x - (lastFragment->x + lastFragment->width); + + lastFragment = &fragment; + } + } +} + +float SVGTextChunk::calculateTextAnchorShift(float length) const +{ + switch (m_textAnchor) { + case TA_START: + return 0; + case TA_MIDDLE: + return -length / 2; + case TA_END: + return -length; + }; + + ASSERT_NOT_REACHED(); + return 0; +} + +} // namespace WebCore + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/svg/SVGTextChunk.h b/WebCore/rendering/svg/SVGTextChunk.h new file mode 100644 index 0000000..ebe6d81 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextChunk.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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 SVGTextChunk_h +#define SVGTextChunk_h + +#if ENABLE(SVG) +#include "SVGRenderStyleDefs.h" +#include "SVGTextContentElement.h" + +namespace WebCore { + +class SVGInlineTextBox; + +// A SVGTextChunk describes a range of SVGTextFragments, see the SVG spec definition of a "text chunk". +class SVGTextChunk { +public: + SVGTextChunk(bool isVerticalText, ETextAnchor, SVGTextContentElement::SVGLengthAdjustType, float desiredTextLength); + + void calculateLength(float& length, unsigned& characters) const; + float calculateTextAnchorShift(float length) const; + + bool isVerticalText() const { return m_isVerticalText; } + ETextAnchor textAnchor() const { return m_textAnchor; } + SVGTextContentElement::SVGLengthAdjustType lengthAdjust() const { return m_lengthAdjust; } + float desiredTextLength() const { return m_desiredTextLength; } + + Vector<SVGInlineTextBox*>& boxes() { return m_boxes; } + const Vector<SVGInlineTextBox*>& boxes() const { return m_boxes; } + + bool hasDesiredTextLength() const { return m_lengthAdjust != SVGTextContentElement::LENGTHADJUST_UNKNOWN && m_desiredTextLength > 0; } + bool hasTextAnchor() const { return m_textAnchor != TA_START; } + +private: + // Contains all SVGInlineTextBoxes this chunk spans. + Vector<SVGInlineTextBox*> m_boxes; + + // writing-mode specific property. + bool m_isVerticalText; + + // text-anchor specific properties. + ETextAnchor m_textAnchor; + + // textLength/lengthAdjust specific properties. + SVGTextContentElement::SVGLengthAdjustType m_lengthAdjust; + float m_desiredTextLength; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif diff --git a/WebCore/rendering/svg/SVGTextChunkBuilder.cpp b/WebCore/rendering/svg/SVGTextChunkBuilder.cpp new file mode 100644 index 0000000..bbbae6c --- /dev/null +++ b/WebCore/rendering/svg/SVGTextChunkBuilder.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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. + */ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGTextChunkBuilder.h" + +#include "RenderSVGInlineText.h" +#include "SVGElement.h" +#include "SVGInlineTextBox.h" + +namespace WebCore { + +SVGTextChunkBuilder::SVGTextChunkBuilder() +{ +} + +void SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox, AffineTransform& transform) const +{ + DEFINE_STATIC_LOCAL(const AffineTransform, s_identityTransform, ()); + if (!m_textBoxTransformations.contains(textBox)) { + transform = s_identityTransform; + return; + } + + transform = m_textBoxTransformations.get(textBox); +} + +void SVGTextChunkBuilder::buildTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes) +{ + if (lineLayoutBoxes.isEmpty()) + return; + + bool foundStart = false; + unsigned lastChunkStartPosition = 0; + unsigned boxPosition = 0; + unsigned boxCount = lineLayoutBoxes.size(); + for (; boxPosition < boxCount; ++boxPosition) { + SVGInlineTextBox* textBox = lineLayoutBoxes.at(boxPosition); + if (!textBox->startsNewTextChunk()) + continue; + + if (!foundStart) { + lastChunkStartPosition = boxPosition; + foundStart = true; + } else { + ASSERT(boxPosition > lastChunkStartPosition); + addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition); + lastChunkStartPosition = boxPosition; + } + } + + if (!foundStart) + return; + + if (boxPosition - lastChunkStartPosition > 0) + addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition); +} + +void SVGTextChunkBuilder::layoutTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes) +{ + buildTextChunks(lineLayoutBoxes); + if (m_textChunks.isEmpty()) + return; + + unsigned chunkCount = m_textChunks.size(); + for (unsigned i = 0; i < chunkCount; ++i) + processTextChunk(m_textChunks.at(i)); + + m_textChunks.clear(); +} + +void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount) +{ + SVGInlineTextBox* textBox = lineLayoutBoxes.at(boxStart); + ASSERT(textBox); + + RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer()); + ASSERT(textRenderer); + + const RenderStyle* style = textRenderer->style(); + ASSERT(style); + + const SVGRenderStyle* svgStyle = style->svgStyle(); + ASSERT(svgStyle); + + SVGTextContentElement::SVGLengthAdjustType lengthAdjust = SVGTextContentElement::LENGTHADJUST_UNKNOWN; + float desiredTextLength = 0; + + if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textRenderer->parent())) { + lengthAdjust = static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust()); + desiredTextLength = textContentElement->textLength().value(textContentElement); + } + + SVGTextChunk chunk(svgStyle->isVerticalWritingMode(), svgStyle->textAnchor(), lengthAdjust, desiredTextLength); + + Vector<SVGInlineTextBox*>& boxes = chunk.boxes(); + for (unsigned i = boxStart; i < boxStart + boxCount; ++i) + boxes.append(lineLayoutBoxes.at(i)); + + m_textChunks.append(chunk); +} + +void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk) +{ + bool processTextLength = chunk.hasDesiredTextLength(); + bool processTextAnchor = chunk.hasTextAnchor(); + if (!processTextAnchor && !processTextLength) + return; + + const Vector<SVGInlineTextBox*>& boxes = chunk.boxes(); + unsigned boxCount = boxes.size(); + if (!boxCount) + return; + + // Calculate absolute length of whole text chunk (starting from text box 'start', spanning 'length' text boxes). + float chunkLength = 0; + unsigned chunkCharacters = 0; + chunk.calculateLength(chunkLength, chunkCharacters); + + bool isVerticalText = chunk.isVerticalText(); + if (processTextLength) { + if (chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACING) { + float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters; + unsigned atCharacter = 0; + for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { + Vector<SVGTextFragment>& fragments = boxes.at(boxPosition)->textFragments(); + if (fragments.isEmpty()) + continue; + processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter); + } + } else { + ASSERT(chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS); + float scale = chunk.desiredTextLength() / chunkLength; + AffineTransform spacingAndGlyphsTransform; + + bool foundFirstFragment = false; + for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { + SVGInlineTextBox* textBox = boxes.at(boxPosition); + Vector<SVGTextFragment>& fragments = textBox->textFragments(); + if (fragments.isEmpty()) + continue; + + if (!foundFirstFragment) { + foundFirstFragment = true; + buildSpacingAndGlyphsTransform(isVerticalText, scale, fragments.first(), spacingAndGlyphsTransform); + } + + m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform); + } + } + } + + if (!processTextAnchor) + return; + + // If we previously applied a lengthAdjust="spacing" correction, we have to recalculate the chunk length, to be able to apply the text-anchor shift. + if (processTextLength && chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACING) { + chunkLength = 0; + chunkCharacters = 0; + chunk.calculateLength(chunkLength, chunkCharacters); + } + + float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength); + for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { + Vector<SVGTextFragment>& fragments = boxes.at(boxPosition)->textFragments(); + if (fragments.isEmpty()) + continue; + processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); + } +} + +void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharacter) +{ + unsigned fragmentCount = fragments.size(); + for (unsigned i = 0; i < fragmentCount; ++i) { + SVGTextFragment& fragment = fragments.at(i); + + if (isVerticalText) + fragment.y += textLengthShift * atCharacter; + else + fragment.x += textLengthShift * atCharacter; + + atCharacter += fragment.length; + } +} + +void SVGTextChunkBuilder::processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>& fragments) +{ + unsigned fragmentCount = fragments.size(); + for (unsigned i = 0; i < fragmentCount; ++i) { + SVGTextFragment& fragment = fragments.at(i); + + if (isVerticalText) + fragment.y += textAnchorShift; + else + fragment.x += textAnchorShift; + } +} + +void SVGTextChunkBuilder::buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) +{ + spacingAndGlyphsTransform.translate(fragment.x, fragment.y); + + if (isVerticalText) + spacingAndGlyphsTransform.scaleNonUniform(1, scale); + else + spacingAndGlyphsTransform.scaleNonUniform(scale, 1); + + spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); +} + +} + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/svg/SVGTextChunkBuilder.h b/WebCore/rendering/svg/SVGTextChunkBuilder.h new file mode 100644 index 0000000..36342e7 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextChunkBuilder.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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 SVGTextChunkBuilder_h +#define SVGTextChunkBuilder_h + +#if ENABLE(SVG) +#include "SVGTextChunk.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class SVGInlineTextBox; +struct SVGTextFragment; + +// SVGTextChunkBuilder performs the third layout phase for SVG text. +// +// Phase one built the layout information from the SVG DOM stored in the RenderSVGInlineText objects (SVGTextLayoutAttributes). +// Phase two performed the actual per-character layout, computing the final positions for each character, stored in the SVGInlineTextBox objects (SVGTextFragment). +// Phase three performs all modifications that have to be applied to each individual text chunk (text-anchor & textLength). + +class SVGTextChunkBuilder : public Noncopyable { +public: + SVGTextChunkBuilder(); + + const Vector<SVGTextChunk>& textChunks() const { return m_textChunks; } + void transformationForTextBox(SVGInlineTextBox*, AffineTransform&) const; + + void buildTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes); + void layoutTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes); + +private: + void addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxPosition, unsigned boxCount); + void processTextChunk(const SVGTextChunk&); + + void processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>&, unsigned& atCharacter); + void processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>&); + void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment&, AffineTransform&); + +private: + Vector<SVGTextChunk> m_textChunks; + HashMap<SVGInlineTextBox*, AffineTransform> m_textBoxTransformations; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif diff --git a/WebCore/rendering/svg/SVGTextFragment.h b/WebCore/rendering/svg/SVGTextFragment.h new file mode 100644 index 0000000..2e520da --- /dev/null +++ b/WebCore/rendering/svg/SVGTextFragment.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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 SVGTextFragment_h +#define SVGTextFragment_h + +#if ENABLE(SVG) +#include "AffineTransform.h" + +namespace WebCore { + +// A SVGTextFragment describes a text fragment of a RenderSVGInlineText which can be rendered at once. +struct SVGTextFragment { + SVGTextFragment() + : positionListOffset(0) + , length(0) + , x(0) + , y(0) + , width(0) + , height(0) + { + } + + // The first rendered character starts at RenderSVGInlineText::characters() + positionListOffset. + unsigned positionListOffset; + unsigned length; + + float x; + float y; + float width; + float height; + + // Includes rotation/glyph-orientation-(horizontal|vertical) transforms, lengthAdjust="spacingAndGlyphs" (for textPath only), + // as well as orientation related shifts (see SVGTextLayoutEngine, which builds this transformation). + AffineTransform transform; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif diff --git a/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp b/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp index 3283b35..3037b77 100644 --- a/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp +++ b/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp @@ -31,13 +31,13 @@ SVGTextLayoutAttributes::SVGTextLayoutAttributes() { } -void SVGTextLayoutAttributes::fillWithEmptyValues(unsigned length) +void SVGTextLayoutAttributes::reserveCapacity(unsigned length) { - m_xValues.fill(emptyValue(), length); - m_yValues.fill(emptyValue(), length); - m_dxValues.fill(emptyValue(), length); - m_dyValues.fill(emptyValue(), length); - m_rotateValues.fill(emptyValue(), length); + m_xValues.reserveCapacity(length); + m_yValues.reserveCapacity(length); + m_dxValues.reserveCapacity(length); + m_dyValues.reserveCapacity(length); + m_rotateValues.reserveCapacity(length); } float SVGTextLayoutAttributes::emptyValue() @@ -46,7 +46,7 @@ float SVGTextLayoutAttributes::emptyValue() return s_emptyValue; } -static inline void dumpLayoutVector(Vector<float>& values) +static inline void dumpLayoutVector(const Vector<float>& values) { if (values.isEmpty()) { fprintf(stderr, "empty"); @@ -63,7 +63,7 @@ static inline void dumpLayoutVector(Vector<float>& values) } } -void SVGTextLayoutAttributes::dump() +void SVGTextLayoutAttributes::dump() const { fprintf(stderr, "x values: "); dumpLayoutVector(m_xValues); @@ -86,11 +86,11 @@ void SVGTextLayoutAttributes::dump() fprintf(stderr, "\n"); fprintf(stderr, "character data values:\n"); - Vector<CharacterData>::iterator end = m_characterDataValues.end(); - for (Vector<CharacterData>::iterator it = m_characterDataValues.begin(); it != end; ++it) { - CharacterData& data = *it; - fprintf(stderr, "| {spansCharacters=%i, glyphName='%s', unicodeString='%s', width=%lf, height=%lf}\n", - data.spansCharacters, data.glyphName.utf8().data(), data.unicodeString.utf8().data(), data.width, data.height); + unsigned textMetricsSize = m_textMetricsValues.size(); + for (unsigned i = 0; i < textMetricsSize; ++i) { + const SVGTextMetrics& metrics = m_textMetricsValues.at(i); + fprintf(stderr, "| {length=%i, glyphName='%s', unicodeString='%s', width=%lf, height=%lf}\n", + metrics.length(), metrics.glyph().name.utf8().data(), metrics.glyph().unicodeString.utf8().data(), metrics.width(), metrics.height()); } fprintf(stderr, "\n"); } diff --git a/WebCore/rendering/svg/SVGTextLayoutAttributes.h b/WebCore/rendering/svg/SVGTextLayoutAttributes.h index d88b356..d08d5b7 100644 --- a/WebCore/rendering/svg/SVGTextLayoutAttributes.h +++ b/WebCore/rendering/svg/SVGTextLayoutAttributes.h @@ -21,6 +21,7 @@ #define SVGTextLayoutAttributes_h #if ENABLE(SVG) +#include "SVGTextMetrics.h" #include <wtf/Vector.h> #include <wtf/text/WTFString.h> @@ -30,8 +31,10 @@ class SVGTextLayoutAttributes { public: SVGTextLayoutAttributes(); - void fillWithEmptyValues(unsigned length); - void dump(); + void reserveCapacity(unsigned length); + void dump() const; + + static float emptyValue(); Vector<float>& xValues() { return m_xValues; } const Vector<float>& xValues() const { return m_xValues; } @@ -48,31 +51,8 @@ public: Vector<float>& rotateValues() { return m_rotateValues; } const Vector<float>& rotateValues() const { return m_rotateValues; } - static float emptyValue(); - - struct CharacterData { - CharacterData() - : spansCharacters(0) - , width(0) - , height(0) - { - } - - // When multiple unicode characters map to a single glyph (eg. 'ffi' ligature) - // 'spansCharacters' contains the number of characters this glyph spans. - int spansCharacters; - - // The 'glyphName' / 'unicodeString' pair is needed for kerning calculations. - String glyphName; - String unicodeString; - - // 'width' and 'height' hold the size of this glyph/character. - float width; - float height; - }; - - Vector<CharacterData>& characterDataValues() { return m_characterDataValues; } - const Vector<CharacterData>& characterDataValues() const { return m_characterDataValues; } + Vector<SVGTextMetrics>& textMetricsValues() { return m_textMetricsValues; } + const Vector<SVGTextMetrics>& textMetricsValues() const { return m_textMetricsValues; } private: Vector<float> m_xValues; @@ -80,7 +60,7 @@ private: Vector<float> m_dxValues; Vector<float> m_dyValues; Vector<float> m_rotateValues; - Vector<CharacterData> m_characterDataValues; + Vector<SVGTextMetrics> m_textMetricsValues; }; } // namespace WebCore diff --git a/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp b/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp new file mode 100644 index 0000000..c3f4b6a --- /dev/null +++ b/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp @@ -0,0 +1,317 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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. + */ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGTextLayoutAttributesBuilder.h" + +#include "RenderSVGInlineText.h" +#include "RenderSVGText.h" +#include "SVGTextPositioningElement.h" + +// Set to a value > 0 to dump the text layout attributes +#define DUMP_TEXT_LAYOUT_ATTRIBUTES 0 + +namespace WebCore { + +SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder() +{ +} + +void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextSubtree(RenderSVGText* textRoot) +{ + ASSERT(textRoot); + m_scopes.clear(); + + // Build list of x/y/dx/dy/rotate values for each subtree element that may define these values (tspan/textPath etc). + unsigned atCharacter = 0; + UChar lastCharacter = '\0'; + buildLayoutScopes(textRoot, atCharacter, lastCharacter); + + if (!atCharacter) + return; + + // Build list of x/y/dx/dy/rotate values for the outermost <text> element. + buildOutermostLayoutScope(textRoot, atCharacter); + + // Propagate layout attributes to each RenderSVGInlineText object. + atCharacter = 0; + lastCharacter = '\0'; + propagateLayoutAttributes(textRoot, atCharacter, lastCharacter); +} + +static inline void extractFloatValuesFromSVGLengthList(SVGElement* lengthContext, SVGLengthList* list, Vector<float>& floatValues, unsigned textContentLength) +{ + ASSERT(lengthContext); + ASSERT(list); + + unsigned length = list->numberOfItems(); + if (length > textContentLength) + length = textContentLength; + floatValues.reserveCapacity(length); + + ExceptionCode ec = 0; + for (unsigned i = 0; i < length; ++i) { + SVGLength length = list->getItem(i, ec); + ASSERT(!ec); + floatValues.append(length.value(lengthContext)); + } +} + +static inline void extractFloatValuesFromSVGNumberList(SVGNumberList* list, Vector<float>& floatValues, unsigned textContentLength) +{ + ASSERT(list); + + unsigned length = list->numberOfItems(); + if (length > textContentLength) + length = textContentLength; + floatValues.reserveCapacity(length); + + ExceptionCode ec = 0; + for (unsigned i = 0; i < length; ++i) { + float length = list->getItem(i, ec); + ASSERT(!ec); + floatValues.append(length); + } +} + +void SVGTextLayoutAttributesBuilder::buildLayoutScope(LayoutScope& scope, RenderObject* renderer, unsigned textContentStart, unsigned textContentLength) const +{ + ASSERT(renderer); + ASSERT(renderer->style()); + + scope.textContentStart = textContentStart; + scope.textContentLength = textContentLength; + + SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(renderer); + if (!element) + return; + + SVGTextLayoutAttributes& attributes = scope.attributes; + extractFloatValuesFromSVGLengthList(element, element->x(), attributes.xValues(), textContentLength); + extractFloatValuesFromSVGLengthList(element, element->y(), attributes.yValues(), textContentLength); + extractFloatValuesFromSVGLengthList(element, element->dx(), attributes.dxValues(), textContentLength); + extractFloatValuesFromSVGLengthList(element, element->dy(), attributes.dyValues(), textContentLength); + extractFloatValuesFromSVGNumberList(element->rotate(), attributes.rotateValues(), textContentLength); + + // The last rotation value spans the whole scope. + Vector<float>& rotateValues = attributes.rotateValues(); + if (rotateValues.isEmpty()) + return; + + unsigned rotateValuesSize = rotateValues.size(); + if (rotateValuesSize == textContentLength) + return; + + float lastRotation = rotateValues.last(); + + rotateValues.resize(textContentLength); + for (unsigned i = rotateValuesSize; i < textContentLength; ++i) + rotateValues.at(i) = lastRotation; +} + +static inline bool characterIsSpace(const UChar& character) +{ + return character == ' '; +} + +static inline bool characterIsSpaceOrNull(const UChar& character) +{ + return character == ' ' || character == '\0'; +} + +static inline bool shouldPreserveAllWhiteSpace(RenderStyle* style) +{ + ASSERT(style); + return style->whiteSpace() == PRE; +} + +void SVGTextLayoutAttributesBuilder::buildLayoutScopes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter) +{ + for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { + if (child->isSVGInlineText()) { + RenderSVGInlineText* text = toRenderSVGInlineText(child); + + if (!shouldPreserveAllWhiteSpace(text->style())) { + const UChar* characters = text->characters(); + unsigned textLength = text->textLength(); + for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) { + const UChar& currentCharacter = characters[textPosition]; + if (characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) + continue; + + lastCharacter = currentCharacter; + ++atCharacter; + } + } else + atCharacter += text->textLength(); + + continue; + } + + if (!child->isSVGInline()) + continue; + + unsigned textContentStart = atCharacter; + buildLayoutScopes(child, atCharacter, lastCharacter); + + LayoutScope scope; + buildLayoutScope(scope, child, textContentStart, atCharacter - textContentStart); + m_scopes.append(scope); + } +} + +void SVGTextLayoutAttributesBuilder::buildOutermostLayoutScope(RenderSVGText* textRoot, unsigned textLength) +{ + LayoutScope scope; + buildLayoutScope(scope, textRoot, 0, textLength); + + // Handle <text> x/y default attributes. + Vector<float>& xValues = scope.attributes.xValues(); + if (xValues.isEmpty()) + xValues.append(0); + + Vector<float>& yValues = scope.attributes.yValues(); + if (yValues.isEmpty()) + yValues.append(0); + + m_scopes.prepend(scope); +} + +void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter) const +{ + for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { + if (child->isSVGInlineText()) { + RenderSVGInlineText* text = toRenderSVGInlineText(child); + const UChar* characters = text->characters(); + unsigned textLength = text->textLength(); + bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style()); + + SVGTextLayoutAttributes attributes; + attributes.reserveCapacity(textLength); + + unsigned valueListPosition = atCharacter; + unsigned metricsLength = 1; + for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) { + const UChar& currentCharacter = characters[textPosition]; + + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1); + metricsLength = metrics.length(); + + if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) { + assignEmptyLayoutAttributesForCharacter(attributes); + attributes.textMetricsValues().append(SVGTextMetrics::emptyMetrics()); + continue; + } + + assignLayoutAttributesForCharacter(attributes, metrics, valueListPosition); + + if (metricsLength > 1) { + for (unsigned i = 0; i < metricsLength - 1; ++i) + assignEmptyLayoutAttributesForCharacter(attributes); + } + + lastCharacter = currentCharacter; + valueListPosition += metricsLength; + } + +#if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0 + fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter); + attributes.dump(); +#endif + + text->storeLayoutAttributes(attributes); + atCharacter = valueListPosition; + continue; + } + + if (!child->isSVGInline()) + continue; + + propagateLayoutAttributes(child, atCharacter, lastCharacter); + } +} + +float SVGTextLayoutAttributesBuilder::nextLayoutValue(LayoutValueType type, unsigned atCharacter) const +{ + for (int i = m_scopes.size() - 1; i >= 0; --i) { + const LayoutScope& scope = m_scopes.at(i); + if (scope.textContentStart > atCharacter || scope.textContentStart + scope.textContentLength < atCharacter) + continue; + + const Vector<float>* valuesPointer = 0; + switch (type) { + case XValueAttribute: + valuesPointer = &scope.attributes.xValues(); + break; + case YValueAttribute: + valuesPointer = &scope.attributes.yValues(); + break; + case DxValueAttribute: + valuesPointer = &scope.attributes.dxValues(); + break; + case DyValueAttribute: + valuesPointer = &scope.attributes.dyValues(); + break; + case RotateValueAttribute: + valuesPointer = &scope.attributes.rotateValues(); + break; + default: + ASSERT_NOT_REACHED(); + } + + ASSERT(valuesPointer); + const Vector<float>& values = *valuesPointer; + if (values.isEmpty()) + continue; + + unsigned position = atCharacter - scope.textContentStart; + if (position >= values.size()) + continue; + + return values.at(position); + } + + return SVGTextLayoutAttributes::emptyValue(); +} + +void SVGTextLayoutAttributesBuilder::assignLayoutAttributesForCharacter(SVGTextLayoutAttributes& attributes, SVGTextMetrics& metrics, unsigned valueListPosition) const +{ + attributes.xValues().append(nextLayoutValue(XValueAttribute, valueListPosition)); + attributes.yValues().append(nextLayoutValue(YValueAttribute, valueListPosition)); + attributes.dxValues().append(nextLayoutValue(DxValueAttribute, valueListPosition)); + attributes.dyValues().append(nextLayoutValue(DyValueAttribute, valueListPosition)); + attributes.rotateValues().append(nextLayoutValue(RotateValueAttribute, valueListPosition)); + attributes.textMetricsValues().append(metrics); +} + +void SVGTextLayoutAttributesBuilder::assignEmptyLayoutAttributesForCharacter(SVGTextLayoutAttributes& attributes) const +{ + attributes.xValues().append(SVGTextLayoutAttributes::emptyValue()); + attributes.yValues().append(SVGTextLayoutAttributes::emptyValue()); + attributes.dxValues().append(SVGTextLayoutAttributes::emptyValue()); + attributes.dyValues().append(SVGTextLayoutAttributes::emptyValue()); + attributes.rotateValues().append(SVGTextLayoutAttributes::emptyValue()); + // This doesn't add an empty value to textMetricsValues() on purpose! +} + +} + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/svg/SVGTextLayoutBuilder.h b/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h index 6df7fa1..f29ac64 100644 --- a/WebCore/rendering/svg/SVGTextLayoutBuilder.h +++ b/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h @@ -17,8 +17,8 @@ * Boston, MA 02110-1301, USA. */ -#ifndef SVGTextLayoutBuilder_h -#define SVGTextLayoutBuilder_h +#ifndef SVGTextLayoutAttributesBuilder_h +#define SVGTextLayoutAttributesBuilder_h #if ENABLE(SVG) #include "SVGTextLayoutAttributes.h" @@ -27,38 +27,54 @@ namespace WebCore { class RenderObject; -class RenderSVGInlineText; class RenderSVGText; -class SVGTextLayoutBuilder : public Noncopyable { +// SVGTextLayoutAttributesBuilder performs the first layout phase for SVG text. +// +// It extracts the x/y/dx/dy/rotate values from the SVGTextPositioningElements in the DOM, +// measures all characters in the RenderSVGText subtree and extracts kerning/ligature information. +// These values are propagated to the corresponding RenderSVGInlineText renderers. +// The first layout phase only extracts the relevant information needed in RenderBlockLineLayout +// to create the InlineBox tree based on text chunk boundaries & BiDi information. +// The second layout phase is carried out by SVGTextLayoutEngine. + +class SVGTextLayoutAttributesBuilder : public Noncopyable { public: - SVGTextLayoutBuilder(); + SVGTextLayoutAttributesBuilder(); void buildLayoutAttributesForTextSubtree(RenderSVGText*); private: struct LayoutScope { LayoutScope() - : isVerticalWritingMode(false) - , textContentStart(0) + : textContentStart(0) , textContentLength(0) { } - bool isVerticalWritingMode; unsigned textContentStart; unsigned textContentLength; SVGTextLayoutAttributes attributes; }; - void buildLayoutScopes(RenderObject*, unsigned& atCharacter); - void buildLayoutScope(LayoutScope&, RenderObject*, unsigned textContentStart, unsigned textContentLength); - void buildLayoutAttributesFromScopes(); - void propagateLayoutAttributes(RenderObject*, unsigned& atCharacter); - void measureCharacters(RenderSVGInlineText*, SVGTextLayoutAttributes&); + void buildLayoutScope(LayoutScope&, RenderObject*, unsigned textContentStart, unsigned textContentLength) const; + void buildLayoutScopes(RenderObject*, unsigned& atCharacter, UChar& lastCharacter); + void buildOutermostLayoutScope(RenderSVGText*, unsigned textLength); + void propagateLayoutAttributes(RenderObject*, unsigned& atCharacter, UChar& lastCharacter) const; + + enum LayoutValueType { + XValueAttribute, + YValueAttribute, + DxValueAttribute, + DyValueAttribute, + RotateValueAttribute + }; + + float nextLayoutValue(LayoutValueType, unsigned atCharacter) const; + void assignLayoutAttributesForCharacter(SVGTextLayoutAttributes&, SVGTextMetrics&, unsigned valueListPosition) const; + void assignEmptyLayoutAttributesForCharacter(SVGTextLayoutAttributes&) const; private: Vector<LayoutScope> m_scopes; - SVGTextLayoutAttributes m_attributes; }; } // namespace WebCore diff --git a/WebCore/rendering/svg/SVGTextLayoutBuilder.cpp b/WebCore/rendering/svg/SVGTextLayoutBuilder.cpp deleted file mode 100644 index 0b3a752..0000000 --- a/WebCore/rendering/svg/SVGTextLayoutBuilder.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) Research In Motion Limited 2010. 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. - */ - -#include "config.h" - -#if ENABLE(SVG) -#include "SVGTextLayoutBuilder.h" - -#include "RenderSVGInlineText.h" -#include "RenderSVGText.h" -#include "SVGTextLayoutUtilities.h" -#include "SVGTextPositioningElement.h" - -// Set to a value > 0 to dump the layout vectors -#define DUMP_LAYOUT_VECTORS 0 - -namespace WebCore { - -SVGTextLayoutBuilder::SVGTextLayoutBuilder() -{ -} - -void SVGTextLayoutBuilder::buildLayoutAttributesForTextSubtree(RenderSVGText* textRoot) -{ - ASSERT(textRoot); - m_scopes.clear(); - - // Build layout scopes. - unsigned atCharacter = 0; - buildLayoutScopes(textRoot, atCharacter); - - if (!atCharacter) - return; - - // Add outermost scope, after text length is known. - LayoutScope scope; - buildLayoutScope(scope, textRoot, 0, atCharacter); - m_scopes.prepend(scope); - - // Build layout information respecting scope of attribute values. - buildLayoutAttributesFromScopes(); - - atCharacter = 0; - propagateLayoutAttributes(textRoot, atCharacter); -} - -static inline void copyToDestinationVector(Vector<float>& destination, unsigned destinationStartOffset, Vector<float>& source, unsigned sourceStartOffset, unsigned length) -{ - ASSERT(destinationStartOffset + length <= destination.size()); - - Vector<float>::iterator sourceBegin = source.begin() + sourceStartOffset; - std::copy(sourceBegin, sourceBegin + length, destination.begin() + destinationStartOffset); -} - -static inline void copyToDestinationVectorIfSourceRangeIsNotEmpty(Vector<float>& destination, unsigned destinationStartOffset, Vector<float>& source, unsigned sourceStartOffset, unsigned length) -{ - bool rangeEmpty = true; - - unsigned size = sourceStartOffset + length; - for (unsigned i = sourceStartOffset; i < size; ++i) { - if (source.at(i) == SVGTextLayoutAttributes::emptyValue()) - continue; - rangeEmpty = false; - break; - } - - if (rangeEmpty) - return; - - destination.resize(length); - copyToDestinationVector(destination, destinationStartOffset, source, sourceStartOffset, length); -} - -void SVGTextLayoutBuilder::propagateLayoutAttributes(RenderObject* start, unsigned& atCharacter) -{ - for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { - if (!child->isSVGInlineText()) { - if (child->isSVGInline()) - propagateLayoutAttributes(child, atCharacter); - continue; - } - - RenderSVGInlineText* text = static_cast<RenderSVGInlineText*>(child); - unsigned textLength = text->textLength(); - - // Build layout attributes for a single RenderSVGInlineText renderer. - SVGTextLayoutAttributes attributes; - - // The x value list should always be as large as the text length. - // Any values that are empty will be filled in by the actual text layout process later, - // as we need to be able to query the x/y position for every character through SVG DOM. - attributes.xValues().resize(textLength); - copyToDestinationVector(attributes.xValues(), 0, m_attributes.xValues(), atCharacter, textLength); - - // Same for the y value list. - attributes.yValues().resize(textLength); - copyToDestinationVector(attributes.yValues(), 0, m_attributes.yValues(), atCharacter, textLength); - - // The dx/dy/rotate value lists may be empty. - copyToDestinationVectorIfSourceRangeIsNotEmpty(attributes.dxValues(), 0, m_attributes.dxValues(), atCharacter, textLength); - copyToDestinationVectorIfSourceRangeIsNotEmpty(attributes.dyValues(), 0, m_attributes.dyValues(), atCharacter, textLength); - copyToDestinationVectorIfSourceRangeIsNotEmpty(attributes.rotateValues(), 0, m_attributes.rotateValues(), atCharacter, textLength); - - // Build CharacterData, which will be used to detect ligatures, holds kerning pairs (glyph name, unicode string) and character metrics. - measureCharacters(text, attributes); - -#if DUMP_LAYOUT_VECTORS > 0 - fprintf(stderr, "Dumping layout vector for RenderSVGInlineText, renderer=%p, node=%p\n", text, text->node()); - attributes.dump(); -#endif - - text->storeLayoutAttributes(attributes); - atCharacter += text->textLength(); - } -} - -void SVGTextLayoutBuilder::buildLayoutScopes(RenderObject* start, unsigned& atCharacter) -{ - for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { - if (child->isSVGInlineText()) { - atCharacter += toRenderText(child)->textLength(); - continue; - } - - if (!child->isSVGInline()) - continue; - - unsigned textContentStart = atCharacter; - buildLayoutScopes(child, atCharacter); - - LayoutScope scope; - buildLayoutScope(scope, child, textContentStart, atCharacter - textContentStart); - m_scopes.append(scope); - } -} - -static inline void fillDestinationVectorWithLastSourceValue(Vector<float>& destination, unsigned destinationStartOffset, Vector<float>& source, unsigned length) -{ - if (source.isEmpty()) - return; - - float lastValue = source.last(); - - unsigned rotateValuesSize = source.size(); - for (unsigned i = rotateValuesSize; i < length; ++i) { - ASSERT(i + destinationStartOffset < destination.size()); - destination.at(i + destinationStartOffset) = lastValue; - } -} - -void SVGTextLayoutBuilder::buildLayoutAttributesFromScopes() -{ - ASSERT(!m_scopes.isEmpty()); - - unsigned totalLength = m_scopes.first().textContentLength; - if (!totalLength) - return; - - m_attributes.fillWithEmptyValues(totalLength); - - // Build final list of x/y/dx/dy/rotate values for each character stores in the <text> subtree. - for (unsigned atScope = 0; atScope < m_scopes.size(); ++atScope) { - LayoutScope& scope = m_scopes.at(atScope); - SVGTextLayoutAttributes& attributes = scope.attributes; - - copyToDestinationVector(m_attributes.xValues(), scope.textContentStart, attributes.xValues(), 0, attributes.xValues().size()); - copyToDestinationVector(m_attributes.yValues(), scope.textContentStart, attributes.yValues(), 0, attributes.yValues().size()); - copyToDestinationVector(m_attributes.dxValues(), scope.textContentStart, attributes.dxValues(), 0, attributes.dxValues().size()); - copyToDestinationVector(m_attributes.dyValues(), scope.textContentStart, attributes.dyValues(), 0, attributes.dyValues().size()); - copyToDestinationVector(m_attributes.rotateValues(), scope.textContentStart, attributes.rotateValues(), 0, attributes.rotateValues().size()); - - // In horizontal (vertical) writing modes, the last y (x) value in the scope is the default y (x) value for all following characters, unless explicitely overriden. - if (scope.isVerticalWritingMode) - fillDestinationVectorWithLastSourceValue(m_attributes.xValues(), scope.textContentStart, attributes.xValues(), scope.textContentLength); - else - fillDestinationVectorWithLastSourceValue(m_attributes.yValues(), scope.textContentStart, attributes.yValues(), scope.textContentLength); - - // The last rotation value in the scope is the default rotation for all following character, unless explicitely overriden. - fillDestinationVectorWithLastSourceValue(m_attributes.rotateValues(), scope.textContentStart, attributes.rotateValues(), scope.textContentLength); - } -} - -void SVGTextLayoutBuilder::measureCharacters(RenderSVGInlineText* text, SVGTextLayoutAttributes& attributes) -{ - ASSERT(text); - ASSERT(text->style()); - const Font& font = text->style()->font(); - const UChar* characters = text->characters(); - int length = text->textLength(); - - TextRun run(0, 0); - run.disableSpacing(); - run.disableRoundingHacks(); - - int charsConsumed = 0; - for (int position = 0; position < length; position += charsConsumed) { - run.setText(characters + position, 1); - int extraCharsAvailable = length - position - 1; - - SVGTextLayoutAttributes::CharacterData characterData; - characterData.width = font.floatWidth(run, extraCharsAvailable, characterData.spansCharacters, characterData.glyphName); - characterData.height = font.height(); - characterData.unicodeString = String(characters + position, characterData.spansCharacters); - attributes.characterDataValues().append(characterData); - - charsConsumed = characterData.spansCharacters; - } -} - -static inline void extractFloatValuesFromSVGLengthList(SVGElement* lengthContext, SVGLengthList* list, Vector<float>& floatValues, int textContentLength) -{ - ASSERT(lengthContext); - ASSERT(list); - ASSERT(textContentLength >= 0); - - ExceptionCode ec = 0; - int length = list->numberOfItems(); - if (length > textContentLength) - length = textContentLength; - - for (int i = 0; i < length; ++i) { - SVGLength length(list->getItem(i, ec)); - ASSERT(!ec); - floatValues.append(length.value(lengthContext)); - } -} - -static inline void extractFloatValuesFromSVGNumberList(SVGNumberList* list, Vector<float>& floatValues, int textContentLength) -{ - ASSERT(list); - ASSERT(textContentLength >= 0); - - ExceptionCode ec = 0; - int length = list->numberOfItems(); - if (length > textContentLength) - length = textContentLength; - - for (int i = 0; i < length; ++i) { - float length(list->getItem(i, ec)); - ASSERT(!ec); - floatValues.append(length); - } -} - -static inline SVGTextPositioningElement* svgTextPositioningElementForInlineRenderer(RenderObject* renderer) -{ - ASSERT(renderer); - ASSERT(renderer->isSVGText() || renderer->isSVGInline()); - - Node* node = renderer->node(); - ASSERT(node); - ASSERT(node->isSVGElement()); - - if (!node->hasTagName(SVGNames::textTag) - && !node->hasTagName(SVGNames::tspanTag) -#if ENABLE(SVG_FONTS) - && !node->hasTagName(SVGNames::altGlyphTag) -#endif - && !node->hasTagName(SVGNames::trefTag)) - return 0; - - return static_cast<SVGTextPositioningElement*>(node); -} - -void SVGTextLayoutBuilder::buildLayoutScope(LayoutScope& scope, RenderObject* renderer, unsigned textContentStart, unsigned textContentLength) -{ - ASSERT(renderer); - ASSERT(renderer->style()); - - scope.isVerticalWritingMode = isVerticalWritingMode(renderer->style()->svgStyle()); - scope.textContentStart = textContentStart; - scope.textContentLength = textContentLength; - - SVGTextPositioningElement* element = svgTextPositioningElementForInlineRenderer(renderer); - if (!element) - return; - - SVGTextLayoutAttributes& attributes = scope.attributes; - extractFloatValuesFromSVGLengthList(element, element->x(), attributes.xValues(), textContentLength); - extractFloatValuesFromSVGLengthList(element, element->y(), attributes.yValues(), textContentLength); - extractFloatValuesFromSVGLengthList(element, element->dx(), attributes.dxValues(), textContentLength); - extractFloatValuesFromSVGLengthList(element, element->dy(), attributes.dyValues(), textContentLength); - extractFloatValuesFromSVGNumberList(element->rotate(), attributes.rotateValues(), textContentLength); -} - -} - -#endif // ENABLE(SVG) diff --git a/WebCore/rendering/svg/SVGTextLayoutEngine.cpp b/WebCore/rendering/svg/SVGTextLayoutEngine.cpp new file mode 100644 index 0000000..7eefad6 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextLayoutEngine.cpp @@ -0,0 +1,579 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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. + */ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGTextLayoutEngine.h" + +#include "RenderSVGInlineText.h" +#include "RenderSVGTextPath.h" +#include "SVGElement.h" +#include "SVGInlineTextBox.h" +#include "SVGTextLayoutEngineBaseline.h" +#include "SVGTextLayoutEngineSpacing.h" + +// Set to a value > 0 to dump the text fragments +#define DUMP_TEXT_FRAGMENTS 0 + +namespace WebCore { + +SVGTextLayoutEngine::SVGTextLayoutEngine() + : m_x(0) + , m_y(0) + , m_dx(0) + , m_dy(0) + , m_isVerticalText(false) + , m_inPathLayout(false) + , m_textPathLength(0) + , m_textPathCurrentOffset(0) + , m_textPathSpacing(0) + , m_textPathScaling(1) +{ +} + +void SVGTextLayoutEngine::updateCharacerPositionIfNeeded(float& x, float& y) +{ + if (m_inPathLayout) + return; + + // Replace characters x/y position, with the current text position plus any + // relative adjustments, if it doesn't specify an absolute position itself. + if (x == SVGTextLayoutAttributes::emptyValue()) + x = m_x + m_dx; + + if (y == SVGTextLayoutAttributes::emptyValue()) + y = m_y + m_dy; + + m_dx = 0; + m_dy = 0; +} + +void SVGTextLayoutEngine::updateCurrentTextPosition(float x, float y, float glyphAdvance) +{ + // Update current text position after processing the character. + if (m_isVerticalText) { + m_x = x; + m_y = y + glyphAdvance; + } else { + m_x = x + glyphAdvance; + m_y = y; + } +} + +void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues, unsigned positionListOffset) +{ + // Update relative positioning information. + if (dxValues.isEmpty() && dyValues.isEmpty()) + return; + + float dx = 0; + if (!dxValues.isEmpty()) { + float& dxCurrent = dxValues.at(positionListOffset); + if (dxCurrent != SVGTextLayoutAttributes::emptyValue()) + dx = dxCurrent; + } + + float dy = 0; + if (!dyValues.isEmpty()) { + float& dyCurrent = dyValues.at(positionListOffset); + if (dyCurrent != SVGTextLayoutAttributes::emptyValue()) + dy = dyCurrent; + } + + if (m_inPathLayout) { + if (m_isVerticalText) { + m_dx += dx; + m_dy = dy; + } else { + m_dx = dx; + m_dy += dy; + } + + return; + } + + m_dx = dx; + m_dy = dy; +} + +void SVGTextLayoutEngine::recordTextFragment(SVGInlineTextBox* textBox, RenderSVGInlineText* text, unsigned positionListOffset, const SVGTextMetrics& lastCharacterMetrics) +{ + ASSERT(!m_currentTextFragment.length); + + // Figure out length of fragment. + m_currentTextFragment.length = positionListOffset - m_currentTextFragment.positionListOffset; + + // Figure out fragment metrics. + if (m_currentTextFragment.length == 1) { + // Fast path, can rely on already computed per-character metrics. + m_currentTextFragment.width = lastCharacterMetrics.width(); + m_currentTextFragment.height = lastCharacterMetrics.height(); + } else { + // Need to measure the whole range (range metrics != sum of character metrics) + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(text, m_currentTextFragment.positionListOffset, m_currentTextFragment.length); + m_currentTextFragment.width = metrics.width(); + m_currentTextFragment.height = metrics.height(); + } + + textBox->textFragments().append(m_currentTextFragment); + m_currentTextFragment = SVGTextFragment(); +} + +bool SVGTextLayoutEngine::parentDefinesTextLength(RenderObject* parent) const +{ + RenderObject* currentParent = parent; + while (currentParent) { + SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(currentParent); + if (textContentElement) { + SVGTextContentElement::SVGLengthAdjustType lengthAdjust = static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust()); + if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACING && textContentElement->textLength().value(textContentElement) > 0) + return true; + } + + if (currentParent->isSVGText()) + return false; + + currentParent = currentParent->parent(); + } + + ASSERT_NOT_REACHED(); + return false; +} + +void SVGTextLayoutEngine::beginTextPathLayout(RenderObject* object, SVGTextLayoutEngine& lineLayout) +{ + ASSERT(object); + + m_inPathLayout = true; + RenderSVGTextPath* textPath = toRenderSVGTextPath(object); + + m_textPath = textPath->layoutPath(); + m_textPathStartOffset = textPath->startOffset(); + m_textPathLength = m_textPath.length(); + if (m_textPathStartOffset > 0 && m_textPathStartOffset <= 1) + m_textPathStartOffset *= m_textPathLength; + + float totalLength = 0; + unsigned totalCharacters = 0; + + lineLayout.m_chunkLayoutBuilder.buildTextChunks(lineLayout.m_lineLayoutBoxes); + const Vector<SVGTextChunk>& textChunks = lineLayout.m_chunkLayoutBuilder.textChunks(); + + unsigned size = textChunks.size(); + for (unsigned i = 0; i < size; ++i) { + const SVGTextChunk& chunk = textChunks.at(i); + + float length = 0; + unsigned characters = 0; + chunk.calculateLength(length, characters); + + // Handle text-anchor as additional start offset for text paths. + m_textPathStartOffset += chunk.calculateTextAnchorShift(length); + + totalLength += length; + totalCharacters += characters; + } + + m_textPathCurrentOffset = m_textPathStartOffset; + + // Eventually handle textLength adjustments. + SVGTextContentElement::SVGLengthAdjustType lengthAdjust = SVGTextContentElement::LENGTHADJUST_UNKNOWN; + float desiredTextLength = 0; + + if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textPath)) { + lengthAdjust = static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust()); + desiredTextLength = textContentElement->textLength().value(textContentElement); + } + + if (!desiredTextLength) + return; + + if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACING) + m_textPathSpacing = (desiredTextLength - totalLength) / totalCharacters; + else + m_textPathScaling = desiredTextLength / totalLength; +} + +void SVGTextLayoutEngine::endTextPathLayout() +{ + m_inPathLayout = false; + m_textPath = Path(); + m_textPathLength = 0; + m_textPathStartOffset = 0; + m_textPathCurrentOffset = 0; + m_textPathSpacing = 0; + m_textPathScaling = 1; +} + +void SVGTextLayoutEngine::layoutInlineTextBox(SVGInlineTextBox* textBox) +{ + ASSERT(textBox); + + RenderSVGInlineText* text = toRenderSVGInlineText(textBox->textRenderer()); + ASSERT(text); + ASSERT(text->parent()); + ASSERT(text->parent()->node()); + ASSERT(text->parent()->node()->isSVGElement()); + + const RenderStyle* style = text->style(); + ASSERT(style); + + textBox->clearTextFragments(); + m_isVerticalText = style->svgStyle()->isVerticalWritingMode(); + layoutTextOnLineOrPath(textBox, text, style); + + if (m_inPathLayout) { + m_pathLayoutBoxes.append(textBox); + return; + } + + m_lineLayoutBoxes.append(textBox); +} + +void SVGTextLayoutEngine::finishLayout() +{ + // After all text fragments are stored in their correpsonding SVGInlineTextBoxes, we can layout individual text chunks. + // Chunk layouting is only performed for line layout boxes, not for path layout, where it has already been done. + m_chunkLayoutBuilder.layoutTextChunks(m_lineLayoutBoxes); + + // Finalize transform matrices, after the chunk layout corrections have been applied, and all fragment x/y positions are finalized. + if (!m_lineLayoutBoxes.isEmpty()) { +#if DUMP_TEXT_FRAGMENTS > 0 + fprintf(stderr, "Line layout: "); +#endif + + finalizeTransformMatrices(m_lineLayoutBoxes); + } + + if (!m_pathLayoutBoxes.isEmpty()) { +#if DUMP_TEXT_FRAGMENTS > 0 + fprintf(stderr, "Path layout: "); +#endif + finalizeTransformMatrices(m_pathLayoutBoxes); + } +} + +void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& boxes) +{ + unsigned boxCount = boxes.size(); + +#if DUMP_TEXT_FRAGMENTS > 0 + fprintf(stderr, "Dumping all text fragments in text sub tree, %i boxes\n", boxCount); + + for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { + SVGInlineTextBox* textBox = boxes.at(boxPosition); + Vector<SVGTextFragment>& fragments = textBox->textFragments(); + fprintf(stderr, "-> Box %i: Dumping text fragments for SVGInlineTextBox, textBox=%p, textRenderer=%p\n", boxPosition, textBox, textBox->textRenderer()); + fprintf(stderr, " textBox properties, start=%i, len=%i\n", textBox->start(), textBox->len()); + fprintf(stderr, " textRenderer properties, textLength=%i\n", textBox->textRenderer()->textLength()); + + const UChar* characters = textBox->textRenderer()->characters(); + + unsigned fragmentCount = fragments.size(); + for (unsigned i = 0; i < fragmentCount; ++i) { + SVGTextFragment& fragment = fragments.at(i); + String fragmentString(characters + fragment.positionListOffset, fragment.length); + fprintf(stderr, " -> Fragment %i, x=%lf, y=%lf, width=%lf, height=%lf, positionListOffset=%i, length=%i, characters='%s'\n" + , i, fragment.x, fragment.y, fragment.width, fragment.height, fragment.positionListOffset, fragment.length, fragmentString.utf8().data()); + } + } +#endif + + + if (!boxCount) + return; + + AffineTransform textBoxTransformation; + for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { + SVGInlineTextBox* textBox = boxes.at(boxPosition); + Vector<SVGTextFragment>& fragments = textBox->textFragments(); + + unsigned fragmentCount = fragments.size(); + for (unsigned i = 0; i < fragmentCount; ++i) { + SVGTextFragment& fragment = fragments.at(i); + AffineTransform& transform = fragment.transform; + if (!transform.isIdentity()) { + transform.translateRight(fragment.x, fragment.y); + transform.translate(-fragment.x, -fragment.y); + } + + m_chunkLayoutBuilder.transformationForTextBox(textBox, textBoxTransformation); + if (textBoxTransformation.isIdentity()) + continue; + + if (transform.isIdentity()) + transform = textBoxTransformation; + else + transform.multiply(textBoxTransformation); + } + } + + boxes.clear(); +} + +void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, RenderSVGInlineText* text, const RenderStyle* style) +{ + SVGElement* lengthContext = static_cast<SVGElement*>(text->parent()->node()); + + RenderObject* textParent = text->parent(); + bool definesTextLength = textParent ? parentDefinesTextLength(textParent) : false; + + const SVGRenderStyle* svgStyle = style->svgStyle(); + ASSERT(svgStyle); + + SVGTextLayoutAttributes& attributes = text->layoutAttributes(); + Vector<float>& xValues = attributes.xValues(); + Vector<float>& yValues = attributes.yValues(); + Vector<float>& dxValues = attributes.dxValues(); + Vector<float>& dyValues = attributes.dyValues(); + Vector<float>& rotateValues = attributes.rotateValues(); + Vector<SVGTextMetrics>& textMetricsValues = attributes.textMetricsValues(); + + unsigned boxStart = textBox->start(); + unsigned boxLength = textBox->len(); + unsigned textMetricsSize = textMetricsValues.size(); + ASSERT(textMetricsSize <= xValues.size()); + ASSERT(textMetricsSize <= yValues.size()); + ASSERT(xValues.size() == yValues.size()); + + if (boxLength > textMetricsSize) + textMetricsSize = boxLength; + + unsigned positionListOffset = 0; + unsigned metricsListOffset = 0; + const UChar* characters = text->characters(); + + const Font& font = style->font(); + SVGTextLayoutEngineSpacing spacingLayout(font); + SVGTextLayoutEngineBaseline baselineLayout(font); + + bool didStartTextFragment = false; + bool applySpacingToNextCharacter = false; + + float lastAngle = 0; + float baselineShift = baselineLayout.calculateBaselineShift(svgStyle, lengthContext); + baselineShift -= baselineLayout.calculateAlignmentBaselineShift(m_isVerticalText, text); + + // Main layout algorithm. + unsigned positionListSize = xValues.size(); + for (; metricsListOffset < textMetricsSize && positionListOffset < positionListSize; ++metricsListOffset) { + SVGTextMetrics& metrics = textMetricsValues.at(metricsListOffset); + // Advance to text box start location. + if (positionListOffset < boxStart) { + positionListOffset += metrics.length(); + continue; + } + + // Stop if we've finished processing this text box. + if (positionListOffset >= boxStart + boxLength) + break; + + float x = xValues.at(positionListOffset); + float y = yValues.at(positionListOffset); + + // When we've advanced to the box start offset, determine using the original x/y values, + // wheter this character starts a new text chunk, before doing any further processing. + if (positionListOffset == boxStart) + textBox->setStartsNewTextChunk(text->characterStartsNewTextChunk(boxStart)); + + if (metrics == SVGTextMetrics::emptyMetrics()) { + positionListOffset += metrics.length(); + continue; + } + + const UChar* currentCharacter = characters + positionListOffset; + float angle = 0; + if (!rotateValues.isEmpty()) { + float newAngle = rotateValues.at(positionListOffset); + if (newAngle != SVGTextLayoutAttributes::emptyValue()) + angle = newAngle; + } + + // Calculate glyph orientation angle. + float orientationAngle = baselineLayout.calculateGlyphOrientationAngle(m_isVerticalText, svgStyle, *currentCharacter); + + // Calculate glyph advance & x/y orientation shifts. + float xOrientationShift = 0; + float yOrientationShift = 0; + float glyphAdvance = baselineLayout.calculateGlyphAdvanceAndOrientation(m_isVerticalText, metrics, orientationAngle, xOrientationShift, yOrientationShift); + + // Assign current text position to x/y values, if needed. + updateCharacerPositionIfNeeded(x, y); + + // Apply dx/dy value adjustments to current text position, if needed. + updateRelativePositionAdjustmentsIfNeeded(dxValues, dyValues, positionListOffset); + + // Calculate SVG Fonts kerning, if needed. + float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, metrics.glyph()); + + // Calculate CSS 'kerning', 'letter-spacing' and 'word-spacing' for next character, if needed. + float spacing = spacingLayout.calculateCSSKerningAndSpacing(svgStyle, lengthContext, currentCharacter); + + float textPathOffset = 0; + if (m_inPathLayout) { + float scaledGlyphAdvance = glyphAdvance * m_textPathScaling; + if (m_isVerticalText) { + // If there's an absolute y position available, it marks the beginning of a new position along the path. + if (y != SVGTextLayoutAttributes::emptyValue()) + m_textPathCurrentOffset = y + m_textPathStartOffset; + + m_textPathCurrentOffset += m_dy - kerning; + m_dy = 0; + + // Apply dx/dy correction and setup translations that move to the glyph midpoint. + xOrientationShift += m_dx + baselineShift; + yOrientationShift -= scaledGlyphAdvance / 2; + } else { + // If there's an absolute x position available, it marks the beginning of a new position along the path. + if (x != SVGTextLayoutAttributes::emptyValue()) + m_textPathCurrentOffset = x + m_textPathStartOffset; + + m_textPathCurrentOffset += m_dx - kerning; + m_dx = 0; + + // Apply dx/dy correction and setup translations that move to the glyph midpoint. + xOrientationShift -= scaledGlyphAdvance / 2; + yOrientationShift += m_dy - baselineShift; + } + + // Calculate current offset along path. + textPathOffset = m_textPathCurrentOffset + scaledGlyphAdvance / 2; + + // Move to next character. + m_textPathCurrentOffset += scaledGlyphAdvance + m_textPathSpacing + spacing * m_textPathScaling; + + // Skip character, if we're before the path. + if (textPathOffset < 0) { + positionListOffset += metrics.length(); + continue; + } + + // Stop processing, if the next character lies behind the path. + if (textPathOffset > m_textPathLength) + break; + + bool ok = false; + FloatPoint point = m_textPath.pointAtLength(textPathOffset, ok); + ASSERT(ok); + + x = point.x(); + y = point.y(); + angle = m_textPath.normalAngleAtLength(textPathOffset, ok); + ASSERT(ok); + + // For vertical text on path, the actual angle has to be rotated 90 degrees anti-clockwise, not the orientation angle! + if (m_isVerticalText) + angle -= 90; + } else { + // Apply all previously calculated shift values. + if (m_isVerticalText) { + x += baselineShift; + y -= kerning; + } else { + x -= kerning; + y -= baselineShift; + } + + x += m_dx; + 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; + + // If we already started a fragment, close it now. + if (didStartTextFragment && shouldStartNewFragment) { + applySpacingToNextCharacter = false; + recordTextFragment(textBox, text, positionListOffset, textMetricsValues.at(metricsListOffset - 1)); + } + + // Eventually start a new fragment, if not yet done. + if (!didStartTextFragment || shouldStartNewFragment) { + ASSERT(!m_currentTextFragment.positionListOffset); + ASSERT(!m_currentTextFragment.length); + + didStartTextFragment = true; + m_currentTextFragment.positionListOffset = positionListOffset; + m_currentTextFragment.x = x; + m_currentTextFragment.y = y; + + // Build fragment transformation. + if (angle) + m_currentTextFragment.transform.rotate(angle); + + if (xOrientationShift || yOrientationShift) + m_currentTextFragment.transform.translate(xOrientationShift, yOrientationShift); + + if (orientationAngle) + m_currentTextFragment.transform.rotate(orientationAngle); + + if (m_inPathLayout && m_textPathScaling != 1) { + if (m_isVerticalText) + m_currentTextFragment.transform.scaleNonUniform(1, m_textPathScaling); + else + m_currentTextFragment.transform.scaleNonUniform(m_textPathScaling, 1); + } + } + + // Update current text position, after processing of the current character finished. + if (m_inPathLayout) + updateCurrentTextPosition(x, y, glyphAdvance); + else { + // Apply CSS 'kerning', 'letter-spacing' and 'word-spacing' to next character, if needed. + if (spacing) + applySpacingToNextCharacter = true; + + float xNew = x - m_dx; + float yNew = y - m_dy; + + if (m_isVerticalText) + xNew -= baselineShift; + else + yNew += baselineShift; + + updateCurrentTextPosition(xNew, yNew, glyphAdvance + spacing); + } + + positionListOffset += metrics.length(); + lastAngle = angle; + } + + if (!didStartTextFragment) + return; + + // Close last open fragment, if needed. + recordTextFragment(textBox, text, positionListOffset, textMetricsValues.at(metricsListOffset - 1)); +} + +} + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/svg/SVGTextLayoutEngine.h b/WebCore/rendering/svg/SVGTextLayoutEngine.h new file mode 100644 index 0000000..ad058d8 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextLayoutEngine.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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 SVGTextLayoutEngine_h +#define SVGTextLayoutEngine_h + +#if ENABLE(SVG) +#include "Path.h" +#include "SVGTextChunkBuilder.h" +#include "SVGTextFragment.h" +#include "SVGTextMetrics.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class RenderObject; +class RenderStyle; +class RenderSVGInlineText; +class SVGElement; +class SVGInlineTextBox; +class SVGRenderStyle; + +// SVGTextLayoutEngine performs the second layout phase for SVG text. +// +// The InlineBox tree was created, containing the text chunk information, necessary to apply +// certain SVG specific text layout properties (text-length adjustments and text-anchor). +// The second layout phase uses the SVGTextLayoutAttributes stored in the individual +// RenderSVGInlineText renderers to compute the final positions for each character +// which are stored in the SVGInlineTextBox objects. + +class SVGTextLayoutEngine : public Noncopyable { +public: + SVGTextLayoutEngine(); + SVGTextChunkBuilder& chunkLayoutBuilder() { return m_chunkLayoutBuilder; } + + void beginTextPathLayout(RenderObject*, SVGTextLayoutEngine& lineLayout); + void endTextPathLayout(); + + void layoutInlineTextBox(SVGInlineTextBox*); + void finishLayout(); + +private: + void updateCharacerPositionIfNeeded(float& x, float& y); + void updateCurrentTextPosition(float x, float y, float glyphAdvance); + void updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues, unsigned valueListPosition); + + void recordTextFragment(SVGInlineTextBox*, RenderSVGInlineText*, unsigned positionListOffset, const SVGTextMetrics& lastCharacterMetrics); + bool parentDefinesTextLength(RenderObject*) const; + + void layoutTextOnLineOrPath(SVGInlineTextBox*, RenderSVGInlineText*, const RenderStyle*); + void finalizeTransformMatrices(Vector<SVGInlineTextBox*>&); + +private: + Vector<SVGInlineTextBox*> m_lineLayoutBoxes; + Vector<SVGInlineTextBox*> m_pathLayoutBoxes; + SVGTextChunkBuilder m_chunkLayoutBuilder; + + SVGTextFragment m_currentTextFragment; + float m_x; + float m_y; + float m_dx; + float m_dy; + bool m_isVerticalText; + bool m_inPathLayout; + + // Text on path layout + Path m_textPath; + float m_textPathLength; + float m_textPathStartOffset; + float m_textPathCurrentOffset; + float m_textPathSpacing; + float m_textPathScaling; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif diff --git a/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.cpp b/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.cpp new file mode 100644 index 0000000..7060ac6 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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. + */ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGTextLayoutEngineBaseline.h" + +#include "Font.h" +#include "RenderObject.h" +#include "SVGRenderStyle.h" +#include "SVGTextMetrics.h" +#include "UnicodeRange.h" + +namespace WebCore { + +SVGTextLayoutEngineBaseline::SVGTextLayoutEngineBaseline(const Font& font) + : m_font(font) +{ +} + +float SVGTextLayoutEngineBaseline::calculateBaselineShift(const SVGRenderStyle* style, SVGElement* lengthContext) const +{ + if (style->baselineShift() == BS_LENGTH) { + SVGLength baselineShiftValueLength = style->baselineShiftValue(); + if (baselineShiftValueLength.unitType() == LengthTypePercentage) + return baselineShiftValueLength.valueAsPercentage() * m_font.pixelSize(); + + return baselineShiftValueLength.value(lengthContext); + } + + switch (style->baselineShift()) { + case BS_BASELINE: + return 0; + case BS_SUB: + return -m_font.height() / 2; + case BS_SUPER: + return m_font.height() / 2; + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +EAlignmentBaseline SVGTextLayoutEngineBaseline::dominantBaselineToAlignmentBaseline(bool isVerticalText, const RenderObject* textRenderer) const +{ + ASSERT(textRenderer); + ASSERT(textRenderer->style()); + ASSERT(textRenderer->parent()); + ASSERT(textRenderer->parent()->style()); + + const SVGRenderStyle* style = textRenderer->style()->svgStyle(); + ASSERT(style); + + EDominantBaseline baseline = style->dominantBaseline(); + if (baseline == DB_AUTO) { + if (isVerticalText) + baseline = DB_CENTRAL; + else + baseline = DB_ALPHABETIC; + } + + switch (baseline) { + case DB_USE_SCRIPT: + // FIXME: The dominant-baseline and the baseline-table components are set by determining the predominant script of the character data content. + return AB_ALPHABETIC; + case DB_NO_CHANGE: + return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent()); + case DB_RESET_SIZE: + return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent()); + case DB_IDEOGRAPHIC: + return AB_IDEOGRAPHIC; + case DB_ALPHABETIC: + return AB_ALPHABETIC; + case DB_HANGING: + return AB_HANGING; + case DB_MATHEMATICAL: + return AB_MATHEMATICAL; + case DB_CENTRAL: + return AB_CENTRAL; + case DB_MIDDLE: + return AB_MIDDLE; + case DB_TEXT_AFTER_EDGE: + return AB_TEXT_AFTER_EDGE; + case DB_TEXT_BEFORE_EDGE: + return AB_TEXT_BEFORE_EDGE; + default: + ASSERT_NOT_REACHED(); + return AB_AUTO; + } +} + +float SVGTextLayoutEngineBaseline::calculateAlignmentBaselineShift(bool isVerticalText, const RenderObject* textRenderer) const +{ + ASSERT(textRenderer); + ASSERT(textRenderer->style()); + ASSERT(textRenderer->style()->svgStyle()); + ASSERT(textRenderer->parent()); + + const RenderObject* textRendererParent = textRenderer->parent(); + ASSERT(textRendererParent); + + EAlignmentBaseline baseline = textRenderer->style()->svgStyle()->alignmentBaseline(); + if (baseline == AB_AUTO) { + baseline = dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent); + ASSERT(baseline != AB_AUTO); + } + + // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling + switch (baseline) { + case AB_BASELINE: + return dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent); + case AB_BEFORE_EDGE: + case AB_TEXT_BEFORE_EDGE: + return m_font.ascent(); + case AB_MIDDLE: + return m_font.xHeight() / 2; + case AB_CENTRAL: + return (m_font.ascent() - m_font.descent()) / 2; + case AB_AFTER_EDGE: + case AB_TEXT_AFTER_EDGE: + case AB_IDEOGRAPHIC: + return m_font.descent(); + case AB_ALPHABETIC: + return 0; + case AB_HANGING: + return m_font.ascent() * 8 / 10.f; + case AB_MATHEMATICAL: + return m_font.ascent() / 2; + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +float SVGTextLayoutEngineBaseline::calculateGlyphOrientationAngle(bool isVerticalText, const SVGRenderStyle* style, const UChar& character) const +{ + ASSERT(style); + + switch (isVerticalText ? style->glyphOrientationVertical() : style->glyphOrientationHorizontal()) { + case GO_AUTO: + { + // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees. + // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees. + unsigned int unicodeRange = findCharUnicodeRange(character); + if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic) + return 90; + + return 0; + } + case GO_90DEG: + return 90; + case GO_180DEG: + return 180; + case GO_270DEG: + return 270; + case GO_0DEG: + default: + return 0; + } +} + +static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle) +{ + return !fabsf(fmodf(orientationAngle, 180)); +} + +float SVGTextLayoutEngineBaseline::calculateGlyphAdvanceAndOrientation(bool isVerticalText, SVGTextMetrics& metrics, float angle, float& xOrientationShift, float& yOrientationShift) const +{ + bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(angle); + + // The function is based on spec requirements: + // + // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of + // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph. + // + // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of + // 180 degrees, then the current text position is incremented according to the horizontal metrics of the glyph. + + // Vertical orientation handling. + if (isVerticalText) { + float ascentMinusDescent = m_font.ascent() - m_font.descent(); + if (!angle) { + xOrientationShift = (ascentMinusDescent - metrics.width()) / 2; + yOrientationShift = m_font.ascent(); + } else if (angle == 180) + xOrientationShift = (ascentMinusDescent + metrics.width()) / 2; + else if (angle == 270) { + yOrientationShift = metrics.width(); + xOrientationShift = ascentMinusDescent; + } + + // Vertical advance calculation. + if (angle && !orientationIsMultiplyOf180Degrees) + return metrics.width(); + + return metrics.height(); + } + + // Horizontal orientation handling. + if (angle == 90) + yOrientationShift = -metrics.width(); + else if (angle == 180) { + xOrientationShift = metrics.width(); + yOrientationShift = -m_font.ascent(); + } else if (angle == 270) + xOrientationShift = metrics.width(); + + // Horizontal advance calculation. + if (angle && !orientationIsMultiplyOf180Degrees) + return metrics.height(); + + return metrics.width(); +} + +} + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.h b/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.h new file mode 100644 index 0000000..d753b39 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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 SVGTextLayoutEngineBaseline_h +#define SVGTextLayoutEngineBaseline_h + +#if ENABLE(SVG) +#include "SVGRenderStyleDefs.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class Font; +class RenderObject; +class SVGElement; +class SVGRenderStyle; +class SVGTextMetrics; + +// Helper class used by SVGTextLayoutEngine to handle 'alignment-baseline' / 'dominant-baseline' and 'baseline-shift'. +class SVGTextLayoutEngineBaseline : public Noncopyable { +public: + SVGTextLayoutEngineBaseline(const Font&); + + float calculateBaselineShift(const SVGRenderStyle*, SVGElement* lengthContext) const; + float calculateAlignmentBaselineShift(bool isVerticalText, const RenderObject* textRenderer) const; + float calculateGlyphOrientationAngle(bool isVerticalText, const SVGRenderStyle*, const UChar& character) const; + float calculateGlyphAdvanceAndOrientation(bool isVerticalText, SVGTextMetrics&, float angle, float& xOrientationShift, float& yOrientationShift) const; + +private: + EAlignmentBaseline dominantBaselineToAlignmentBaseline(bool isVerticalText, const RenderObject* textRenderer) const; + + const Font& m_font; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif diff --git a/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp b/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp new file mode 100644 index 0000000..6c54b67 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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. + */ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGTextLayoutEngineSpacing.h" + +#include "Font.h" +#include "SVGRenderStyle.h" + +#if ENABLE(SVG_FONTS) +#include "SVGFontElement.h" +#endif + +namespace WebCore { + +SVGTextLayoutEngineSpacing::SVGTextLayoutEngineSpacing(const Font& font) + : m_font(font) + , m_lastCharacter(0) +{ +} + +float SVGTextLayoutEngineSpacing::calculateSVGKerning(bool isVerticalText, const SVGTextMetrics::Glyph& currentGlyph) +{ +#if ENABLE(SVG_FONTS) + if (!m_font.isSVGFont()) { + m_lastGlyph.isValid = false; + return 0; + } + + SVGFontElement* svgFont = m_font.svgFont(); + ASSERT(svgFont); + + float kerning = 0; + if (m_lastGlyph.isValid) { + if (isVerticalText) + kerning = svgFont->verticalKerningForPairOfStringsAndGlyphs(m_lastGlyph.unicodeString, m_lastGlyph.name, currentGlyph.unicodeString, currentGlyph.name); + else + kerning = svgFont->horizontalKerningForPairOfStringsAndGlyphs(m_lastGlyph.unicodeString, m_lastGlyph.name, currentGlyph.unicodeString, currentGlyph.name); + } + + m_lastGlyph = currentGlyph; + m_lastGlyph.isValid = true; + kerning *= m_font.size() / m_font.primaryFont()->unitsPerEm(); + return kerning; +#else + UNUSED_PARAM(isVerticalText); + UNUSED_PARAM(currentGlyph); + return false; +#endif +} + +float SVGTextLayoutEngineSpacing::calculateCSSKerningAndSpacing(const SVGRenderStyle* style, SVGElement* lengthContext, const UChar* currentCharacter) +{ + float kerning = 0; + SVGLength kerningLength = style->kerning(); + if (kerningLength.unitType() == LengthTypePercentage) + kerning = kerningLength.valueAsPercentage() * m_font.pixelSize(); + else + kerning = kerningLength.value(lengthContext); + + const UChar* lastCharacter = m_lastCharacter; + m_lastCharacter = currentCharacter; + + if (!kerning && !m_font.letterSpacing() && !m_font.wordSpacing()) + return 0; + + float spacing = m_font.letterSpacing() + kerning; + if (currentCharacter && lastCharacter && m_font.wordSpacing()) { + if (Font::treatAsSpace(*currentCharacter) && !Font::treatAsSpace(*lastCharacter)) + spacing += m_font.wordSpacing(); + } + + return spacing; +} + +} + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGCharacterData.cpp b/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.h index 394a303..0a6d736 100644 --- a/WebCore/rendering/SVGCharacterData.cpp +++ b/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -16,40 +15,38 @@ * 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 "SVGCharacterData.h" +#ifndef SVGTextLayoutEngineSpacing_h +#define SVGTextLayoutEngineSpacing_h #if ENABLE(SVG) -#include "AffineTransform.h" +#include "SVGTextMetrics.h" namespace WebCore { -bool SVGChar::isHidden() const -{ - return pathData && pathData->hidden; -} +class Font; +class SVGRenderStyle; +class SVGElement; -AffineTransform SVGChar::characterTransform() const -{ - AffineTransform ctm; +// Helper class used by SVGTextLayoutEngine to handle 'kerning' / 'letter-spacing' and 'word-spacing'. +class SVGTextLayoutEngineSpacing : public Noncopyable { +public: + SVGTextLayoutEngineSpacing(const Font&); - // Rotate character around angle, and possibly scale. - ctm.translate(x, y); - ctm.rotate(angle); + float calculateSVGKerning(bool isVerticalText, const SVGTextMetrics::Glyph& currentGlyph); + float calculateCSSKerningAndSpacing(const SVGRenderStyle*, SVGElement* lengthContext, const UChar* currentCharacter); - if (pathData) { - ctm.scaleNonUniform(pathData->xScale, pathData->yScale); - ctm.translate(pathData->xShift, pathData->yShift); - ctm.rotate(pathData->orientationAngle); - } +private: + const Font& m_font; + const UChar* m_lastCharacter; - ctm.translate(orientationShiftX - x, orientationShiftY - y); - return ctm; -} +#if ENABLE(SVG_FONTS) + SVGTextMetrics::Glyph m_lastGlyph; +#endif +}; } // namespace WebCore #endif // ENABLE(SVG) +#endif diff --git a/WebCore/rendering/svg/SVGTextMetrics.cpp b/WebCore/rendering/svg/SVGTextMetrics.cpp new file mode 100644 index 0000000..58d0ad9 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextMetrics.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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. + */ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGTextMetrics.h" + +#include "RenderSVGInlineText.h" + +namespace WebCore { + +SVGTextMetrics::SVGTextMetrics() + : m_width(0) + , m_height(0) + , m_length(0) +{ +} + +SVGTextMetrics::SVGTextMetrics(const Font& font, const TextRun& run, unsigned position, unsigned textLength) + : m_width(0) + , m_height(0) + , m_length(0) +{ + int extraCharsAvailable = textLength - (position + run.length()); + int length = 0; + + m_width = font.floatWidth(run, extraCharsAvailable, length, m_glyph.name); + m_height = font.height(); + m_glyph.unicodeString = String(run.characters(), length); + m_glyph.isValid = true; + + ASSERT(length >= 0); + m_length = static_cast<unsigned>(length); +} + +bool SVGTextMetrics::operator==(const SVGTextMetrics& other) +{ + return m_width == other.m_width + && m_height == other.m_height + && m_length == other.m_length + && m_glyph == other.m_glyph; +} + +SVGTextMetrics SVGTextMetrics::emptyMetrics() +{ + DEFINE_STATIC_LOCAL(SVGTextMetrics, s_emptyMetrics, ()); + s_emptyMetrics.m_length = 1; + return s_emptyMetrics; +} + +static TextRun constructTextRun(RenderSVGInlineText* text, const UChar* characters, unsigned position, unsigned length) +{ + TextRun run(characters + position, length); + +#if ENABLE(SVG_FONTS) + ASSERT(text->parent()); + run.setReferencingRenderObject(text->parent()); +#endif + + // Disable any word/character rounding. + run.disableRoundingHacks(); + + // We handle letter & word spacing ourselves. + run.disableSpacing(); + return run; +} + +SVGTextMetrics SVGTextMetrics::measureCharacterRange(RenderSVGInlineText* text, unsigned position, unsigned length) +{ + ASSERT(text); + ASSERT(text->style()); + + TextRun run(constructTextRun(text, text->characters(), position, length)); + return SVGTextMetrics(text->style()->font(), run, position, text->textLength()); +} + +void SVGTextMetrics::measureAllCharactersIndividually(RenderSVGInlineText* text, Vector<SVGTextMetrics>& allMetrics) +{ + ASSERT(text); + ASSERT(text->style()); + + const Font& font = text->style()->font(); + const UChar* characters = text->characters(); + unsigned length = text->textLength(); + + TextRun run(constructTextRun(text, 0, 0, 0)); + for (unsigned position = 0; position < length; ) { + run.setText(characters + position, 1); + + SVGTextMetrics metrics(font, run, position, text->textLength()); + allMetrics.append(metrics); + position += metrics.length(); + } +} + +} + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/svg/SVGTextMetrics.h b/WebCore/rendering/svg/SVGTextMetrics.h new file mode 100644 index 0000000..ba18589 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextMetrics.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) Research In Motion Limited 2010. 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 SVGTextMetrics_h +#define SVGTextMetrics_h + +#if ENABLE(SVG) +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class Font; +class RenderSVGInlineText; +class TextRun; + +class SVGTextMetrics { +public: + static SVGTextMetrics emptyMetrics(); + static SVGTextMetrics measureCharacterRange(RenderSVGInlineText*, unsigned position, unsigned length); + static void measureAllCharactersIndividually(RenderSVGInlineText*, Vector<SVGTextMetrics>&); + + bool operator==(const SVGTextMetrics&); + + float width() const { return m_width; } + float height() const { return m_height; } + unsigned length() const { return m_length; } + + struct Glyph { + Glyph() + : isValid(false) + { + } + + bool operator==(const Glyph& other) + { + return isValid == other.isValid + && name == other.name + && unicodeString == other.unicodeString; + } + + bool isValid; + String name; + String unicodeString; + }; + + // Only useful when measuring individual characters, to lookup ligatures. + const Glyph& glyph() const { return m_glyph; } + +private: + SVGTextMetrics(); + SVGTextMetrics(const Font&, const TextRun&, unsigned position, unsigned textLength); + + float m_width; + float m_height; + unsigned m_length; + Glyph m_glyph; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif diff --git a/WebCore/rendering/svg/SVGTextQuery.cpp b/WebCore/rendering/svg/SVGTextQuery.cpp new file mode 100644 index 0000000..fcc7924 --- /dev/null +++ b/WebCore/rendering/svg/SVGTextQuery.cpp @@ -0,0 +1,562 @@ +/* + Copyright (C) Research In Motion Limited 2010. 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. +*/ + +#include "config.h" +#include "SVGTextQuery.h" + +#if ENABLE(SVG) +#include "FloatConversion.h" +#include "InlineFlowBox.h" +#include "RenderBlock.h" +#include "RenderInline.h" +#include "RenderSVGInlineText.h" +#include "SVGInlineTextBox.h" +#include "SVGTextMetrics.h" +#include "VisiblePosition.h" + +#include <wtf/MathExtras.h> + +namespace WebCore { + +// Base structure for callback user data +struct SVGTextQuery::Data { + Data() + : isVerticalText(false) + , processedCharacters(0) + , textRenderer(0) + , textBox(0) + { + } + + bool isVerticalText; + unsigned processedCharacters; + RenderSVGInlineText* textRenderer; + const SVGInlineTextBox* textBox; +}; + +static inline InlineFlowBox* flowBoxForRenderer(RenderObject* renderer) +{ + if (!renderer) + return 0; + + if (renderer->isRenderBlock()) { + // If we're given a block element, it has to be a RenderSVGText. + ASSERT(renderer->isSVGText()); + RenderBlock* renderBlock = toRenderBlock(renderer); + + // RenderSVGText only ever contains a single line box. + InlineFlowBox* flowBox = renderBlock->firstLineBox(); + ASSERT(flowBox == renderBlock->lastLineBox()); + return flowBox; + } + + if (renderer->isRenderInline()) { + // We're given a RenderSVGInline or objects that derive from it (RenderSVGTSpan / RenderSVGTextPath) + RenderInline* renderInline = toRenderInline(renderer); + + // RenderSVGInline only ever contains a single line box. + InlineFlowBox* flowBox = renderInline->firstLineBox(); + ASSERT(flowBox == renderInline->lastLineBox()); + return flowBox; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +static inline float mapLengthThroughFragmentTransformation(const SVGTextFragment& fragment, bool isVerticalText, float length) +{ + if (fragment.transform.isIdentity()) + return length; + + if (isVerticalText) + return narrowPrecisionToFloat(static_cast<double>(length) * fragment.transform.yScale()); + + return narrowPrecisionToFloat(static_cast<double>(length) * fragment.transform.xScale()); +} + +SVGTextQuery::SVGTextQuery(RenderObject* renderer) +{ + collectTextBoxesInFlowBox(flowBoxForRenderer(renderer)); +} + +void SVGTextQuery::collectTextBoxesInFlowBox(InlineFlowBox* flowBox) +{ + if (!flowBox) + return; + + for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnLine()) { + if (child->isInlineFlowBox()) { + // Skip generated content. + if (!child->renderer()->node()) + continue; + + collectTextBoxesInFlowBox(static_cast<InlineFlowBox*>(child)); + continue; + } + + ASSERT(child->isSVGInlineTextBox()); + m_textBoxes.append(static_cast<SVGInlineTextBox*>(child)); + } +} + +bool SVGTextQuery::executeQuery(Data* queryData, ProcessTextFragmentCallback fragmentCallback) const +{ + ASSERT(!m_textBoxes.isEmpty()); + + unsigned processedCharacters = 0; + unsigned textBoxCount = m_textBoxes.size(); + + // Loop over all text boxes + for (unsigned textBoxPosition = 0; textBoxPosition < textBoxCount; ++textBoxPosition) { + queryData->textBox = m_textBoxes.at(textBoxPosition); + queryData->textRenderer = toRenderSVGInlineText(queryData->textBox->textRenderer()); + ASSERT(queryData->textRenderer); + ASSERT(queryData->textRenderer->style()); + ASSERT(queryData->textRenderer->style()->svgStyle()); + + queryData->isVerticalText = queryData->textRenderer->style()->svgStyle()->isVerticalWritingMode(); + const Vector<SVGTextFragment>& fragments = queryData->textBox->textFragments(); + + // Loop over all text fragments in this text box, firing a callback for each. + unsigned fragmentCount = fragments.size(); + for (unsigned i = 0; i < fragmentCount; ++i) { + const SVGTextFragment& fragment = fragments.at(i); + if ((this->*fragmentCallback)(queryData, fragment)) + return true; + + processedCharacters += fragment.length; + } + + queryData->processedCharacters = processedCharacters; + } + + return false; +} + +bool SVGTextQuery::mapStartEndPositionsIntoFragmentCoordinates(Data* queryData, const SVGTextFragment& fragment, int& startPosition, int& endPosition) const +{ + // Reuse the same logic used for text selection & painting, to map our query start/length into start/endPositions of the current text fragment. + startPosition -= queryData->processedCharacters; + endPosition -= queryData->processedCharacters; + + if (startPosition >= endPosition || startPosition < 0 || endPosition < 0) + return false; + + modifyStartEndPositionsRespectingLigatures(queryData, startPosition, endPosition); + if (!queryData->textBox->mapStartEndPositionsIntoFragmentCoordinates(fragment, startPosition, endPosition)) + return false; + + ASSERT(startPosition < endPosition); + return true; +} + +void SVGTextQuery::modifyStartEndPositionsRespectingLigatures(Data* queryData, int& startPosition, int& endPosition) const +{ + const SVGTextLayoutAttributes& layoutAttributes = queryData->textRenderer->layoutAttributes(); + const Vector<float>& xValues = layoutAttributes.xValues(); + const Vector<SVGTextMetrics>& textMetricsValues = layoutAttributes.textMetricsValues(); + + unsigned boxStart = queryData->textBox->start(); + unsigned boxLength = queryData->textBox->len(); + + unsigned textMetricsOffset = 0; + unsigned textMetricsSize = textMetricsValues.size(); + + unsigned positionOffset = 0; + unsigned positionSize = xValues.size(); + + bool alterStartPosition = true; + bool alterEndPosition = true; + + int lastPositionOffset = -1; + for (; textMetricsOffset < textMetricsSize && positionOffset < positionSize; ++textMetricsOffset) { + const SVGTextMetrics& metrics = textMetricsValues.at(textMetricsOffset); + + // Advance to text box start location. + if (positionOffset < boxStart) { + positionOffset += metrics.length(); + continue; + } + + // Stop if we've finished processing this text box. + if (positionOffset >= boxStart + boxLength) + break; + + // If the start position maps to a character in the metrics list, we don't need to modify it. + if (startPosition == static_cast<int>(positionOffset)) + alterStartPosition = false; + + // If the start position maps to a character in the metrics list, we don't need to modify it. + if (endPosition == static_cast<int>(positionOffset)) + alterEndPosition = false; + + // Detect ligatures. + if (lastPositionOffset != -1 && lastPositionOffset - positionOffset > 1) { + if (alterStartPosition && startPosition > lastPositionOffset && startPosition < static_cast<int>(positionOffset)) { + startPosition = lastPositionOffset; + alterStartPosition = false; + } + + if (alterEndPosition && endPosition > lastPositionOffset && endPosition < static_cast<int>(positionOffset)) { + endPosition = positionOffset; + alterEndPosition = false; + } + } + + if (!alterStartPosition && !alterEndPosition) + break; + + lastPositionOffset = positionOffset; + positionOffset += metrics.length(); + } + + if (!alterStartPosition && !alterEndPosition) + return; + + if (lastPositionOffset != -1 && lastPositionOffset - positionOffset > 1) { + if (alterStartPosition && startPosition > lastPositionOffset && startPosition < static_cast<int>(positionOffset)) { + startPosition = lastPositionOffset; + alterStartPosition = false; + } + + if (alterEndPosition && endPosition > lastPositionOffset && endPosition < static_cast<int>(positionOffset)) { + endPosition = positionOffset; + alterEndPosition = false; + } + } +} + +// numberOfCharacters() implementation +bool SVGTextQuery::numberOfCharactersCallback(Data*, const SVGTextFragment&) const +{ + // no-op + return false; +} + +unsigned SVGTextQuery::numberOfCharacters() const +{ + if (m_textBoxes.isEmpty()) + return 0; + + Data data; + executeQuery(&data, &SVGTextQuery::numberOfCharactersCallback); + return data.processedCharacters; +} + +// textLength() implementation +struct TextLengthData : SVGTextQuery::Data { + TextLengthData() + : textLength(0) + { + } + + float textLength; +}; + +bool SVGTextQuery::textLengthCallback(Data* queryData, const SVGTextFragment& fragment) const +{ + TextLengthData* data = static_cast<TextLengthData*>(queryData); + + float fragmentLength = queryData->isVerticalText ? fragment.height : fragment.width; + data->textLength += mapLengthThroughFragmentTransformation(fragment, queryData->isVerticalText, fragmentLength); + return false; +} + +float SVGTextQuery::textLength() const +{ + if (m_textBoxes.isEmpty()) + return 0; + + TextLengthData data; + executeQuery(&data, &SVGTextQuery::textLengthCallback); + return data.textLength; +} + +// subStringLength() implementation +struct SubStringLengthData : SVGTextQuery::Data { + SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) + : startPosition(queryStartPosition) + , length(queryLength) + , subStringLength(0) + { + } + + unsigned startPosition; + unsigned length; + + float subStringLength; +}; + +bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGTextFragment& fragment) const +{ + SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData); + + int startPosition = data->startPosition; + int endPosition = startPosition + data->length; + if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) + return false; + + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset + startPosition, endPosition - startPosition); + float fragmentLength = queryData->isVerticalText ? metrics.height() : metrics.width(); + + data->subStringLength += mapLengthThroughFragmentTransformation(fragment, queryData->isVerticalText, fragmentLength); + return false; +} + +float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) const +{ + if (m_textBoxes.isEmpty()) + return 0; + + SubStringLengthData data(startPosition, length); + executeQuery(&data, &SVGTextQuery::subStringLengthCallback); + return data.subStringLength; +} + +// startPositionOfCharacter() implementation +struct StartPositionOfCharacterData : SVGTextQuery::Data { + StartPositionOfCharacterData(unsigned queryPosition) + : position(queryPosition) + { + } + + unsigned position; + FloatPoint startPosition; +}; + +bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const +{ + StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterData*>(queryData); + + int startPosition = data->position; + int endPosition = startPosition + 1; + if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) + return false; + + data->startPosition = FloatPoint(fragment.x, fragment.y); + + if (startPosition) { + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition); + if (queryData->isVerticalText) + data->startPosition.move(0, metrics.height()); + else + data->startPosition.move(metrics.width(), 0); + } + + if (fragment.transform.isIdentity()) + return true; + + data->startPosition = fragment.transform.mapPoint(data->startPosition); + return true; +} + +FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const +{ + if (m_textBoxes.isEmpty()) + return FloatPoint(); + + StartPositionOfCharacterData data(position); + executeQuery(&data, &SVGTextQuery::startPositionOfCharacterCallback); + return data.startPosition; +} + +// endPositionOfCharacter() implementation +struct EndPositionOfCharacterData : SVGTextQuery::Data { + EndPositionOfCharacterData(unsigned queryPosition) + : position(queryPosition) + { + } + + unsigned position; + FloatPoint endPosition; +}; + +bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const +{ + EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(queryData); + + int startPosition = data->position; + int endPosition = startPosition + 1; + if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) + return false; + + data->endPosition = FloatPoint(fragment.x, fragment.y); + + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition + 1); + if (queryData->isVerticalText) + data->endPosition.move(0, metrics.height()); + else + data->endPosition.move(metrics.width(), 0); + + if (fragment.transform.isIdentity()) + return true; + + data->endPosition = fragment.transform.mapPoint(data->endPosition); + return true; +} + +FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const +{ + if (m_textBoxes.isEmpty()) + return FloatPoint(); + + EndPositionOfCharacterData data(position); + executeQuery(&data, &SVGTextQuery::endPositionOfCharacterCallback); + return data.endPosition; +} + +// rotationOfCharacter() implementation +struct RotationOfCharacterData : SVGTextQuery::Data { + RotationOfCharacterData(unsigned queryPosition) + : position(queryPosition) + , rotation(0) + { + } + + unsigned position; + float rotation; +}; + +bool SVGTextQuery::rotationOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const +{ + RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryData); + + int startPosition = data->position; + int endPosition = startPosition + 1; + if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) + return false; + + AffineTransform newTransform(fragment.transform); + newTransform.scale(1 / fragment.transform.xScale(), 1 / fragment.transform.yScale()); + data->rotation = narrowPrecisionToFloat(rad2deg(atan2(newTransform.b(), newTransform.a()))); + return true; +} + +float SVGTextQuery::rotationOfCharacter(unsigned position) const +{ + if (m_textBoxes.isEmpty()) + return 0; + + RotationOfCharacterData data(position); + executeQuery(&data, &SVGTextQuery::rotationOfCharacterCallback); + return data.rotation; +} + +// extentOfCharacter() implementation +struct ExtentOfCharacterData : SVGTextQuery::Data { + ExtentOfCharacterData(unsigned queryPosition) + : position(queryPosition) + { + } + + unsigned position; + FloatRect extent; +}; + +static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const SVGTextFragment& fragment, int startPosition, FloatRect& extent) +{ + extent.setLocation(FloatPoint(fragment.x, fragment.y - queryData->textRenderer->style()->font().ascent())); + + if (startPosition) { + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition); + if (queryData->isVerticalText) + extent.move(0, metrics.height()); + else + extent.move(metrics.width(), 0); + } + + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset + startPosition, 1); + extent.setSize(FloatSize(metrics.width(), metrics.height())); + + if (fragment.transform.isIdentity()) + return; + + extent = fragment.transform.mapRect(extent); +} + +bool SVGTextQuery::extentOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const +{ + ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData); + + int startPosition = data->position; + int endPosition = startPosition + 1; + if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) + return false; + + calculateGlyphBoundaries(queryData, fragment, startPosition, data->extent); + return true; +} + +FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const +{ + if (m_textBoxes.isEmpty()) + return FloatRect(); + + ExtentOfCharacterData data(position); + executeQuery(&data, &SVGTextQuery::extentOfCharacterCallback); + return data.extent; +} + +// characterNumberAtPosition() implementation +struct CharacterNumberAtPositionData : SVGTextQuery::Data { + CharacterNumberAtPositionData(const FloatPoint& queryPosition) + : position(queryPosition) + { + } + + FloatPoint position; +}; + +bool SVGTextQuery::characterNumberAtPositionCallback(Data* queryData, const SVGTextFragment& fragment) const +{ + CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionData*>(queryData); + + FloatRect extent; + for (unsigned i = 0; i < fragment.length; ++i) { + int startPosition = data->processedCharacters + i; + int endPosition = startPosition + 1; + if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) + continue; + + calculateGlyphBoundaries(queryData, fragment, startPosition, extent); + if (extent.contains(data->position)) { + data->processedCharacters += i; + return true; + } + } + + return false; +} + +int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const +{ + if (m_textBoxes.isEmpty()) + return -1; + + CharacterNumberAtPositionData data(position); + if (!executeQuery(&data, &SVGTextQuery::characterNumberAtPositionCallback)) + return -1; + + return data.processedCharacters; +} + +} + +#endif diff --git a/WebCore/rendering/SVGTextQuery.h b/WebCore/rendering/svg/SVGTextQuery.h index f19c72e..9a671f4 100644 --- a/WebCore/rendering/SVGTextQuery.h +++ b/WebCore/rendering/svg/SVGTextQuery.h @@ -22,15 +22,14 @@ #if ENABLE(SVG) #include "FloatRect.h" +#include "SVGTextFragment.h" #include <wtf/Vector.h> namespace WebCore { class InlineFlowBox; class RenderObject; -class RenderStyle; class SVGInlineTextBox; -struct SVGTextChunkPart; class SVGTextQuery { public: @@ -49,22 +48,22 @@ public: struct Data; private: - typedef bool (SVGTextQuery::*ProcessTextChunkPartCallback)(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; - bool executeQuery(Data*, ProcessTextChunkPartCallback) const; + typedef bool (SVGTextQuery::*ProcessTextFragmentCallback)(Data*, const SVGTextFragment&) const; + bool executeQuery(Data*, ProcessTextFragmentCallback) const; void collectTextBoxesInFlowBox(InlineFlowBox*); - float measureCharacterRange(const SVGInlineTextBox*, RenderStyle*, bool isVerticalText, int startPosition, int length) const; - bool mapStartAndLengthIntoChunkPartCoordinates(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&, int& startPosition, int& endPosition) const; + bool mapStartEndPositionsIntoFragmentCoordinates(Data*, const SVGTextFragment&, int& startPosition, int& endPosition) const; + void modifyStartEndPositionsRespectingLigatures(Data*, int& startPosition, int& endPosition) const; private: - bool numberOfCharactersCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; - bool textLengthCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; - bool subStringLengthCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; - bool startPositionOfCharacterCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; - bool endPositionOfCharacterCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; - bool rotationOfCharacterCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; - bool extentOfCharacterCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; - bool characterNumberAtPositionCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + bool numberOfCharactersCallback(Data*, const SVGTextFragment&) const; + bool textLengthCallback(Data*, const SVGTextFragment&) const; + bool subStringLengthCallback(Data*, const SVGTextFragment&) const; + bool startPositionOfCharacterCallback(Data*, const SVGTextFragment&) const; + bool endPositionOfCharacterCallback(Data*, const SVGTextFragment&) const; + bool rotationOfCharacterCallback(Data*, const SVGTextFragment&) const; + bool extentOfCharacterCallback(Data*, const SVGTextFragment&) const; + bool characterNumberAtPositionCallback(Data*, const SVGTextFragment&) const; private: Vector<SVGInlineTextBox*> m_textBoxes; |