diff options
Diffstat (limited to 'Source/WebCore/rendering')
105 files changed, 3305 insertions, 1427 deletions
diff --git a/Source/WebCore/rendering/AutoTableLayout.cpp b/Source/WebCore/rendering/AutoTableLayout.cpp index 9775ec5..4eef95f 100644 --- a/Source/WebCore/rendering/AutoTableLayout.cpp +++ b/Source/WebCore/rendering/AutoTableLayout.cpp @@ -81,8 +81,8 @@ void AutoTableLayout::recalcColumn(int effCol) Length cellLogicalWidth = cell->styleOrColLogicalWidth(); // FIXME: What is this arbitrary value? - if (cellLogicalWidth.rawValue() > 32760) - cellLogicalWidth.setRawValue(32760); + if (cellLogicalWidth.value() > 32760) + cellLogicalWidth.setValue(32760); if (cellLogicalWidth.isNegative()) cellLogicalWidth.setValue(0); switch (cellLogicalWidth.type()) { @@ -105,13 +105,13 @@ void AutoTableLayout::recalcColumn(int effCol) break; case Percent: m_hasPercent = true; - if (cellLogicalWidth.isPositive() && (!columnLayout.logicalWidth.isPercent() || cellLogicalWidth.rawValue() > columnLayout.logicalWidth.rawValue())) + if (cellLogicalWidth.isPositive() && (!columnLayout.logicalWidth.isPercent() || cellLogicalWidth.value() > columnLayout.logicalWidth.value())) columnLayout.logicalWidth = cellLogicalWidth; break; case Relative: // FIXME: Need to understand this case and whether it makes sense to compare values // which are not necessarily of the same type. - if (cellLogicalWidth.isAuto() || (cellLogicalWidth.isRelative() && cellLogicalWidth.value() > columnLayout.logicalWidth.rawValue())) + if (cellLogicalWidth.isAuto() || (cellLogicalWidth.isRelative() && cellLogicalWidth.value() > columnLayout.logicalWidth.value())) columnLayout.logicalWidth = cellLogicalWidth; default: break; @@ -232,16 +232,16 @@ void AutoTableLayout::computePreferredLogicalWidths(int& minWidth, int& maxWidth // We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero. // FIXME: Handle the 0% cases properly. - const int epsilon = 1; + const float epsilon = 1 / 128.0f; - int remainingPercent = 100 * percentScaleFactor; + float remainingPercent = 100; for (size_t i = 0; i < m_layoutStruct.size(); ++i) { minWidth += m_layoutStruct[i].effectiveMinLogicalWidth; maxWidth += m_layoutStruct[i].effectiveMaxLogicalWidth; if (scaleColumns) { if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) { - int percent = min(m_layoutStruct[i].effectiveLogicalWidth.rawValue(), remainingPercent); - float logicalWidth = static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) * 100 * percentScaleFactor / max(percent, epsilon); + float percent = min(static_cast<float>(m_layoutStruct[i].effectiveLogicalWidth.percent()), remainingPercent); + float logicalWidth = static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) * 100 / max(percent, epsilon); maxPercent = max(logicalWidth, maxPercent); remainingPercent -= percent; } else @@ -250,7 +250,7 @@ void AutoTableLayout::computePreferredLogicalWidths(int& minWidth, int& maxWidth } if (scaleColumns) { - maxNonPercent = maxNonPercent * 100 * percentScaleFactor / max(remainingPercent, epsilon); + maxNonPercent = maxNonPercent * 100 / max(remainingPercent, epsilon); maxWidth = max(maxWidth, static_cast<int>(min(maxNonPercent, INT_MAX / 2.0f))); maxWidth = max(maxWidth, static_cast<int>(min(maxPercent, INT_MAX / 2.0f))); } @@ -266,6 +266,10 @@ void AutoTableLayout::computePreferredLogicalWidths(int& minWidth, int& maxWidth minWidth = max(minWidth, tableLogicalWidth.value()); maxWidth = minWidth; } + + // if there was no remaining percent, maxWidth is invalid. + if (!remainingPercent && maxNonPercent) + maxWidth = intMaxForLength; } /* @@ -300,7 +304,7 @@ int AutoTableLayout::calcEffectiveLogicalWidth() size_t lastCol = effCol; int cellMinLogicalWidth = cell->minPreferredLogicalWidth() + spacingInRowDirection; float cellMaxLogicalWidth = cell->maxPreferredLogicalWidth() + spacingInRowDirection; - int totalPercent = 0; + float totalPercent = 0; int spanMinLogicalWidth = 0; float spanMaxLogicalWidth = 0; bool allColsArePercent = true; @@ -312,7 +316,7 @@ int AutoTableLayout::calcEffectiveLogicalWidth() Layout& columnLayout = m_layoutStruct[lastCol]; switch (columnLayout.logicalWidth.type()) { case Percent: - totalPercent += columnLayout.logicalWidth.rawValue(); + totalPercent += columnLayout.logicalWidth.percent(); allColsAreFixed = false; break; case Fixed: @@ -339,7 +343,7 @@ int AutoTableLayout::calcEffectiveLogicalWidth() columnLayout.effectiveLogicalWidth = Length(); allColsArePercent = false; } else - totalPercent += columnLayout.effectiveLogicalWidth.rawValue(); + totalPercent += columnLayout.effectiveLogicalWidth.percent(); allColsAreFixed = false; } if (!columnLayout.emptyCellsOnly) @@ -354,14 +358,14 @@ int AutoTableLayout::calcEffectiveLogicalWidth() // adjust table max width if needed if (cellLogicalWidth.isPercent()) { - if (totalPercent > cellLogicalWidth.rawValue() || allColsArePercent) { + if (totalPercent > cellLogicalWidth.percent() || allColsArePercent) { // can't satify this condition, treat as variable cellLogicalWidth = Length(); } else { - maxLogicalWidth = max(maxLogicalWidth, max(spanMaxLogicalWidth, cellMaxLogicalWidth) * 100 * percentScaleFactor / cellLogicalWidth.rawValue()); + maxLogicalWidth = max(maxLogicalWidth, static_cast<float>(max(spanMaxLogicalWidth, cellMaxLogicalWidth) * 100 / cellLogicalWidth.percent())); // all non percent columns in the span get percent values to sum up correctly. - int percentMissing = cellLogicalWidth.rawValue() - totalPercent; + float percentMissing = cellLogicalWidth.percent() - totalPercent; float totalWidth = 0; for (unsigned pos = effCol; pos < lastCol; ++pos) { if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent()) @@ -370,11 +374,11 @@ int AutoTableLayout::calcEffectiveLogicalWidth() for (unsigned pos = effCol; pos < lastCol && totalWidth > 0; ++pos) { if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent()) { - int percent = static_cast<int>(percentMissing * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / totalWidth); + float percent = percentMissing * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / totalWidth; totalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth; percentMissing -= percent; if (percent > 0) - m_layoutStruct[pos].effectiveLogicalWidth.setRawValue(Percent, percent); + m_layoutStruct[pos].effectiveLogicalWidth.setValue(Percent, percent); else m_layoutStruct[pos].effectiveLogicalWidth = Length(); } @@ -495,7 +499,7 @@ void AutoTableLayout::layout() int numFixed = 0; float totalAuto = 0; float totalFixed = 0; - int totalPercent = 0; + float totalPercent = 0; int allocAuto = 0; unsigned numAutoEmptyCellsOnly = 0; @@ -508,7 +512,7 @@ void AutoTableLayout::layout() switch (logicalWidth.type()) { case Percent: havePercent = true; - totalPercent += logicalWidth.rawValue(); + totalPercent += logicalWidth.percent(); break; case Relative: totalRelative += logicalWidth.value(); @@ -542,9 +546,9 @@ void AutoTableLayout::layout() m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth; } } - if (totalPercent > 100 * percentScaleFactor) { + if (totalPercent > 100) { // remove overallocated space from the last columns - int excess = tableLogicalWidth * (totalPercent - 100 * percentScaleFactor) / (100 * percentScaleFactor); + int excess = tableLogicalWidth * (totalPercent - 100) / 100; for (int i = nEffCols - 1; i >= 0; --i) { if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) { int cellLogicalWidth = m_layoutStruct[i].computedLogicalWidth; @@ -611,13 +615,13 @@ void AutoTableLayout::layout() } // spread over percent colums - if (available > 0 && m_hasPercent && totalPercent < 100 * percentScaleFactor) { + if (available > 0 && m_hasPercent && totalPercent < 100) { for (size_t i = 0; i < nEffCols; ++i) { Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; if (logicalWidth.isPercent()) { - int cellLogicalWidth = available * logicalWidth.rawValue() / totalPercent; + int cellLogicalWidth = available * logicalWidth.percent() / totalPercent; available -= cellLogicalWidth; - totalPercent -= logicalWidth.rawValue(); + totalPercent -= logicalWidth.percent(); m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth; if (!available || !totalPercent) break; diff --git a/Source/WebCore/rendering/CounterNode.cpp b/Source/WebCore/rendering/CounterNode.cpp index eadd386..323f5db 100644 --- a/Source/WebCore/rendering/CounterNode.cpp +++ b/Source/WebCore/rendering/CounterNode.cpp @@ -32,7 +32,8 @@ CounterNode::CounterNode(RenderObject* o, bool hasResetType, int value) : m_hasResetType(hasResetType) , m_value(value) , m_countInParent(0) - , m_renderer(o) + , m_owner(o) + , m_rootRenderer(0) , m_parent(0) , m_previousSibling(0) , m_nextSibling(0) @@ -41,9 +42,14 @@ CounterNode::CounterNode(RenderObject* o, bool hasResetType, int value) { } -PassRefPtr<CounterNode> CounterNode::create(RenderObject* renderer, bool hasResetType, int value) +CounterNode::~CounterNode() { - return adoptRef(new CounterNode(renderer, hasResetType, value)); + resetRenderers(); +} + +PassRefPtr<CounterNode> CounterNode::create(RenderObject* owner, bool hasResetType, int value) +{ + return adoptRef(new CounterNode(owner, hasResetType, value)); } CounterNode* CounterNode::nextInPreOrderAfterChildren(const CounterNode* stayWithin) const @@ -102,24 +108,76 @@ int CounterNode::computeCountInParent() const return m_parent->m_value + increment; } -void CounterNode::resetRenderer(const AtomicString& identifier) const +void CounterNode::addRenderer(RenderCounter* value) +{ + if (!value) { + ASSERT_NOT_REACHED(); + return; + } + if (value->m_counterNode) { + ASSERT_NOT_REACHED(); + value->m_counterNode->removeRenderer(value); + } + ASSERT(!value->m_nextForSameCounter); + for (RenderCounter* iterator = m_rootRenderer;iterator; iterator = iterator->m_nextForSameCounter) { + if (iterator == value) { + ASSERT_NOT_REACHED(); + return; + } + } + value->m_nextForSameCounter = m_rootRenderer; + m_rootRenderer = value; + if (value->m_counterNode != this) { + if (value->m_counterNode) { + ASSERT_NOT_REACHED(); + value->m_counterNode->removeRenderer(value); + } + value->m_counterNode = this; + } +} + +void CounterNode::removeRenderer(RenderCounter* value) { - if (!m_renderer || m_renderer->documentBeingDestroyed()) + if (!value) { + ASSERT_NOT_REACHED(); return; - if (RenderObjectChildList* children = m_renderer->virtualChildren()) - children->invalidateCounters(m_renderer, identifier); + } + if (value->m_counterNode && value->m_counterNode != this) { + ASSERT_NOT_REACHED(); + value->m_counterNode->removeRenderer(value); + } + RenderCounter* previous = 0; + for (RenderCounter* iterator = m_rootRenderer;iterator; iterator = iterator->m_nextForSameCounter) { + if (iterator == value) { + if (previous) + previous->m_nextForSameCounter = value->m_nextForSameCounter; + else + m_rootRenderer = value->m_nextForSameCounter; + value->m_nextForSameCounter = 0; + value->m_counterNode = 0; + return; + } + previous = iterator; + } + ASSERT_NOT_REACHED(); +} + +void CounterNode::resetRenderers() +{ + while (m_rootRenderer) + m_rootRenderer->invalidate(); // This makes m_rootRenderer point to the next renderer if any since it disconnects the m_rootRenderer from this. } -void CounterNode::resetRenderers(const AtomicString& identifier) const +void CounterNode::resetThisAndDescendantsRenderers() { - const CounterNode* node = this; + CounterNode* node = this; do { - node->resetRenderer(identifier); + node->resetRenderers(); node = node->nextInPreOrder(this); } while (node); } -void CounterNode::recount(const AtomicString& identifier) +void CounterNode::recount() { for (CounterNode* node = this; node; node = node->m_nextSibling) { int oldCount = node->m_countInParent; @@ -127,7 +185,7 @@ void CounterNode::recount(const AtomicString& identifier) if (oldCount == newCount) break; node->m_countInParent = newCount; - node->resetRenderers(identifier); + node->resetThisAndDescendantsRenderers(); } } @@ -141,7 +199,7 @@ void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild, cons if (newChild->m_hasResetType) { while (m_lastChild != refChild) - RenderCounter::destroyCounterNode(m_lastChild->renderer(), identifier); + RenderCounter::destroyCounterNode(m_lastChild->owner(), identifier); } CounterNode* next; @@ -168,9 +226,9 @@ void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild, cons } newChild->m_countInParent = newChild->computeCountInParent(); - newChild->resetRenderers(identifier); + newChild->resetThisAndDescendantsRenderers(); if (next) - next->recount(identifier); + next->recount(); return; } @@ -203,11 +261,11 @@ void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild, cons newChild->m_firstChild = 0; newChild->m_lastChild = 0; newChild->m_countInParent = newChild->computeCountInParent(); - newChild->resetRenderer(identifier); - first->recount(identifier); + newChild->resetRenderers(); + first->recount(); } -void CounterNode::removeChild(CounterNode* oldChild, const AtomicString& identifier) +void CounterNode::removeChild(CounterNode* oldChild) { ASSERT(oldChild); ASSERT(!oldChild->m_firstChild); @@ -235,7 +293,7 @@ void CounterNode::removeChild(CounterNode* oldChild, const AtomicString& identif } if (next) - next->recount(identifier); + next->recount(); } #ifndef NDEBUG @@ -253,8 +311,9 @@ static void showTreeAndMark(const CounterNode* node) fprintf(stderr, "%p %s: %d %d P:%p PS:%p NS:%p R:%p\n", current, current->actsAsReset() ? "reset____" : "increment", current->value(), current->countInParent(), current->parent(), current->previousSibling(), - current->nextSibling(), current->renderer()); + current->nextSibling(), current->owner()); } + fflush(stderr); } #endif diff --git a/Source/WebCore/rendering/CounterNode.h b/Source/WebCore/rendering/CounterNode.h index 639946c..7d6def8 100644 --- a/Source/WebCore/rendering/CounterNode.h +++ b/Source/WebCore/rendering/CounterNode.h @@ -38,16 +38,22 @@ namespace WebCore { class RenderObject; +class RenderCounter; class CounterNode : public RefCounted<CounterNode> { public: static PassRefPtr<CounterNode> create(RenderObject*, bool isReset, int value); - + ~CounterNode(); bool actsAsReset() const { return m_hasResetType || !m_parent; } bool hasResetType() const { return m_hasResetType; } int value() const { return m_value; } int countInParent() const { return m_countInParent; } - RenderObject* renderer() const { return m_renderer; } + RenderObject* owner() const { return m_owner; } + void addRenderer(RenderCounter*); + void removeRenderer(RenderCounter*); + + // Invalidates the text in the renderers of this counter, if any. + void resetRenderers(); CounterNode* parent() const { return m_parent; } CounterNode* previousSibling() const { return m_previousSibling; } @@ -62,26 +68,21 @@ public: void insertAfter(CounterNode* newChild, CounterNode* beforeChild, const AtomicString& identifier); // identifier must match the identifier of this counter. - void removeChild(CounterNode*, const AtomicString& identifier); + void removeChild(CounterNode*); private: CounterNode(RenderObject*, bool isReset, int value); int computeCountInParent() const; - void recount(const AtomicString& identifier); - - // Invalidates the text in the renderer of this counter, if any. - // identifier must match the identifier of this counter. - void resetRenderer(const AtomicString& identifier) const; - // Invalidates the text in the renderer of this counter, if any, // and in the renderers of all descendants of this counter, if any. - // identifier must match the identifier of this counter. - void resetRenderers(const AtomicString& identifier) const; + void resetThisAndDescendantsRenderers(); + void recount(); bool m_hasResetType; int m_value; int m_countInParent; - RenderObject* m_renderer; + RenderObject* m_owner; + RenderCounter* m_rootRenderer; CounterNode* m_parent; CounterNode* m_previousSibling; diff --git a/Source/WebCore/rendering/FixedTableLayout.cpp b/Source/WebCore/rendering/FixedTableLayout.cpp index 3285d15..b1a61b5 100644 --- a/Source/WebCore/rendering/FixedTableLayout.cpp +++ b/Source/WebCore/rendering/FixedTableLayout.cpp @@ -118,7 +118,8 @@ int FixedTableLayout::calcWidthArray(int) spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn); } if ((w.isFixed() || w.isPercent()) && w.isPositive()) { - m_width[currentEffectiveColumn].setRawValue(w.type(), w.rawValue() * spanInCurrentEffectiveColumn); + m_width[currentEffectiveColumn] = w; + m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn; usedWidth += effWidth * spanInCurrentEffectiveColumn; } span -= spanInCurrentEffectiveColumn; @@ -164,10 +165,11 @@ int FixedTableLayout::calcWidthArray(int) int usedSpan = 0; int i = 0; while (usedSpan < span && cCol + i < nEffCols) { - int eSpan = m_table->spanOfEffCol(cCol + i); + float eSpan = m_table->spanOfEffCol(cCol + i); // Only set if no col element has already set it. if (m_width[cCol + i].isAuto() && w.type() != Auto) { - m_width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan / span); + m_width[cCol + i] = w; + m_width[cCol + i] *= eSpan / span; usedWidth += effWidth * eSpan / span; } usedSpan += eSpan; @@ -234,7 +236,7 @@ void FixedTableLayout::layout() int autoSpan = 0; int totalFixedWidth = 0; int totalPercentWidth = 0; - int totalRawPercent = 0; + float totalPercent = 0; // Compute requirements and try to satisfy fixed and percent widths. // Percentages are of the table's width, so for example @@ -247,7 +249,7 @@ void FixedTableLayout::layout() } else if (m_width[i].isPercent()) { calcWidth[i] = m_width[i].calcValue(tableLogicalWidth); totalPercentWidth += calcWidth[i]; - totalRawPercent += m_width[i].rawValue(); + totalPercent += m_width[i].percent(); } else if (m_width[i].isAuto()) { numAuto++; autoSpan += m_table->spanOfEffCol(i); @@ -270,11 +272,11 @@ void FixedTableLayout::layout() } } } - if (totalRawPercent) { + if (totalPercent) { totalPercentWidth = 0; for (int i = 0; i < nEffCols; i++) { if (m_width[i].isPercent()) { - calcWidth[i] = m_width[i].rawValue() * (tableLogicalWidth - totalFixedWidth) / totalRawPercent; + calcWidth[i] = m_width[i].percent() * (tableLogicalWidth - totalFixedWidth) / totalPercent; totalPercentWidth += calcWidth[i]; } } diff --git a/Source/WebCore/rendering/HitTestResult.cpp b/Source/WebCore/rendering/HitTestResult.cpp index ba98eff..e5638c9 100644 --- a/Source/WebCore/rendering/HitTestResult.cpp +++ b/Source/WebCore/rendering/HitTestResult.cpp @@ -95,16 +95,17 @@ HitTestResult::HitTestResult(const HitTestResult& other) , m_scrollbar(other.scrollbar()) , m_isOverWidget(other.isOverWidget()) { - // Only copy the padding and ListHashSet in case of rect hit test. + // Only copy the padding and NodeSet in case of rect hit test. // Copying the later is rather expensive. if ((m_isRectBased = other.isRectBasedTest())) { m_topPadding = other.m_topPadding; m_rightPadding = other.m_rightPadding; m_bottomPadding = other.m_bottomPadding; m_leftPadding = other.m_leftPadding; - m_rectBasedTestResult = other.rectBasedTestResult(); } else m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0; + + m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0); } HitTestResult::~HitTestResult() @@ -120,16 +121,17 @@ HitTestResult& HitTestResult::operator=(const HitTestResult& other) m_innerURLElement = other.URLElement(); m_scrollbar = other.scrollbar(); m_isOverWidget = other.isOverWidget(); - // Only copy the padding and ListHashSet in case of rect hit test. + // Only copy the padding and NodeSet in case of rect hit test. // Copying the later is rather expensive. if ((m_isRectBased = other.isRectBasedTest())) { m_topPadding = other.m_topPadding; m_rightPadding = other.m_rightPadding; m_bottomPadding = other.m_bottomPadding; m_leftPadding = other.m_leftPadding; - m_rectBasedTestResult = other.rectBasedTestResult(); } else m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0; + + m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0); return *this; } @@ -525,7 +527,7 @@ bool HitTestResult::isContentEditable() const if (m_innerNonSharedNode->hasTagName(inputTag)) return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField(); - return m_innerNonSharedNode->isContentEditable(); + return m_innerNonSharedNode->rendererIsEditable(); } bool HitTestResult::addNodeToRectBasedTestResult(Node* node, int x, int y, const IntRect& rect) @@ -540,7 +542,7 @@ bool HitTestResult::addNodeToRectBasedTestResult(Node* node, int x, int y, const return true; node = node->shadowAncestorNode(); - m_rectBasedTestResult.add(node); + mutableRectBasedTestResult().add(node); return !rect.contains(rectForPoint(x, y)); } @@ -557,7 +559,7 @@ bool HitTestResult::addNodeToRectBasedTestResult(Node* node, int x, int y, const return true; node = node->shadowAncestorNode(); - m_rectBasedTestResult.add(node); + mutableRectBasedTestResult().add(node); return !rect.contains(rectForPoint(x, y)); } @@ -575,10 +577,11 @@ void HitTestResult::append(const HitTestResult& other) m_isOverWidget = other.isOverWidget(); } - const ListHashSet<RefPtr<Node> >& list = other.rectBasedTestResult(); - ListHashSet<RefPtr<Node> >::const_iterator last = list.end(); - for (ListHashSet<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it) - m_rectBasedTestResult.add(it->get()); + if (other.m_rectBasedTestResult) { + NodeSet& set = mutableRectBasedTestResult(); + for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it) + set.add(it->get()); + } } IntRect HitTestResult::rectForPoint(const IntPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) @@ -593,4 +596,18 @@ IntRect HitTestResult::rectForPoint(const IntPoint& point, unsigned topPadding, return IntRect(actualPoint, actualPadding); } +const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const +{ + if (!m_rectBasedTestResult) + m_rectBasedTestResult = adoptPtr(new NodeSet); + return *m_rectBasedTestResult; +} + +HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult() +{ + if (!m_rectBasedTestResult) + m_rectBasedTestResult = adoptPtr(new NodeSet); + return *m_rectBasedTestResult; +} + } // namespace WebCore diff --git a/Source/WebCore/rendering/HitTestResult.h b/Source/WebCore/rendering/HitTestResult.h index 3aa17e3..89b5ea1 100644 --- a/Source/WebCore/rendering/HitTestResult.h +++ b/Source/WebCore/rendering/HitTestResult.h @@ -27,6 +27,7 @@ #include "TextDirection.h" #include <wtf/Forward.h> #include <wtf/ListHashSet.h> +#include <wtf/OwnPtr.h> #include <wtf/RefPtr.h> namespace WebCore { @@ -44,6 +45,8 @@ class Scrollbar; class HitTestResult { public: + typedef ListHashSet<RefPtr<Node> > NodeSet; + HitTestResult(); HitTestResult(const IntPoint&); // Pass non-negative padding values to perform a rect-based hit test. @@ -112,10 +115,15 @@ public: // enclosed by the boundaries of a node. bool addNodeToRectBasedTestResult(Node*, int x, int y, const IntRect& = IntRect()); bool addNodeToRectBasedTestResult(Node*, int x, int y, const FloatRect&); - const ListHashSet<RefPtr<Node> >& rectBasedTestResult() const { return m_rectBasedTestResult; } void append(const HitTestResult&); + // If m_rectBasedTestResult is 0 then set it to a new NodeSet. Return *m_rectBasedTestResult. Lazy allocation makes + // sense because the NodeSet is seldom necessary, and it's somewhat expensive to allocate and initialize. This method does + // the same thing as mutableRectBasedTestResult(), but here the return value is const. + const NodeSet& rectBasedTestResult() const; + private: + NodeSet& mutableRectBasedTestResult(); // See above. #if ENABLE(VIDEO) HTMLMediaElement* mediaElement() const; @@ -134,7 +142,7 @@ private: int m_rightPadding; int m_bottomPadding; int m_leftPadding; - ListHashSet<RefPtr<Node> > m_rectBasedTestResult; + mutable OwnPtr<NodeSet> m_rectBasedTestResult; }; inline IntRect HitTestResult::rectForPoint(int x, int y) const diff --git a/Source/WebCore/rendering/InlineBox.cpp b/Source/WebCore/rendering/InlineBox.cpp index 1f4e244..930071e 100644 --- a/Source/WebCore/rendering/InlineBox.cpp +++ b/Source/WebCore/rendering/InlineBox.cpp @@ -154,8 +154,9 @@ void InlineBox::adjustPosition(float dx, float dy) { m_x += dx; m_y += dy; - if (m_renderer->isReplaced()) - toRenderBox(m_renderer)->positionLineBox(this); + + if (m_renderer->isReplaced()) + toRenderBox(m_renderer)->move(dx, dy); } void InlineBox::paint(PaintInfo& paintInfo, int tx, int ty) diff --git a/Source/WebCore/rendering/InlineBox.h b/Source/WebCore/rendering/InlineBox.h index de7b224..9335970 100644 --- a/Source/WebCore/rendering/InlineBox.h +++ b/Source/WebCore/rendering/InlineBox.h @@ -53,7 +53,7 @@ public: #endif , m_isHorizontal(true) , m_endsWithBreak(false) - , m_hasSelectedChildren(false) + , m_hasSelectedChildrenOrCanHaveLeadingExpansion(false) , m_hasEllipsisBoxOrHyphen(false) , m_dirOverride(false) , m_isText(false) @@ -87,7 +87,7 @@ public: #endif , m_isHorizontal(isHorizontal) , m_endsWithBreak(false) - , m_hasSelectedChildren(false) + , m_hasSelectedChildrenOrCanHaveLeadingExpansion(false) , m_hasEllipsisBoxOrHyphen(false) , m_dirOverride(false) , m_isText(false) @@ -340,8 +340,9 @@ protected: // for RootInlineBox bool m_endsWithBreak : 1; // Whether the line ends with a <br>. - bool m_hasSelectedChildren : 1; // Whether we have any children selected (this bit will also be set if the <br> that terminates our line is selected). - bool m_hasEllipsisBoxOrHyphen : 1; + // shared between RootInlineBox and InlineTextBox + bool m_hasSelectedChildrenOrCanHaveLeadingExpansion : 1; // Whether we have any children selected (this bit will also be set if the <br> that terminates our line is selected). + bool m_hasEllipsisBoxOrHyphen : 1; // for InlineTextBox public: diff --git a/Source/WebCore/rendering/InlineFlowBox.cpp b/Source/WebCore/rendering/InlineFlowBox.cpp index e146dd9..b58a30e 100644 --- a/Source/WebCore/rendering/InlineFlowBox.cpp +++ b/Source/WebCore/rendering/InlineFlowBox.cpp @@ -38,7 +38,6 @@ #include "RenderTableCell.h" #include "RootInlineBox.h" #include "Text.h" -#include "VerticalPositionCache.h" #include <math.h> @@ -97,8 +96,48 @@ void InlineFlowBox::addToLine(InlineBox* child) } child->setFirstLineStyleBit(m_firstLine); child->setIsHorizontal(isHorizontal()); - if (child->isText()) + if (child->isText()) { m_hasTextChildren = true; + m_hasTextDescendants = true; + } else if (child->isInlineFlowBox()) { + if (static_cast<InlineFlowBox*>(child)->hasTextDescendants()) + m_hasTextDescendants = true; + } + + if (descendantsHaveSameLineHeightAndBaseline() && !child->renderer()->isPositioned()) { + RenderStyle* parentStyle = renderer()->style(m_firstLine); + RenderStyle* childStyle = child->renderer()->style(m_firstLine); + bool shouldClearDescendantsHaveSameLineHeightAndBaseline = false; + if (child->renderer()->isReplaced()) + shouldClearDescendantsHaveSameLineHeightAndBaseline = true; + else if (child->isText()) { + if (child->renderer()->isBR()) { + if (parentStyle->font().fontMetrics().ascent() != childStyle->font().fontMetrics().ascent() + || parentStyle->font().fontMetrics().descent() != childStyle->font().fontMetrics().descent() || parentStyle->lineHeight() != childStyle->lineHeight() + || (parentStyle->verticalAlign() != BASELINE && !isRootInlineBox()) || childStyle->verticalAlign() != BASELINE) + shouldClearDescendantsHaveSameLineHeightAndBaseline = true; + } else if (childStyle->hasTextCombine() || childStyle->textEmphasisMark() != TextEmphasisMarkNone) + shouldClearDescendantsHaveSameLineHeightAndBaseline = true; + } else { + if (child->renderer()->isBR()) { + // FIXME: This is dumb. We only turn off because current layout test results expect the <br> to be 0-height on the baseline. + // Other than making a zillion tests have to regenerate results, there's no reason to ditch the optimization here. + shouldClearDescendantsHaveSameLineHeightAndBaseline = true; + } else { + ASSERT(isInlineFlowBox()); + InlineFlowBox* childFlowBox = static_cast<InlineFlowBox*>(child); + // Check the child's bit, and then also check for differences in font, line-height, vertical-align + if (!childFlowBox->descendantsHaveSameLineHeightAndBaseline() || parentStyle->font().fontMetrics().ascent() != childStyle->font().fontMetrics().ascent() + || parentStyle->font().fontMetrics().descent() != childStyle->font().fontMetrics().descent() || parentStyle->lineHeight() != childStyle->lineHeight() + || (parentStyle->verticalAlign() != BASELINE && !isRootInlineBox()) || childStyle->verticalAlign() != BASELINE + || childStyle->hasBorder() || childStyle->hasPadding() || childStyle->hasTextCombine()) + shouldClearDescendantsHaveSameLineHeightAndBaseline = true; + } + } + + if (shouldClearDescendantsHaveSameLineHeightAndBaseline) + clearDescendantsHaveSameLineHeightAndBaseline(); + } checkConsistency(); } @@ -214,7 +253,18 @@ bool InlineFlowBox::onEndChain(RenderObject* endObject) return true; } -void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject) +static bool isAnsectorAndWithinBlock(RenderObject* ancestor, RenderObject* child) +{ + RenderObject* object = child; + while (object && !object->isRenderBlock()) { + if (object == ancestor) + return true; + object = object->parent(); + } + return false; +} + +void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject, RenderObject* logicallyLastRunRenderer) { // All boxes start off open. They will not apply any margins/border/padding on // any side. @@ -234,23 +284,18 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* en else if (!ltr && lineBoxList->lastLineBox() == this) includeRightEdge = true; } - - // In order to determine if the inline ends on this line, we check three things: - // (1) If we are the last line and we don't have a continuation(), then we can - // close up. - // (2) If the last line box for the flow has an object following it on the line (ltr, - // reverse for rtl), then the inline has closed. - // (3) The line may end on the inline. If we are the last child (climbing up - // the end object's chain), then we just closed as well. + if (!lineBoxList->lastLineBox()->isConstructed()) { RenderInline* inlineFlow = toRenderInline(renderer()); + bool isLastObjectOnLine = (endObject && endObject->isText()) ? !isAnsectorAndWithinBlock(renderer(), logicallyLastRunRenderer->parent()) : onEndChain(logicallyLastRunRenderer); + if (ltr) { - if (!nextLineBox() && - ((lastLine && !inlineFlow->continuation()) || nextOnLineExists() || onEndChain(endObject))) + if (!nextLineBox() + && ((lastLine || isLastObjectOnLine) && !inlineFlow->continuation())) includeRightEdge = true; } else { - if ((!prevLineBox() || prevLineBox()->isConstructed()) && - ((lastLine && !inlineFlow->continuation()) || prevOnLineExists() || onEndChain(endObject))) + if ((!prevLineBox() || prevLineBox()->isConstructed()) + && ((lastLine || isLastObjectOnLine) && !inlineFlow->continuation())) includeLeftEdge = true; } } @@ -262,7 +307,7 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* en for (InlineBox* currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) { if (currChild->isInlineFlowBox()) { InlineFlowBox* currFlow = static_cast<InlineFlowBox*>(currChild); - currFlow->determineSpacingForFlowBoxes(lastLine, endObject); + currFlow->determineSpacingForFlowBoxes(lastLine, endObject, logicallyLastRunRenderer); } } } @@ -325,7 +370,8 @@ bool InlineFlowBox::requiresIdeographicBaseline(const GlyphOverflowAndFallbackFo if (isHorizontal()) return false; - if (renderer()->style(m_firstLine)->font().primaryFont()->orientation() == Vertical) + if (renderer()->style(m_firstLine)->fontDescription().textOrientation() == TextOrientationUpright + || renderer()->style(m_firstLine)->font().primaryFont()->hasVerticalGlyphs()) return true; for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { @@ -336,7 +382,7 @@ bool InlineFlowBox::requiresIdeographicBaseline(const GlyphOverflowAndFallbackFo if (static_cast<InlineFlowBox*>(curr)->requiresIdeographicBaseline(textBoxDataMap)) return true; } else { - if (curr->renderer()->style(m_firstLine)->font().primaryFont()->orientation() == Vertical) + if (curr->renderer()->style(m_firstLine)->font().primaryFont()->hasVerticalGlyphs()) return true; const Vector<const SimpleFontData*>* usedFonts = 0; @@ -347,7 +393,7 @@ bool InlineFlowBox::requiresIdeographicBaseline(const GlyphOverflowAndFallbackFo if (usedFonts) { for (size_t i = 0; i < usedFonts->size(); ++i) { - if (usedFonts->at(i)->orientation() == Vertical) + if (usedFonts->at(i)->hasVerticalGlyphs()) return true; } } @@ -384,92 +430,36 @@ void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, i } } -static int verticalPositionForBox(InlineBox* box, FontBaseline baselineType, bool firstLine, VerticalPositionCache& verticalPositionCache) -{ - if (box->renderer()->isText()) - return box->parent()->logicalTop(); - - RenderBoxModelObject* renderer = box->boxModelObject(); - ASSERT(renderer->isInline()); - if (!renderer->isInline()) - return 0; - - // This method determines the vertical position for inline elements. - if (firstLine && !renderer->document()->usesFirstLineRules()) - firstLine = false; - - // Check the cache. - bool isRenderInline = renderer->isRenderInline(); - if (isRenderInline && !firstLine) { - int verticalPosition = verticalPositionCache.get(renderer, baselineType); - if (verticalPosition != PositionUndefined) - return verticalPosition; - } - - int verticalPosition = 0; - EVerticalAlign verticalAlign = renderer->style()->verticalAlign(); - if (verticalAlign == TOP || verticalAlign == BOTTOM) - return 0; - - RenderObject* parent = renderer->parent(); - if (parent->isRenderInline() && parent->style()->verticalAlign() != TOP && parent->style()->verticalAlign() != BOTTOM) - verticalPosition = box->parent()->logicalTop(); - - if (verticalAlign != BASELINE) { - const Font& font = parent->style(firstLine)->font(); - const FontMetrics& fontMetrics = font.fontMetrics(); - int fontSize = font.pixelSize(); - - LineDirectionMode lineDirection = parent->style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine; - - if (verticalAlign == SUB) - verticalPosition += fontSize / 5 + 1; - else if (verticalAlign == SUPER) - verticalPosition -= fontSize / 3 + 1; - else if (verticalAlign == TEXT_TOP) - verticalPosition += renderer->baselinePosition(baselineType, firstLine, lineDirection) - fontMetrics.ascent(baselineType); - else if (verticalAlign == MIDDLE) - verticalPosition += -static_cast<int>(fontMetrics.xHeight() / 2) - renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType, firstLine, lineDirection); - else if (verticalAlign == TEXT_BOTTOM) { - verticalPosition += fontMetrics.descent(baselineType); - // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case. - if (!renderer->isReplaced() || renderer->isInlineBlockOrInlineTable()) - verticalPosition -= (renderer->lineHeight(firstLine, lineDirection) - renderer->baselinePosition(baselineType, firstLine, lineDirection)); - } else if (verticalAlign == BASELINE_MIDDLE) - verticalPosition += -renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType, firstLine, lineDirection); - else if (verticalAlign == LENGTH) - verticalPosition -= renderer->style()->verticalAlignLength().calcValue(renderer->lineHeight(firstLine, lineDirection)); - } - - // Store the cached value. - if (isRenderInline && !firstLine) - verticalPositionCache.set(renderer, baselineType, verticalPosition); - - return verticalPosition; -} - -void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, +void InlineFlowBox::computeLogicalBoxHeights(RootInlineBox* rootBox, int& maxPositionTop, int& maxPositionBottom, int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent, bool strictMode, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, FontBaseline baselineType, VerticalPositionCache& verticalPositionCache) { // The primary purpose of this function is to compute the maximal ascent and descent values for - // a line. + // a line. These values are computed based off the block's line-box-contain property, which indicates + // what parts of descendant boxes have to fit within the line. + // + // The maxAscent value represents the distance of the highest point of any box (typically including line-height) from + // the root box's baseline. The maxDescent value represents the distance of the lowest point of any box + // (also typically including line-height) from the root box baseline. These values can be negative. // - // The maxAscent value represents the distance of the highest point of any box (including line-height) from - // the root box's baseline. The maxDescent value represents the distance of the lowest point of any box - // (also including line-height) from the root box baseline. These values can be negative. + // A secondary purpose of this function is to store the offset of every box's baseline from the root box's + // baseline. This information is cached in the logicalTop() of every box. We're effectively just using + // the logicalTop() as scratch space. // - // A secondary purpose of this function is to store the offset of very box's baseline from the root box's - // baseline. This information is cached in the logicalTop() of every box. We're effectively just using - // the logicalTop() as scratch space. + // Because a box can be positioned such that it ends up fully above or fully below the + // root line box, we only consider it to affect the maxAscent and maxDescent values if some + // part of the box (EXCLUDING leading) is above (for ascent) or below (for descent) the root box's baseline. + bool affectsAscent = false; + bool affectsDescent = false; + bool checkChildren = !descendantsHaveSameLineHeightAndBaseline(); + if (isRootInlineBox()) { // Examine our root box. - int height = lineHeight(); - int baseline = baselinePosition(baselineType); - if (hasTextChildren() || strictMode) { - int ascent = baseline; - int descent = height - ascent; + int ascent = 0; + int descent = 0; + rootBox->ascentAndDescentForBox(rootBox, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent); + if (strictMode || hasTextChildren() || (!checkChildren && hasTextDescendants())) { if (maxAscent < ascent || !setMaxAscent) { maxAscent = ascent; setMaxAscent = true; @@ -481,131 +471,96 @@ void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositi } } + if (!checkChildren) + return; + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { if (curr->renderer()->isPositioned()) continue; // Positioned placeholders don't affect calculations. - bool isInlineFlow = curr->isInlineFlowBox(); + InlineFlowBox* inlineFlowBox = curr->isInlineFlowBox() ? static_cast<InlineFlowBox*>(curr) : 0; - // Because a box can be positioned such that it ends up fully above or fully below the - // root line box, we only consider it to affect the maxAscent and maxDescent values if some - // part of the box (EXCLUDING line-height) is above (for ascent) or below (for descent) the root box's baseline. bool affectsAscent = false; bool affectsDescent = false; // The verticalPositionForBox function returns the distance between the child box's baseline // and the root box's baseline. The value is negative if the child box's baseline is above the // root box's baseline, and it is positive if the child box's baseline is below the root box's baseline. - curr->setLogicalTop(verticalPositionForBox(curr, baselineType, m_firstLine, verticalPositionCache)); + curr->setLogicalTop(rootBox->verticalPositionForBox(curr, verticalPositionCache)); - int lineHeight; - int baseline; - Vector<const SimpleFontData*>* usedFonts = 0; - if (curr->isInlineTextBox()) { - GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(static_cast<InlineTextBox*>(curr)); - usedFonts = it == textBoxDataMap.end() ? 0 : &it->second.first; - } - - if (usedFonts && !usedFonts->isEmpty() && curr->renderer()->style(m_firstLine)->lineHeight().isNegative()) { - usedFonts->append(curr->renderer()->style(m_firstLine)->font().primaryFont()); - bool baselineSet = false; - baseline = 0; - int baselineToBottom = 0; - for (size_t i = 0; i < usedFonts->size(); ++i) { - const FontMetrics& fontMetrics = usedFonts->at(i)->fontMetrics(); - int halfLeading = (fontMetrics.lineSpacing() - fontMetrics.height()) / 2; - int usedFontBaseline = halfLeading + fontMetrics.ascent(baselineType); - int usedFontBaselineToBottom = fontMetrics.lineSpacing() - usedFontBaseline; - if (!baselineSet) { - baselineSet = true; - baseline = usedFontBaseline; - baselineToBottom = usedFontBaselineToBottom; - } else { - baseline = max(baseline, usedFontBaseline); - baselineToBottom = max(baselineToBottom, usedFontBaselineToBottom); - } - if (!affectsAscent) - affectsAscent = fontMetrics.ascent() - curr->logicalTop() > 0; - if (!affectsDescent) - affectsDescent = fontMetrics.descent() + curr->logicalTop() > 0; - } - lineHeight = baseline + baselineToBottom; - } else { - lineHeight = curr->lineHeight(); - baseline = curr->baselinePosition(baselineType); - if (curr->isText() || isInlineFlow) { - // Examine the font box for inline flows and text boxes to see if any part of it is above the baseline. - // If the top of our font box relative to the root box baseline is above the root box baseline, then - // we are contributing to the maxAscent value. - const FontMetrics& fontMetrics = curr->renderer()->style(m_firstLine)->fontMetrics(); - affectsAscent = fontMetrics.ascent(baselineType) - curr->logicalTop() > 0; - - // Descent is similar. If any part of our font box is below the root box's baseline, then - // we contribute to the maxDescent value. - affectsDescent = fontMetrics.descent(baselineType) + curr->logicalTop() > 0; - } else { - // Replaced elements always affect both the ascent and descent. - affectsAscent = true; - affectsDescent = true; - } - } + int ascent = 0; + int descent = 0; + rootBox->ascentAndDescentForBox(curr, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent); + int boxHeight = ascent + descent; if (curr->verticalAlign() == TOP) { - if (maxPositionTop < lineHeight) - maxPositionTop = lineHeight; + if (maxPositionTop < ascent) + maxPositionTop = boxHeight; } else if (curr->verticalAlign() == BOTTOM) { - if (maxPositionBottom < lineHeight) - maxPositionBottom = lineHeight; - } else if ((!isInlineFlow || static_cast<InlineFlowBox*>(curr)->hasTextChildren()) || curr->boxModelObject()->hasInlineDirectionBordersOrPadding() || strictMode) { + if (maxPositionBottom < boxHeight) + maxPositionBottom = boxHeight; + } else if (!inlineFlowBox || strictMode || inlineFlowBox->hasTextChildren() || (inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants()) + || inlineFlowBox->boxModelObject()->hasInlineDirectionBordersOrPadding()) { // Note that these values can be negative. Even though we only affect the maxAscent and maxDescent values // if our box (excluding line-height) was above (for ascent) or below (for descent) the root baseline, once you factor in line-height // the final box can end up being fully above or fully below the root box's baseline! This is ok, but what it // means is that ascent and descent (including leading), can end up being negative. The setMaxAscent and // setMaxDescent booleans are used to ensure that we're willing to initially set maxAscent/Descent to negative // values. - int ascent = baseline - curr->logicalTop(); - int descent = lineHeight - ascent; + ascent -= curr->logicalTop(); + descent += curr->logicalTop(); if (affectsAscent && (maxAscent < ascent || !setMaxAscent)) { maxAscent = ascent; setMaxAscent = true; } + if (affectsDescent && (maxDescent < descent || !setMaxDescent)) { maxDescent = descent; setMaxDescent = true; } } - if (curr->isInlineFlowBox()) - static_cast<InlineFlowBox*>(curr)->computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, - setMaxAscent, setMaxDescent, strictMode, textBoxDataMap, - baselineType, verticalPositionCache); + if (inlineFlowBox) + inlineFlowBox->computeLogicalBoxHeights(rootBox, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, + setMaxAscent, setMaxDescent, strictMode, textBoxDataMap, + baselineType, verticalPositionCache); } } void InlineFlowBox::placeBoxesInBlockDirection(int top, int maxHeight, int maxAscent, bool strictMode, int& lineTop, int& lineBottom, bool& setLineTop, int& lineTopIncludingMargins, int& lineBottomIncludingMargins, bool& hasAnnotationsBefore, bool& hasAnnotationsAfter, FontBaseline baselineType) { - if (isRootInlineBox()) - setLogicalTop(top + maxAscent - baselinePosition(baselineType)); // Place our root box. + bool isRootBox = isRootInlineBox(); + if (isRootBox) { + const FontMetrics& fontMetrics = renderer()->style(m_firstLine)->fontMetrics(); + setLogicalTop(top + maxAscent - fontMetrics.ascent(baselineType)); + } + + int adjustmentForChildrenWithSameLineHeightAndBaseline = 0; + if (descendantsHaveSameLineHeightAndBaseline()) { + adjustmentForChildrenWithSameLineHeightAndBaseline = logicalTop(); + if (parent()) + adjustmentForChildrenWithSameLineHeightAndBaseline += (boxModelObject()->borderBefore() + boxModelObject()->paddingBefore()); + } for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { if (curr->renderer()->isPositioned()) continue; // Positioned placeholders don't affect calculations. - - // Adjust boxes to use their real box y/height and not the logical height (as dictated by - // line-height). - bool isInlineFlow = curr->isInlineFlowBox(); - if (isInlineFlow) - static_cast<InlineFlowBox*>(curr)->placeBoxesInBlockDirection(top, maxHeight, maxAscent, strictMode, lineTop, lineBottom, setLineTop, - lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType); + if (descendantsHaveSameLineHeightAndBaseline()) { + curr->adjustBlockDirectionPosition(adjustmentForChildrenWithSameLineHeightAndBaseline); + continue; + } + + InlineFlowBox* inlineFlowBox = curr->isInlineFlowBox() ? static_cast<InlineFlowBox*>(curr) : 0; bool childAffectsTopBottomPos = true; if (curr->verticalAlign() == TOP) curr->setLogicalTop(top); else if (curr->verticalAlign() == BOTTOM) curr->setLogicalTop(top + maxHeight - curr->lineHeight()); else { - if ((isInlineFlow && !static_cast<InlineFlowBox*>(curr)->hasTextChildren()) && !curr->boxModelObject()->hasInlineDirectionBordersOrPadding() && !strictMode) + if (!strictMode && inlineFlowBox && !inlineFlowBox->hasTextChildren() && !curr->boxModelObject()->hasInlineDirectionBordersOrPadding() + && !(inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants())) childAffectsTopBottomPos = false; int posAdjust = maxAscent - curr->baselinePosition(baselineType); curr->setLogicalTop(curr->logicalTop() + top + posAdjust); @@ -676,13 +631,16 @@ void InlineFlowBox::placeBoxesInBlockDirection(int top, int maxHeight, int maxAs lineBottom = max(lineBottom, newLogicalTop + boxHeight); lineBottomIncludingMargins = max(lineBottom, max(lineBottomIncludingMargins, newLogicalTopIncludingMargins + boxHeightIncludingMargins)); } + + // Adjust boxes to use their real box y/height and not the logical height (as dictated by + // line-height). + if (inlineFlowBox) + inlineFlowBox->placeBoxesInBlockDirection(top, maxHeight, maxAscent, strictMode, lineTop, lineBottom, setLineTop, + lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType); } - if (isRootInlineBox()) { - const FontMetrics& fontMetrics = renderer()->style(m_firstLine)->fontMetrics(); - setLogicalTop(logicalTop() + baselinePosition(baselineType) - fontMetrics.ascent(baselineType)); - - if (hasTextChildren() || strictMode) { + if (isRootBox) { + if (strictMode || hasTextChildren() || (descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) { if (!setLineTop) { setLineTop = true; lineTop = logicalTop(); @@ -919,10 +877,29 @@ bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re } } - // Now check ourselves. - FloatPoint boxOrigin = locationIncludingFlipping(); - boxOrigin.move(tx, ty); - FloatRect rect(boxOrigin, IntSize(width(), height())); + // Now check ourselves. Pixel snap hit testing. + IntRect frameRect = roundedFrameRect(); + int minX = frameRect.x(); + int minY = frameRect.y(); + int width = frameRect.width(); + int height = frameRect.height(); + + // Constrain our hit testing to the line top and bottom if necessary. + bool noQuirksMode = renderer()->document()->inNoQuirksMode(); + if (!noQuirksMode && !hasTextChildren() && !(descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) { + RootInlineBox* rootBox = root(); + int& top = isHorizontal() ? minY : minX; + int& logicalHeight = isHorizontal() ? height : width; + int bottom = min(rootBox->lineBottom(), top + logicalHeight); + top = max(rootBox->lineTop(), top); + logicalHeight = bottom - top; + } + + // Move x/y to our coordinates. + IntRect rect(minX, minY, width, height); + flipForWritingMode(rect); + rect.move(tx, ty); + if (visibleToHitTesting() && rect.intersects(result.rectForPoint(x, y))) { renderer()->updateHitTestResult(result, flipForWritingMode(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)) @@ -1074,7 +1051,7 @@ void InlineFlowBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) // Constrain our background/border painting to the line top and bottom if necessary. bool noQuirksMode = renderer()->document()->inNoQuirksMode(); - if (!hasTextChildren() && !noQuirksMode) { + if (!noQuirksMode && !hasTextChildren() && !(descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) { RootInlineBox* rootBox = root(); int& top = isHorizontal() ? y : x; int& logicalHeight = isHorizontal() ? h : w; @@ -1156,7 +1133,7 @@ void InlineFlowBox::paintMask(PaintInfo& paintInfo, int tx, int ty) // Constrain our background/border painting to the line top and bottom if necessary. bool noQuirksMode = renderer()->document()->inNoQuirksMode(); - if (!hasTextChildren() && !noQuirksMode) { + if (!noQuirksMode && !hasTextChildren() && !(descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) { RootInlineBox* rootBox = root(); int& top = isHorizontal() ? y : x; int& logicalHeight = isHorizontal() ? h : w; @@ -1363,6 +1340,59 @@ int InlineFlowBox::computeUnderAnnotationAdjustment(int allowedPosition) const return result; } +void InlineFlowBox::collectLeafBoxesInLogicalOrder(Vector<InlineBox*>& leafBoxesInLogicalOrder, CustomInlineBoxRangeReverse customReverseImplementation, void* userData) const +{ + InlineBox* leaf = firstLeafChild(); + + // FIXME: The reordering code is a copy of parts from BidiResolver::createBidiRunsForLine, operating directly on InlineBoxes, instead of BidiRuns. + // Investigate on how this code could possibly be shared. + unsigned char minLevel = 128; + unsigned char maxLevel = 0; + + // First find highest and lowest levels, and initialize leafBoxesInLogicalOrder with the leaf boxes in visual order. + for (; leaf; leaf = leaf->nextLeafChild()) { + minLevel = min(minLevel, leaf->bidiLevel()); + maxLevel = max(maxLevel, leaf->bidiLevel()); + leafBoxesInLogicalOrder.append(leaf); + } + + if (renderer()->style()->visuallyOrdered()) + return; + + // Reverse of reordering of the line (L2 according to Bidi spec): + // L2. From the highest level found in the text to the lowest odd level on each line, + // reverse any contiguous sequence of characters that are at that level or higher. + + // Reversing the reordering of the line is only done up to the lowest odd level. + if (!(minLevel % 2)) + ++minLevel; + + Vector<InlineBox*>::iterator end = leafBoxesInLogicalOrder.end(); + while (minLevel <= maxLevel) { + Vector<InlineBox*>::iterator it = leafBoxesInLogicalOrder.begin(); + while (it != end) { + while (it != end) { + if ((*it)->bidiLevel() >= minLevel) + break; + ++it; + } + Vector<InlineBox*>::iterator first = it; + while (it != end) { + if ((*it)->bidiLevel() < minLevel) + break; + ++it; + } + Vector<InlineBox*>::iterator last = it; + if (customReverseImplementation) { + ASSERT(userData); + (*customReverseImplementation)(userData, first, last); + } else + std::reverse(first, last); + } + ++minLevel; + } +} + #ifndef NDEBUG void InlineFlowBox::checkConsistency() const diff --git a/Source/WebCore/rendering/InlineFlowBox.h b/Source/WebCore/rendering/InlineFlowBox.h index 918cf5d..9b6f8e4 100644 --- a/Source/WebCore/rendering/InlineFlowBox.h +++ b/Source/WebCore/rendering/InlineFlowBox.h @@ -45,6 +45,7 @@ public: , m_nextLineBox(0) , m_includeLogicalLeftEdge(false) , m_includeLogicalRightEdge(false) + , m_descendantsHaveSameLineHeightAndBaseline(true) #ifndef NDEBUG , m_hasBadChildList(false) #endif @@ -55,6 +56,7 @@ public: // text children must not apply. This change also means that gaps will exist between image bullet list items. Even when the list bullet // is an image, the line is still considered to be immune from the quirk. m_hasTextChildren = obj->style()->display() == LIST_ITEM; + m_hasTextDescendants = m_hasTextChildren; } #ifndef NDEBUG @@ -74,6 +76,9 @@ public: InlineBox* firstLeafChild() const; InlineBox* lastLeafChild() const; + typedef void (*CustomInlineBoxRangeReverse)(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last); + void collectLeafBoxesInLogicalOrder(Vector<InlineBox*>&, CustomInlineBoxRangeReverse customReverseImplementation = 0, void* userData = 0) const; + virtual void setConstructed() { InlineBox::setConstructed(); @@ -154,11 +159,11 @@ public: } // Helper functions used during line construction and placement. - void determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject); + void determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject, RenderObject* logicallyLastRunRenderer); int getFlowSpacingLogicalWidth(); bool onEndChain(RenderObject* endObject); float placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); - void computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, + void computeLogicalBoxHeights(RootInlineBox*, int& maxPositionTop, int& maxPositionBottom, int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent, bool strictMode, GlyphOverflowAndFallbackFontsMap&, FontBaseline, VerticalPositionCache&); void adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, @@ -181,6 +186,7 @@ public: virtual float placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, bool&); bool hasTextChildren() const { return m_hasTextChildren; } + bool hasTextDescendants() const { return m_hasTextDescendants; } void checkConsistency() const; void setHasBadChildList(); @@ -226,6 +232,14 @@ public: void setLayoutOverflow(const IntRect&); void setVisualOverflow(const IntRect&); + bool descendantsHaveSameLineHeightAndBaseline() const { return m_descendantsHaveSameLineHeightAndBaseline; } + void clearDescendantsHaveSameLineHeightAndBaseline() + { + m_descendantsHaveSameLineHeightAndBaseline = false; + if (parent() && parent()->descendantsHaveSameLineHeightAndBaseline()) + parent()->clearDescendantsHaveSameLineHeightAndBaseline(); + } + private: void addBoxShadowVisualOverflow(IntRect& logicalVisualOverflow); void addTextBoxVisualOverflow(const InlineTextBox*, GlyphOverflowAndFallbackFontsMap&, IntRect& logicalVisualOverflow); @@ -245,6 +259,8 @@ protected: bool m_includeLogicalLeftEdge : 1; bool m_includeLogicalRightEdge : 1; bool m_hasTextChildren : 1; + bool m_hasTextDescendants : 1; + bool m_descendantsHaveSameLineHeightAndBaseline : 1; #ifndef NDEBUG bool m_hasBadChildList; diff --git a/Source/WebCore/rendering/InlineIterator.h b/Source/WebCore/rendering/InlineIterator.h index 270364f..75d9763 100644 --- a/Source/WebCore/rendering/InlineIterator.h +++ b/Source/WebCore/rendering/InlineIterator.h @@ -34,43 +34,89 @@ namespace WebCore { class InlineIterator { public: InlineIterator() - : block(0) - , obj(0) - , pos(0) - , nextBreakablePosition(-1) + : m_block(0) + , m_obj(0) + , m_pos(0) + , m_nextBreakablePosition(-1) { } InlineIterator(RenderBlock* b, RenderObject* o, unsigned p) - : block(b) - , obj(o) - , pos(p) - , nextBreakablePosition(-1) + : m_block(b) + , m_obj(o) + , m_pos(p) + , m_nextBreakablePosition(-1) { } + void clear() { moveTo(0, 0); } + + void moveToStartOf(RenderObject* object) + { + moveTo(object, 0); + } + + void moveTo(RenderObject* object, unsigned offset, int nextBreak = -1) + { + m_obj = object; + m_pos = offset; + m_nextBreakablePosition = nextBreak; + } + void increment(InlineBidiResolver* resolver = 0); bool atEnd() const; UChar current() const; ALWAYS_INLINE WTF::Unicode::Direction direction() const; - RenderBlock* block; - RenderObject* obj; - unsigned pos; - int nextBreakablePosition; + RenderBlock* m_block; + RenderObject* m_obj; + unsigned m_pos; + int m_nextBreakablePosition; }; inline bool operator==(const InlineIterator& it1, const InlineIterator& it2) { - return it1.pos == it2.pos && it1.obj == it2.obj; + return it1.m_pos == it2.m_pos && it1.m_obj == it2.m_obj; } inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2) { - return it1.pos != it2.pos || it1.obj != it2.obj; + return it1.m_pos != it2.m_pos || it1.m_obj != it2.m_obj; +} + +static inline WTF::Unicode::Direction embedCharFromDirection(TextDirection dir, EUnicodeBidi unicodeBidi) +{ + using namespace WTF::Unicode; + if (unicodeBidi == Embed) + return dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding; + return dir == RTL ? RightToLeftOverride : LeftToRightOverride; +} + +static inline void notifyResolverEnteredObject(InlineBidiResolver* resolver, RenderObject* object) +{ + if (!resolver || !object || !object->isRenderInline()) + return; + + RenderStyle* style = object->style(); + EUnicodeBidi unicodeBidi = style->unicodeBidi(); + if (unicodeBidi == UBNormal) + return; + resolver->embed(embedCharFromDirection(style->direction(), unicodeBidi), FromStyleOrDOM); +} + +static inline void notifyResolverWillExitObject(InlineBidiResolver* resolver, RenderObject* object) +{ + if (!resolver || !object || !object->isRenderInline()) + return; + if (object->style()->unicodeBidi() == UBNormal) + return; + resolver->embed(WTF::Unicode::PopDirectionalFormat, FromStyleOrDOM); } +// FIXME: This function is misleadingly named. It has little to do with bidi. +// This function will iterate over inlines within a block, optionally notifying +// a bidi resolver as it enters/exits inlines (so it can push/pop embedding levels). static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0) { RenderObject* next = 0; @@ -81,16 +127,7 @@ static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, next = 0; if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned() && !current->isText()) { next = current->firstChild(); - if (next && resolver && next->isRenderInline()) { - EUnicodeBidi ub = next->style()->unicodeBidi(); - if (ub != UBNormal) { - TextDirection dir = next->style()->direction(); - WTF::Unicode::Direction d = (ub == Embed - ? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding : WTF::Unicode::LeftToRightEmbedding) - : (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride)); - resolver->embed(d); - } - } + notifyResolverEnteredObject(resolver, next); } if (!next) { @@ -101,24 +138,14 @@ static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, } while (current && current != block) { - if (resolver && current->isRenderInline() && current->style()->unicodeBidi() != UBNormal) - resolver->embed(WTF::Unicode::PopDirectionalFormat); + notifyResolverWillExitObject(resolver, current); next = current->nextSibling(); if (next) { - if (resolver && next->isRenderInline()) { - EUnicodeBidi ub = next->style()->unicodeBidi(); - if (ub != UBNormal) { - TextDirection dir = next->style()->direction(); - WTF::Unicode::Direction d = (ub == Embed - ? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding: WTF::Unicode::LeftToRightEmbedding) - : (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride)); - resolver->embed(d); - } - } + notifyResolverEnteredObject(resolver, next); break; } - + current = current->parent(); if (!skipInlines && current && current != block && current->isRenderInline()) { next = current; @@ -148,19 +175,10 @@ static inline RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* re { if (!block->firstChild()) return 0; - + RenderObject* o = block->firstChild(); if (o->isRenderInline()) { - if (resolver) { - EUnicodeBidi ub = o->style()->unicodeBidi(); - if (ub != UBNormal) { - TextDirection dir = o->style()->direction(); - WTF::Unicode::Direction d = (ub == Embed - ? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding : WTF::Unicode::LeftToRightEmbedding) - : (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride)); - resolver->embed(d); - } - } + notifyResolverEnteredObject(resolver, o); if (skipInlines && o->firstChild()) o = bidiNext(block, o, resolver, skipInlines); else { @@ -181,37 +199,32 @@ static inline RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* re inline void InlineIterator::increment(InlineBidiResolver* resolver) { - if (!obj) + if (!m_obj) return; - if (obj->isText()) { - pos++; - if (pos >= toRenderText(obj)->textLength()) { - obj = bidiNext(block, obj, resolver); - pos = 0; - nextBreakablePosition = -1; - } - } else { - obj = bidiNext(block, obj, resolver); - pos = 0; - nextBreakablePosition = -1; + if (m_obj->isText()) { + m_pos++; + if (m_pos < toRenderText(m_obj)->textLength()) + return; } + // bidiNext can return 0, so use moveTo instead of moveToStartOf + moveTo(bidiNext(m_block, m_obj, resolver), 0); } inline bool InlineIterator::atEnd() const { - return !obj; + return !m_obj; } inline UChar InlineIterator::current() const { - if (!obj || !obj->isText()) + if (!m_obj || !m_obj->isText()) return 0; - RenderText* text = toRenderText(obj); - if (pos >= text->textLength()) + RenderText* text = toRenderText(m_obj); + if (m_pos >= text->textLength()) return 0; - return text->characters()[pos]; + return text->characters()[m_pos]; } ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const @@ -219,8 +232,8 @@ ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const if (UChar c = current()) return WTF::Unicode::direction(c); - if (obj && obj->isListMarker()) - return obj->style()->isLeftToRightDirection() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; + if (m_obj && m_obj->isListMarker()) + return m_obj->style()->isLeftToRightDirection() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; return WTF::Unicode::OtherNeutral; } @@ -228,33 +241,33 @@ ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const template<> inline void InlineBidiResolver::increment() { - current.increment(this); + m_current.increment(this); } template <> inline void InlineBidiResolver::appendRun() { - if (!emptyRun && !eor.atEnd()) { - int start = sor.pos; - RenderObject *obj = sor.obj; - while (obj && obj != eor.obj && obj != endOfLine.obj) { + if (!emptyRun && !m_eor.atEnd()) { + int start = m_sor.m_pos; + RenderObject* obj = m_sor.m_obj; + while (obj && obj != m_eor.m_obj && obj != endOfLine.m_obj) { RenderBlock::appendRunsForObject(start, obj->length(), obj, *this); start = 0; - obj = bidiNext(sor.block, obj); + obj = bidiNext(m_sor.m_block, obj); } if (obj) { - unsigned pos = obj == eor.obj ? eor.pos : UINT_MAX; - if (obj == endOfLine.obj && endOfLine.pos <= pos) { - reachedEndOfLine = true; - pos = endOfLine.pos; + unsigned pos = obj == m_eor.m_obj ? m_eor.m_pos : UINT_MAX; + if (obj == endOfLine.m_obj && endOfLine.m_pos <= pos) { + m_reachedEndOfLine = true; + pos = endOfLine.m_pos; } // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be - int end = obj->length() ? pos+1 : 0; + int end = obj->length() ? pos + 1 : 0; RenderBlock::appendRunsForObject(start, end, obj, *this); } - eor.increment(); - sor = eor; + m_eor.increment(); + m_sor = m_eor; } m_direction = WTF::Unicode::OtherNeutral; diff --git a/Source/WebCore/rendering/InlineTextBox.cpp b/Source/WebCore/rendering/InlineTextBox.cpp index 3c3e450..b614f0f 100644 --- a/Source/WebCore/rendering/InlineTextBox.cpp +++ b/Source/WebCore/rendering/InlineTextBox.cpp @@ -163,7 +163,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_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride), + IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(characters, len, textObj->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride), IntPoint(), selHeight, sPos, ePos)); int logicalWidth = r.width(); @@ -427,6 +427,14 @@ bool InlineTextBox::getEmphasisMarkPosition(RenderStyle* style, TextEmphasisPosi return !rubyText || !rubyText->firstLineBox(); } +enum RotationDirection { Counterclockwise, Clockwise }; + +static inline AffineTransform rotation(const FloatRect& boxRect, RotationDirection clockwise) +{ + return clockwise ? AffineTransform(0, 1, -1, 0, boxRect.x() + boxRect.maxY(), boxRect.maxY() - boxRect.x()) + : AffineTransform(0, -1, 1, 0, boxRect.x() - boxRect.maxY(), boxRect.x() + boxRect.maxY()); +} + void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) { if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE || @@ -485,26 +493,22 @@ void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) FloatPoint boxOrigin = locationIncludingFlipping(); boxOrigin.move(tx, ty); FloatRect boxRect(boxOrigin, IntSize(logicalWidth(), logicalHeight())); - FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + styleToUse->fontMetrics().ascent()); - RenderCombineText* combinedText = styleToUse->hasTextCombine() ? toRenderCombineText(textRenderer()) : 0; - bool shouldRotate = !isHorizontal() && (!combinedText || !combinedText->isCombined()); - if (shouldRotate) { - context->save(); - context->translate(boxRect.x(), boxRect.maxY()); - context->rotate(static_cast<float>(deg2rad(90.))); - context->translate(-boxRect.x(), -boxRect.maxY()); - } - - + RenderCombineText* combinedText = styleToUse->hasTextCombine() && textRenderer()->isCombineText() && toRenderCombineText(textRenderer())->isCombined() ? toRenderCombineText(textRenderer()) : 0; + + bool shouldRotate = !isHorizontal() && !combinedText; + if (shouldRotate) + context->concatCTM(rotation(boxRect, Clockwise)); + // Determine whether or not we have composition underlines to draw. bool containsComposition = renderer()->node() && renderer()->frame()->editor()->compositionNode() == renderer()->node(); bool useCustomUnderlines = containsComposition && renderer()->frame()->editor()->compositionUsesCustomUnderlines(); // Set our font. - int d = styleToUse->textDecorationsInEffect(); const Font& font = styleToUse->font(); + FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); + if (combinedText) combinedText->adjustTextOrigin(textOrigin, boxRect); @@ -618,7 +622,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) if (hasHyphen()) adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, length); - TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || styleToUse->visuallyOrdered()); + TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || styleToUse->visuallyOrdered()); int sPos = 0; int ePos = 0; @@ -653,11 +657,21 @@ void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) if (!emphasisMark.isEmpty()) { updateGraphicsContext(context, emphasisMarkColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); + + static TextRun objectReplacementCharacterTextRun(&objectReplacementCharacter, 1); + TextRun& emphasisMarkTextRun = combinedText ? objectReplacementCharacterTextRun : textRun; + FloatPoint emphasisMarkTextOrigin = combinedText ? FloatPoint(boxOrigin.x() + boxRect.width() / 2, boxOrigin.y() + font.fontMetrics().ascent()) : textOrigin; + if (combinedText) + context->concatCTM(rotation(boxRect, Clockwise)); + if (!paintSelectedTextSeparately || ePos <= sPos) { // FIXME: Truncate right-to-left text correctly. - paintTextWithShadows(context, font, textRun, emphasisMark, emphasisMarkOffset, 0, length, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); + paintTextWithShadows(context, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, 0, length, length, emphasisMarkTextOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); } else - paintTextWithShadows(context, font, textRun, emphasisMark, emphasisMarkOffset, ePos, sPos, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); + paintTextWithShadows(context, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, ePos, sPos, length, emphasisMarkTextOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); + + if (combinedText) + context->concatCTM(rotation(boxRect, Counterclockwise)); } if (textStrokeWidth > 0) @@ -673,16 +687,27 @@ void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) paintTextWithShadows(context, font, textRun, nullAtom, 0, sPos, ePos, length, textOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal()); if (!emphasisMark.isEmpty()) { updateGraphicsContext(context, selectionEmphasisMarkColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); - paintTextWithShadows(context, font, textRun, emphasisMark, emphasisMarkOffset, sPos, ePos, length, textOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal()); + + static TextRun objectReplacementCharacterTextRun(&objectReplacementCharacter, 1); + TextRun& emphasisMarkTextRun = combinedText ? objectReplacementCharacterTextRun : textRun; + FloatPoint emphasisMarkTextOrigin = combinedText ? FloatPoint(boxOrigin.x() + boxRect.width() / 2, boxOrigin.y() + font.fontMetrics().ascent()) : textOrigin; + if (combinedText) + context->concatCTM(rotation(boxRect, Clockwise)); + + paintTextWithShadows(context, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, sPos, ePos, length, emphasisMarkTextOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal()); + + if (combinedText) + context->concatCTM(rotation(boxRect, Counterclockwise)); } if (selectionStrokeWidth > 0) context->restore(); } // Paint decorations - if (d != TDNONE && paintInfo.phase != PaintPhaseSelection) { + int textDecorations = styleToUse->textDecorationsInEffect(); + if (textDecorations != TDNONE && paintInfo.phase != PaintPhaseSelection) { updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); - paintDecoration(context, boxOrigin, d, textShadow); + paintDecoration(context, boxOrigin, textDecorations, textShadow); } if (paintInfo.phase == PaintPhaseForeground) { @@ -715,7 +740,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) } if (shouldRotate) - context->restore(); + context->concatCTM(rotation(boxRect, Counterclockwise)); } void InlineTextBox::selectionStartEnd(int& sPos, int& ePos) @@ -772,7 +797,7 @@ void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& b int selHeight = selectionHeight(); FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY); context->clip(FloatRect(localOrigin, FloatSize(m_logicalWidth, selHeight))); - context->drawHighlightForText(font, TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), + context->drawHighlightForText(font, TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), localOrigin, selHeight, c, style->colorSpace(), sPos, ePos); context->restore(); @@ -796,7 +821,7 @@ void InlineTextBox::paintCompositionBackground(GraphicsContext* context, const F int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop(); int selHeight = selectionHeight(); FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY); - context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), + context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), localOrigin, selHeight, c, style->colorSpace(), sPos, ePos); context->restore(); @@ -958,7 +983,7 @@ void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, const Floa int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop(); int selHeight = selectionHeight(); FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY); - TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); // FIXME: Convert the document markers to float rects. IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, selHeight, startPosition, endPosition)); @@ -1003,7 +1028,7 @@ void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const FloatPoint& 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_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !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, selectionTop()), selHeight, sPos, ePos)); @@ -1031,7 +1056,7 @@ void InlineTextBox::computeRectForReplacementMarker(const DocumentMarker& marker 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_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); IntPoint startPoint = IntPoint(m_x, y); // Compute and store the rect associated with this marker. @@ -1192,7 +1217,7 @@ int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs RenderStyle* style = text->style(m_firstLine); const Font* f = &style->font(); int offset = f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len, - textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), + textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), lineOffset - logicalLeft(), includePartialGlyphs); if (blockIsInOppositeDirection && (!offset || offset == m_len)) return !offset ? m_len : 0; @@ -1212,7 +1237,7 @@ float InlineTextBox::positionForOffset(int offset) const int from = !isLeftToRightDirection() ? offset - m_start : 0; int to = !isLeftToRightDirection() ? m_len : offset - m_start; // FIXME: Do we need to add rightBearing here? - return f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, trailingExpansionBehavior(), !isLeftToRightDirection(), m_dirOverride), + return f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_expansion, expansionBehavior(), !isLeftToRightDirection(), m_dirOverride), IntPoint(logicalLeft(), 0), 0, from, to).maxX(); } diff --git a/Source/WebCore/rendering/InlineTextBox.h b/Source/WebCore/rendering/InlineTextBox.h index d894b85..98a1b78 100644 --- a/Source/WebCore/rendering/InlineTextBox.h +++ b/Source/WebCore/rendering/InlineTextBox.h @@ -69,6 +69,10 @@ public: bool hasHyphen() const { return m_hasEllipsisBoxOrHyphen; } void setHasHyphen(bool hasHyphen) { m_hasEllipsisBoxOrHyphen = hasHyphen; } + + bool canHaveLeadingExpansion() const { return m_hasSelectedChildrenOrCanHaveLeadingExpansion; } + void setCanHaveLeadingExpansion(bool canHaveLeadingExpansion) { m_hasSelectedChildrenOrCanHaveLeadingExpansion = canHaveLeadingExpansion; } + static inline bool compareByStart(const InlineTextBox* first, const InlineTextBox* second) { return first->start() < second->start(); } virtual int baselinePosition(FontBaseline) const; @@ -158,7 +162,11 @@ private: void paintTextMatchMarker(GraphicsContext*, const FloatPoint& boxOrigin, const DocumentMarker&, RenderStyle*, const Font&); void computeRectForReplacementMarker(const DocumentMarker&, RenderStyle*, const Font&); - TextRun::TrailingExpansionBehavior trailingExpansionBehavior() const { return m_expansion && nextLeafChild() ? TextRun::AllowTrailingExpansion : TextRun::ForbidTrailingExpansion; } + TextRun::ExpansionBehavior expansionBehavior() const + { + return (canHaveLeadingExpansion() ? TextRun::AllowLeadingExpansion : TextRun::ForbidLeadingExpansion) + | (m_expansion && nextLeafChild() ? TextRun::AllowTrailingExpansion : TextRun::ForbidTrailingExpansion); + } }; inline RenderText* InlineTextBox::textRenderer() const diff --git a/Source/WebCore/rendering/MediaControlElements.cpp b/Source/WebCore/rendering/MediaControlElements.cpp index 32aac90..07df4d9 100644 --- a/Source/WebCore/rendering/MediaControlElements.cpp +++ b/Source/WebCore/rendering/MediaControlElements.cpp @@ -41,9 +41,11 @@ #include "MediaControls.h" #include "MouseEvent.h" #include "Page.h" +#include "RenderFlexibleBox.h" #include "RenderMedia.h" #include "RenderSlider.h" #include "RenderTheme.h" +#include "RenderView.h" #include "Settings.h" namespace WebCore { @@ -250,11 +252,41 @@ const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const // ---------------------------- +class RenderMediaVolumeSliderContainer : public RenderBlock { +public: + RenderMediaVolumeSliderContainer(Node*); + +private: + virtual void layout(); +}; + +RenderMediaVolumeSliderContainer::RenderMediaVolumeSliderContainer(Node* node) + : RenderBlock(node) +{ +} + +void RenderMediaVolumeSliderContainer::layout() +{ + RenderBlock::layout(); + if (style()->display() == NONE || !previousSibling() || !previousSibling()->isBox()) + return; + + RenderBox* buttonBox = toRenderBox(previousSibling()); + + if (view()) + view()->disableLayoutState(); + + IntPoint offset = theme()->volumeSliderOffsetFromMuteButton(buttonBox, IntSize(width(), height())); + setX(offset.x() + buttonBox->offsetLeft()); + setY(offset.y() + buttonBox->offsetTop()); + + if (view()) + view()->enableLayoutState(); +} + inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(HTMLMediaElement* mediaElement) : MediaControlElement(mediaElement) , m_isVisible(false) - , m_x(0) - , m_y(0) { } @@ -263,12 +295,15 @@ PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderCon return adoptRef(new MediaControlVolumeSliderContainerElement(mediaElement)); } +RenderObject* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderMediaVolumeSliderContainer(this); +} + PassRefPtr<RenderStyle> MediaControlVolumeSliderContainerElement::styleForElement() { RefPtr<RenderStyle> style = MediaControlElement::styleForElement(); style->setPosition(AbsolutePosition); - style->setLeft(Length(m_x, Fixed)); - style->setTop(Length(m_y, Fixed)); style->setDisplay(m_isVisible ? BLOCK : NONE); return style; } @@ -280,14 +315,6 @@ void MediaControlVolumeSliderContainerElement::setVisible(bool visible) m_isVisible = visible; } -void MediaControlVolumeSliderContainerElement::setPosition(int x, int y) -{ - if (x == m_x && y == m_y) - return; - m_x = x; - m_y = y; -} - bool MediaControlVolumeSliderContainerElement::hitTest(const IntPoint& absPoint) { if (renderer() && renderer()->style()->hasAppearance()) @@ -776,6 +803,7 @@ PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(HTML { RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(mediaElement)); timeline->setType("range"); + timeline->setAttribute(precisionAttr, "float"); return timeline.release(); } @@ -841,6 +869,9 @@ PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::cre { RefPtr<MediaControlVolumeSliderElement> slider = adoptRef(new MediaControlVolumeSliderElement(mediaElement)); slider->setType("range"); + slider->setAttribute(precisionAttr, "float"); + slider->setAttribute(maxAttr, "1"); + slider->setAttribute(valueAttr, String::number(mediaElement->volume())); return slider.release(); } @@ -882,6 +913,29 @@ const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const // ---------------------------- +inline MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(HTMLMediaElement* mediaElement) +: MediaControlVolumeSliderElement(mediaElement) +{ +} + +PassRefPtr<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(new MediaControlFullscreenVolumeSliderElement(mediaElement)); + slider->setType("range"); + slider->setAttribute(precisionAttr, "float"); + slider->setAttribute(maxAttr, "1"); + slider->setAttribute(valueAttr, String::number(mediaElement->volume())); + return slider.release(); +} + +const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-slider")); + return id; +} + +// ---------------------------- + inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(HTMLMediaElement* mediaElement) : MediaControlInputElement(mediaElement, MediaFullscreenButton) { @@ -924,36 +978,95 @@ const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const // ---------------------------- -inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(HTMLMediaElement* mediaElement) - : MediaControlElement(mediaElement) - , m_currentValue(0) - , m_isVisible(true) +inline MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(HTMLMediaElement* mediaElement) +: MediaControlInputElement(mediaElement, MediaUnMuteButton) { } -PassRefPtr<RenderStyle> MediaControlTimeDisplayElement::styleForElement() +PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(HTMLMediaElement* mediaElement) { - RefPtr<RenderStyle> style = MediaControlElement::styleForElement(); - if (!m_isVisible) { - style = RenderStyle::clone(style.get()); - style->setWidth(Length(0, Fixed)); + RefPtr<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMinButtonElement(mediaElement)); + button->setType("button"); + return button.release(); +} + +void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + ExceptionCode code = 0; + mediaElement()->setVolume(0, code); + event->setDefaultHandled(); } - return style; + HTMLInputElement::defaultEventHandler(event); } -void MediaControlTimeDisplayElement::setVisible(bool visible) +const AtomicString& MediaControlFullscreenVolumeMinButtonElement::shadowPseudoId() const { - if (visible == m_isVisible) - return; - m_isVisible = visible; + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-min-button")); + return id; +} - // This function is used during the RenderMedia::layout() - // call, where we cannot change the renderer at this time. - if (!renderer() || !renderer()->style()) - return; +// ---------------------------- - RefPtr<RenderStyle> style = styleForElement(); - renderer()->setStyle(style.get()); +inline MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(HTMLMediaElement* mediaElement) +: MediaControlInputElement(mediaElement, MediaMuteButton) +{ +} + +PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMaxButtonElement(mediaElement)); + button->setType("button"); + return button.release(); +} + +void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + ExceptionCode code = 0; + mediaElement()->setVolume(1, code); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +const AtomicString& MediaControlFullscreenVolumeMaxButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-max-button")); + return id; +} + +// ---------------------------- + +class RenderMediaControlTimeDisplay : public RenderFlexibleBox { +public: + RenderMediaControlTimeDisplay(Node*); + +private: + virtual void layout(); +}; + +RenderMediaControlTimeDisplay::RenderMediaControlTimeDisplay(Node* node) + : RenderFlexibleBox(node) +{ +} + +// We want the timeline slider to be at least 100 pixels wide. +// FIXME: Eliminate hard-coded widths altogether. +static const int minWidthToDisplayTimeDisplays = 45 + 100 + 45; + +void RenderMediaControlTimeDisplay::layout() +{ + RenderFlexibleBox::layout(); + RenderBox* timelineContainerBox = parentBox(); + if (timelineContainerBox && timelineContainerBox->width() < minWidthToDisplayTimeDisplays) + setWidth(0); +} + +inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(HTMLMediaElement* mediaElement) + : MediaControlElement(mediaElement) + , m_currentValue(0) +{ } void MediaControlTimeDisplayElement::setCurrentValue(float time) @@ -961,6 +1074,11 @@ void MediaControlTimeDisplayElement::setCurrentValue(float time) m_currentValue = time; } +RenderObject* MediaControlTimeDisplayElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderMediaControlTimeDisplay(this); +} + // ---------------------------- PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(HTMLMediaElement* mediaElement) diff --git a/Source/WebCore/rendering/MediaControlElements.h b/Source/WebCore/rendering/MediaControlElements.h index 44e8811..77116e3 100644 --- a/Source/WebCore/rendering/MediaControlElements.h +++ b/Source/WebCore/rendering/MediaControlElements.h @@ -142,16 +142,16 @@ public: virtual PassRefPtr<RenderStyle> styleForElement(); void setVisible(bool); bool isVisible() { return m_isVisible; } - void setPosition(int x, int y); bool hitTest(const IntPoint& absPoint); private: MediaControlVolumeSliderContainerElement(HTMLMediaElement*); + + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); virtual MediaControlElementType displayType() const; virtual const AtomicString& shadowPseudoId() const; bool m_isVisible; - int m_x, m_y; }; // ---------------------------- @@ -372,9 +372,10 @@ public: virtual void defaultEventHandler(Event*); void update(); -private: +protected: MediaControlVolumeSliderElement(HTMLMediaElement*); +private: virtual const AtomicString& shadowPseudoId() const; }; @@ -394,10 +395,48 @@ private: // ---------------------------- -class MediaControlTimeDisplayElement : public MediaControlElement { +class MediaControlFullscreenVolumeSliderElement : public MediaControlVolumeSliderElement { public: - void setVisible(bool); + static PassRefPtr<MediaControlFullscreenVolumeSliderElement> create(HTMLMediaElement*); + +private: + MediaControlFullscreenVolumeSliderElement(HTMLMediaElement*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlFullscreenVolumeMinButtonElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> create(HTMLMediaElement*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlFullscreenVolumeMinButtonElement(HTMLMediaElement*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlFullscreenVolumeMaxButtonElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> create(HTMLMediaElement*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlFullscreenVolumeMaxButtonElement(HTMLMediaElement*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- +class MediaControlTimeDisplayElement : public MediaControlElement { +public: void setCurrentValue(float); float currentValue() const { return m_currentValue; } @@ -405,9 +444,9 @@ protected: MediaControlTimeDisplayElement(HTMLMediaElement*); private: - virtual PassRefPtr<RenderStyle> styleForElement(); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + float m_currentValue; - bool m_isVisible; }; // ---------------------------- diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp index 7488ff5..919e23b 100644 --- a/Source/WebCore/rendering/RenderBlock.cpp +++ b/Source/WebCore/rendering/RenderBlock.cpp @@ -34,6 +34,7 @@ #include "HTMLFormElement.h" #include "HTMLNames.h" #include "HitTestResult.h" +#include "InlineIterator.h" #include "InlineTextBox.h" #include "PaintInfo.h" #include "RenderCombineText.h" @@ -51,6 +52,7 @@ #include "Settings.h" #include "TextRun.h" #include "TransformState.h" +#include "visible_units.h" #include <wtf/StdLibExtras.h> #ifdef ANDROID_LAYOUT @@ -243,8 +245,7 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty // FIXME: We could save this call when the change only affected non-inherited properties for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (child->isAnonymousBlock()) { - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(style()); + RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style()); if (style()->specifiesColumns()) { if (child->style()->specifiesColumns()) newStyle->inheritColumnPropertiesFrom(style()); @@ -937,6 +938,9 @@ static bool canMergeContiguousAnonymousBlocks(RenderObject* oldChild, RenderObje if (oldChild->documentBeingDestroyed() || oldChild->isInline() || oldChild->virtualContinuation()) return false; + if (oldChild->parent() && oldChild->parent()->isDetails()) + return false; + if ((prev && (!prev->isAnonymousBlock() || toRenderBlock(prev)->continuation() || toRenderBlock(prev)->beingDestroyed())) || (next && (!next->isAnonymousBlock() || toRenderBlock(next)->continuation() || toRenderBlock(next)->beingDestroyed()))) return false; @@ -979,8 +983,7 @@ void RenderBlock::removeChild(RenderObject* oldChild) // to clear out inherited column properties by just making a new style, and to also clear the // column span flag if it is set. ASSERT(!inlineChildrenBlock->continuation()); - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(style()); + RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style()); children()->removeChildNode(this, inlineChildrenBlock, inlineChildrenBlock->hasLayer()); inlineChildrenBlock->setStyle(newStyle); @@ -1136,7 +1139,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren, int pageLogicalHeight) if (isInline() && !isInlineBlockOrInlineTable()) // Inline <form>s inside various table elements can return; // cause us to come in here. Just bail. - if (!relayoutChildren && layoutOnlyPositionedObjects()) + if (!relayoutChildren && simplifiedLayout()) return; LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout()); @@ -1292,7 +1295,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren, int pageLogicalHeight) } IntRect repaintRect; - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) repaintRect = IntRect(repaintLogicalLeft, repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop); else repaintRect = IntRect(repaintLogicalTop, repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft); @@ -1331,17 +1334,21 @@ void RenderBlock::addOverflowFromChildren() ColumnInfo* colInfo = columnInfo(); if (columnCount(colInfo)) { IntRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1); - if (style()->isHorizontalWritingMode()) { + if (isHorizontalWritingMode()) { int overflowLeft = !style()->isLeftToRightDirection() ? min(0, lastRect.x()) : 0; int overflowRight = style()->isLeftToRightDirection() ? max(width(), lastRect.maxX()) : 0; int overflowHeight = borderBefore() + paddingBefore() + colInfo->columnHeight(); addLayoutOverflow(IntRect(overflowLeft, 0, overflowRight - overflowLeft, overflowHeight)); + if (!hasOverflowClip()) + addVisualOverflow(IntRect(overflowLeft, 0, overflowRight - overflowLeft, overflowHeight)); } else { IntRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1); int overflowTop = !style()->isLeftToRightDirection() ? min(0, lastRect.y()) : 0; int overflowBottom = style()->isLeftToRightDirection() ? max(height(), lastRect.maxY()) : 0; int overflowWidth = borderBefore() + paddingBefore() + colInfo->columnHeight(); addLayoutOverflow(IntRect(0, overflowTop, overflowWidth, overflowBottom - overflowTop)); + if (!hasOverflowClip()) + addVisualOverflow(IntRect(0, overflowTop, overflowWidth, overflowBottom - overflowTop)); } } } @@ -1364,7 +1371,7 @@ void RenderBlock::computeOverflow(int oldClientAfterEdge, bool recomputeFloats) // be considered reachable. IntRect clientRect(clientBoxRect()); IntRect rectToApply; - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) rectToApply = IntRect(clientRect.x(), clientRect.y(), 1, max(0, oldClientAfterEdge - clientRect.y())); else rectToApply = IntRect(clientRect.x(), clientRect.y(), max(0, oldClientAfterEdge - clientRect.x()), 1); @@ -1422,34 +1429,31 @@ bool RenderBlock::expandsToEncloseOverhangingFloats() const void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marginInfo) { - bool isHorizontal = style()->isHorizontalWritingMode(); - bool hasStaticInlinePosition = child->style()->hasStaticInlinePosition(isHorizontal); + bool isHorizontal = isHorizontalWritingMode(); bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontal); RenderLayer* childLayer = child->layer(); - if (hasStaticInlinePosition) - childLayer->setStaticInlinePosition(borderAndPaddingStart()); - - if (hasStaticBlockPosition) { - int logicalTop = logicalHeight(); - if (!marginInfo.canCollapseWithMarginBefore()) { - child->computeBlockDirectionMargins(this); - int marginBefore = marginBeforeForChild(child); - int collapsedBeforePos = marginInfo.positiveMargin(); - int collapsedBeforeNeg = marginInfo.negativeMargin(); - if (marginBefore > 0) { - if (marginBefore > collapsedBeforePos) - collapsedBeforePos = marginBefore; - } else { - if (-marginBefore > collapsedBeforeNeg) - collapsedBeforeNeg = -marginBefore; - } - logicalTop += (collapsedBeforePos - collapsedBeforeNeg) - marginBefore; + childLayer->setStaticInlinePosition(borderAndPaddingStart()); + + int logicalTop = logicalHeight(); + if (!marginInfo.canCollapseWithMarginBefore()) { + child->computeBlockDirectionMargins(this); + int marginBefore = marginBeforeForChild(child); + int collapsedBeforePos = marginInfo.positiveMargin(); + int collapsedBeforeNeg = marginInfo.negativeMargin(); + if (marginBefore > 0) { + if (marginBefore > collapsedBeforePos) + collapsedBeforePos = marginBefore; + } else { + if (-marginBefore > collapsedBeforeNeg) + collapsedBeforeNeg = -marginBefore; } - if (childLayer->staticBlockPosition() != logicalTop) { - childLayer->setStaticBlockPosition(logicalTop); + logicalTop += (collapsedBeforePos - collapsedBeforeNeg) - marginBefore; + } + if (childLayer->staticBlockPosition() != logicalTop) { + childLayer->setStaticBlockPosition(logicalTop); + if (hasStaticBlockPosition) child->setChildNeedsLayout(true, false); - } } } @@ -1818,7 +1822,7 @@ void RenderBlock::handleAfterSideOfBlock(int beforeSide, int afterSide, MarginIn void RenderBlock::setLogicalLeftForChild(RenderBox* child, int logicalLeft, ApplyLayoutDeltaMode applyDelta) { - if (style()->isHorizontalWritingMode()) { + if (isHorizontalWritingMode()) { if (applyDelta == ApplyLayoutDelta) view()->addLayoutDelta(IntSize(child->x() - logicalLeft, 0)); child->setX(logicalLeft); @@ -1831,7 +1835,7 @@ void RenderBlock::setLogicalLeftForChild(RenderBox* child, int logicalLeft, Appl void RenderBlock::setLogicalTopForChild(RenderBox* child, int logicalTop, ApplyLayoutDeltaMode applyDelta) { - if (style()->isHorizontalWritingMode()) { + if (isHorizontalWritingMode()) { if (applyDelta == ApplyLayoutDelta) view()->addLayoutDelta(IntSize(0, child->y() - logicalTop)); child->setY(logicalTop); @@ -2080,21 +2084,58 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int ASSERT(oldLayoutDelta == view()->layoutDelta()); } -bool RenderBlock::layoutOnlyPositionedObjects() +void RenderBlock::simplifiedNormalFlowLayout() { - if (!posChildNeedsLayout() || normalChildNeedsLayout() || selfNeedsLayout()) + if (childrenInline()) { + ListHashSet<RootInlineBox*> lineBoxes; + bool endOfInline = false; + RenderObject* o = bidiFirst(this, 0, false); + while (o) { + if (!o->isPositioned() && (o->isReplaced() || o->isFloating())) { + o->layoutIfNeeded(); + if (toRenderBox(o)->inlineBoxWrapper()) { + RootInlineBox* box = toRenderBox(o)->inlineBoxWrapper()->root(); + lineBoxes.add(box); + } + } else if (o->isText() || (o->isRenderInline() && !endOfInline)) + o->setNeedsLayout(false); + o = bidiNext(this, o, 0, false, &endOfInline); + } + + // FIXME: Glyph overflow will get lost in this case, but not really a big deal. + GlyphOverflowAndFallbackFontsMap textBoxDataMap; + for (ListHashSet<RootInlineBox*>::const_iterator it = lineBoxes.begin(); it != lineBoxes.end(); ++it) { + RootInlineBox* box = *it; + box->computeOverflow(box->lineTop(), box->lineBottom(), document()->inNoQuirksMode(), textBoxDataMap); + } + } else { + for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) { + if (!box->isPositioned()) + box->layoutIfNeeded(); + } + } +} + +bool RenderBlock::simplifiedLayout() +{ + if ((!posChildNeedsLayout() && !needsSimplifiedNormalFlowLayout()) || normalChildNeedsLayout() || selfNeedsLayout()) return false; LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - + if (needsPositionedMovementLayout()) { tryLayoutDoingPositionedMovementOnly(); if (needsLayout()) return false; } - // All we have to is lay out our positioned objects. - layoutPositionedObjects(false); + // Lay out positioned descendants or objects that just need to recompute overflow. + if (needsSimplifiedNormalFlowLayout()) + simplifiedNormalFlowLayout(); + + // Lay out our positioned objects if our positioned child bit is set. + if (posChildNeedsLayout()) + layoutPositionedObjects(false); // Recompute our overflow information. // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only @@ -2137,7 +2178,7 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren) // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is // positioned explicitly) this should not incur a performance penalty. - if (relayoutChildren || (r->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()) && r->parent() != this && r->parent()->isBlockFlow())) + if (relayoutChildren || (r->style()->hasStaticBlockPosition(isHorizontalWritingMode()) && r->parent() != this && r->parent()->isBlockFlow())) r->setChildNeedsLayout(true, false); // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. @@ -2221,6 +2262,7 @@ void RenderBlock::paint(PaintInfo& paintInfo, int tx, int ty) // paints the root's background. if (!isRoot()) { IntRect overflowBox = visualOverflowRect(); + flipForWritingMode(overflowBox); overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); overflowBox.move(tx, ty); if (!overflowBox.intersects(paintInfo.rect)) @@ -2259,7 +2301,7 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, int tx, int ty) for (unsigned i = 0; i < colCount; i++) { IntRect colRect = columnRectAt(colInfo, i); - int inlineDirectionSize = style()->isHorizontalWritingMode() ? colRect.width() : colRect.height(); + int inlineDirectionSize = isHorizontalWritingMode() ? colRect.width() : colRect.height(); // Move to the next position. if (style()->isLeftToRightDirection()) { @@ -2272,10 +2314,10 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, int tx, int ty) // Now paint the column rule. if (i < colCount - 1) { - int ruleLeft = style()->isHorizontalWritingMode() ? tx + ruleLogicalLeft - ruleWidth / 2 + ruleAdd : tx + borderBefore() + paddingBefore(); - int ruleRight = style()->isHorizontalWritingMode() ? ruleLeft + ruleWidth : ruleLeft + contentWidth(); - int ruleTop = style()->isHorizontalWritingMode() ? ty + borderTop() + paddingTop() : ty + ruleLogicalLeft - ruleWidth / 2 + ruleAdd; - int ruleBottom = style()->isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleWidth; + int ruleLeft = isHorizontalWritingMode() ? tx + ruleLogicalLeft - ruleWidth / 2 + ruleAdd : tx + borderBefore() + paddingBefore(); + int ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleWidth : ruleLeft + contentWidth(); + int ruleTop = isHorizontalWritingMode() ? ty + borderTop() + paddingTop() : ty + ruleLogicalLeft - ruleWidth / 2 + ruleAdd; + int ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleWidth; drawLineForBoxSide(paintInfo.context, ruleLeft, ruleTop, ruleRight, ruleBottom, style()->isLeftToRightDirection() ? BSLeft : BSRight, ruleColor, ruleStyle, 0, 0); } @@ -2297,8 +2339,8 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, int tx, int ty, bool // For each rect, we clip to the rect, and then we adjust our coords. IntRect colRect = columnRectAt(colInfo, i); flipForWritingMode(colRect); - int logicalLeftOffset = (style()->isHorizontalWritingMode() ? colRect.x() : colRect.y()) - logicalLeftOffsetForContent(); - IntSize offset = style()->isHorizontalWritingMode() ? IntSize(logicalLeftOffset, currLogicalTopOffset) : IntSize(currLogicalTopOffset, logicalLeftOffset); + int logicalLeftOffset = (isHorizontalWritingMode() ? colRect.x() : colRect.y()) - logicalLeftOffsetForContent(); + IntSize offset = isHorizontalWritingMode() ? IntSize(logicalLeftOffset, currLogicalTopOffset) : IntSize(currLogicalTopOffset, logicalLeftOffset); colRect.move(tx, ty); PaintInfo info(paintInfo); info.rect.intersect(colRect); @@ -2321,7 +2363,7 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, int tx, int ty, bool context->restore(); } - int blockDelta = (style()->isHorizontalWritingMode() ? colRect.height() : colRect.width()); + int blockDelta = (isHorizontalWritingMode() ? colRect.height() : colRect.width()); if (style()->isFlippedBlocksWritingMode()) currLogicalTopOffset += blockDelta; else @@ -2510,7 +2552,7 @@ IntPoint RenderBlock::flipFloatForWritingMode(const FloatingObject* child, const // This is similar to the ParentToChildFlippingAdjustment in RenderBox::flipForWritingMode. We have to subtract out our left/top offsets twice, since // it's going to get added back in. We hide this complication here so that the calling code looks normal for the unflipped // case. - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return IntPoint(point.x(), point.y() + height() - child->renderer()->height() - 2 * yPositionForFloatIncludingMargin(child)); return IntPoint(point.x() + width() - child->width() - 2 * xPositionForFloatIncludingMargin(child), point.y()); } @@ -2738,18 +2780,18 @@ static void clipOutPositionedObjects(const PaintInfo* paintInfo, const IntPoint& static int blockDirectionOffset(RenderBlock* rootBlock, const IntSize& offsetFromRootBlock) { - return rootBlock->style()->isHorizontalWritingMode() ? offsetFromRootBlock.height() : offsetFromRootBlock.width(); + return rootBlock->isHorizontalWritingMode() ? offsetFromRootBlock.height() : offsetFromRootBlock.width(); } static int inlineDirectionOffset(RenderBlock* rootBlock, const IntSize& offsetFromRootBlock) { - return rootBlock->style()->isHorizontalWritingMode() ? offsetFromRootBlock.width() : offsetFromRootBlock.height(); + return rootBlock->isHorizontalWritingMode() ? offsetFromRootBlock.width() : offsetFromRootBlock.height(); } IntRect RenderBlock::logicalRectToPhysicalRect(const IntPoint& rootBlockPhysicalPosition, const IntRect& logicalRect) { IntRect result; - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) result = logicalRect; else result = IntRect(logicalRect.y(), logicalRect.x(), logicalRect.height(), logicalRect.width()); @@ -2846,10 +2888,10 @@ GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const IntPoint selTop, paintInfo)); IntRect logicalRect(curr->logicalLeft(), selTop, curr->logicalWidth(), selTop + selHeight); - logicalRect.move(style()->isHorizontalWritingMode() ? offsetFromRootBlock : IntSize(offsetFromRootBlock.height(), offsetFromRootBlock.width())); + logicalRect.move(isHorizontalWritingMode() ? offsetFromRootBlock : IntSize(offsetFromRootBlock.height(), offsetFromRootBlock.width())); IntRect physicalRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); - if (!paintInfo || (style()->isHorizontalWritingMode() && physicalRect.y() < paintInfo->rect.maxY() && physicalRect.maxY() > paintInfo->rect.y()) - || (!style()->isHorizontalWritingMode() && physicalRect.x() < paintInfo->rect.maxX() && physicalRect.maxX() > paintInfo->rect.x())) + if (!paintInfo || (isHorizontalWritingMode() && physicalRect.y() < paintInfo->rect.maxY() && physicalRect.maxY() > paintInfo->rect.y()) + || (!isHorizontalWritingMode() && physicalRect.x() < paintInfo->rect.maxX() && physicalRect.maxX() > paintInfo->rect.x())) result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, selTop, selHeight, paintInfo)); lastSelectedLine = curr; @@ -3294,12 +3336,23 @@ bool RenderBlock::positionNewFloats() return true; } -bool RenderBlock::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine) +bool RenderBlock::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, bool firstLine, int& lineLeftOffset, int& lineRightOffset) { bool didPosition = positionNewFloats(); - if (!didPosition || !newFloat->m_paginationStrut) + if (!didPosition) return didPosition; + + int blockOffset = logicalHeight(); + if (blockOffset >= logicalTopForFloat(newFloat) && blockOffset < logicalBottomForFloat(newFloat)) { + if (newFloat->type() == FloatingObject::FloatLeft) + lineLeftOffset = logicalRightForFloat(newFloat); + else + lineRightOffset = logicalLeftForFloat(newFloat); + } + if (!newFloat->m_paginationStrut) + return didPosition; + FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); ASSERT(floatingObjectSet.last() == newFloat); @@ -3329,8 +3382,10 @@ bool RenderBlock::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObjec } } - setLogicalHeight(logicalHeight() + paginationStrut); - + setLogicalHeight(blockOffset + paginationStrut); + lineLeftOffset = logicalLeftOffsetForLine(logicalHeight(), firstLine); + lineRightOffset = logicalRightOffsetForLine(logicalHeight(), firstLine); + return didPosition; } @@ -3416,6 +3471,11 @@ HashSet<RenderBox*>* RenderBlock::percentHeightDescendants() const return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0; } +// FIXME: The logicalLeftOffsetForLine/logicalRightOffsetForLine functions are very slow if there are many floats +// present. We need to add a structure to floating objects to represent "lines" of floats. Then instead of checking +// each float individually, we'd just walk backwards through the "lines" and stop when we hit a line that is fully above +// the vertical offset that we'd like to check. Computing the "lines" would be rather complicated, but could replace the left +// objects and right objects count hack that is currently used here. int RenderBlock::logicalLeftOffsetForLine(int logicalTop, int fixedOffset, bool applyTextIndent, int* heightRemaining) const { int left = fixedOffset; @@ -3423,18 +3483,23 @@ int RenderBlock::logicalLeftOffsetForLine(int logicalTop, int fixedOffset, bool if (heightRemaining) *heightRemaining = 1; + // We know the list is non-empty, since we have "left" objects to search for. + // Therefore we can assume that begin != end, and that we can do at least one + // decrement. FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); - FloatingObjectSetIterator end = floatingObjectSet.end(); - for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) { + FloatingObjectSetIterator begin = floatingObjectSet.begin(); + FloatingObjectSetIterator it = floatingObjectSet.end(); + do { + --it; FloatingObject* r = *it; if (r->isPlaced() && logicalTopForFloat(r) <= logicalTop && logicalBottomForFloat(r) > logicalTop && r->type() == FloatingObject::FloatLeft && logicalRightForFloat(r) > left) { - left = logicalRightForFloat(r); + left = max(left, logicalRightForFloat(r)); if (heightRemaining) *heightRemaining = logicalBottomForFloat(r) - logicalTop; } - } + } while (it != begin); } if (applyTextIndent && style()->isLeftToRightDirection()) { @@ -3454,18 +3519,24 @@ int RenderBlock::logicalRightOffsetForLine(int logicalTop, int fixedOffset, bool if (m_floatingObjects && m_floatingObjects->hasRightObjects()) { if (heightRemaining) *heightRemaining = 1; + + // We know the list is non-empty, since we have "right" objects to search for. + // Therefore we can assume that begin != end, and that we can do at least one + // decrement. FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); - FloatingObjectSetIterator end = floatingObjectSet.end(); - for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) { + FloatingObjectSetIterator begin = floatingObjectSet.begin(); + FloatingObjectSetIterator it = floatingObjectSet.end(); + do { + --it; FloatingObject* r = *it; if (r->isPlaced() && logicalTopForFloat(r) <= logicalTop && logicalBottomForFloat(r) > logicalTop && r->type() == FloatingObject::FloatRight && logicalLeftForFloat(r) < right) { - right = logicalLeftForFloat(r); + right = min(right, logicalLeftForFloat(r)); if (heightRemaining) *heightRemaining = logicalBottomForFloat(r) - logicalTop; } - } + } while (it != begin); } if (applyTextIndent && !style()->isLeftToRightDirection()) { @@ -3663,8 +3734,8 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int logicalLeftOffset, if (logicalBottom > logicalHeight()) { // If the object is not in the list, we add it now. if (!containsFloat(r->m_renderer)) { - int leftOffset = style()->isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset; - int topOffset = style()->isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset; + int leftOffset = isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset; + int topOffset = isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset; FloatingObject* floatingObj = new FloatingObject(r->type(), IntRect(r->x() - leftOffset, r->y() - topOffset, r->width(), r->height())); floatingObj->m_renderer = r->m_renderer; @@ -3712,7 +3783,7 @@ void RenderBlock::addIntrudingFloats(RenderBlock* prev, int logicalLeftOffset, i if (!prev->m_floatingObjects) return; - logicalLeftOffset += (style()->isHorizontalWritingMode() ? marginLeft() : marginTop()); + logicalLeftOffset += (isHorizontalWritingMode() ? marginLeft() : marginTop()); FloatingObjectSet& prevSet = prev->m_floatingObjects->set(); FloatingObjectSetIterator prevEnd = prevSet.end(); @@ -3720,8 +3791,8 @@ void RenderBlock::addIntrudingFloats(RenderBlock* prev, int logicalLeftOffset, i FloatingObject* r = *prevIt; if (logicalBottomForFloat(r) > logicalTopOffset) { if (!m_floatingObjects || !m_floatingObjects->set().contains(r)) { - int leftOffset = style()->isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset; - int topOffset = style()->isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset; + int leftOffset = isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset; + int topOffset = isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset; FloatingObject* floatingObj = new FloatingObject(r->type(), IntRect(r->x() - leftOffset, r->y() - topOffset, r->width(), r->height())); @@ -3731,7 +3802,7 @@ void RenderBlock::addIntrudingFloats(RenderBlock* prev, int logicalLeftOffset, i // into account. Only apply this code if prev is the parent, since otherwise the left margin // will get applied twice. if (prev != parent()) { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) floatingObj->setX(floatingObj->x() + prev->marginLeft()); else floatingObj->setY(floatingObj->y() + prev->marginTop()); @@ -3868,7 +3939,7 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); bool useClip = (hasControlClip() || useOverflowClip); IntRect hitTestArea(result.rectForPoint(_x, _y)); - bool checkChildren = !useClip || (hasControlClip() ? controlClipRect(tx, ty).intersects(hitTestArea) : overflowClipRect(tx, ty).intersects(hitTestArea)); + bool checkChildren = !useClip || (hasControlClip() ? controlClipRect(tx, ty).intersects(hitTestArea) : overflowClipRect(tx, ty, IncludeOverlayScrollbarSize).intersects(hitTestArea)); if (checkChildren) { // Hit test descendants first. int scrolledX = tx; @@ -3945,7 +4016,7 @@ bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& r int logicalLeft = logicalLeftOffsetForContent(); int currLogicalTopOffset = 0; int i; - bool isHorizontal = style()->isHorizontalWritingMode(); + bool isHorizontal = isHorizontalWritingMode(); for (i = 0; i < colCount; i++) { IntRect colRect = columnRectAt(colInfo, i); int blockDelta = (isHorizontal ? colRect.height() : colRect.width()); @@ -4038,12 +4109,12 @@ static VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock* ancestor = ancestor->parent(); // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal - if (!ancestor || ancestor->node()->isContentEditable() == childNode->isContentEditable()) + if (!ancestor || ancestor->node()->rendererIsEditable() == childNode->rendererIsEditable()) return child->positionForPoint(pointInChildCoordinates); // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child int childMiddle = parent->logicalWidthForChild(child) / 2; - int logicalLeft = parent->style()->isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y(); + int logicalLeft = parent->isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y(); if (logicalLeft < childMiddle) return ancestor->createVisiblePosition(childNode->nodeIndex(), DOWNSTREAM); return ancestor->createVisiblePosition(childNode->nodeIndex() + 1, UPSTREAM); @@ -4090,7 +4161,7 @@ VisiblePosition RenderBlock::positionForPointWithInlineChildren(const IntPoint& // pass the box a top position that is inside it IntPoint point(pointInLogicalContents.x(), closestBox->logicalTop()); - if (!style()->isHorizontalWritingMode()) + if (!isHorizontalWritingMode()) point = point.transposedPoint(); if (closestBox->renderer()->isReplaced()) return positionForPointRespectingEditingBoundaries(this, toRenderBox(closestBox->renderer()), point); @@ -4100,7 +4171,9 @@ VisiblePosition RenderBlock::positionForPointWithInlineChildren(const IntPoint& if (lastRootBoxWithChildren) { // We hit this case for Mac behavior when the Y coordinate is below the last box. ASSERT(moveCaretToBoundary); - return VisiblePosition(positionForBox(lastRootBoxWithChildren->lastLeafChild(), false), DOWNSTREAM); + InlineBox* logicallyLastBox; + if (lastRootBoxWithChildren->getLogicalEndBoxWithNode(logicallyLastBox)) + return VisiblePosition(positionForBox(logicallyLastBox, false), DOWNSTREAM); } // Can't reach this. We have a root line box, but it has no kids. @@ -4121,8 +4194,8 @@ VisiblePosition RenderBlock::positionForPoint(const IntPoint& point) if (isReplaced()) { // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode. - int pointLogicalLeft = style()->isHorizontalWritingMode() ? point.x() : point.y(); - int pointLogicalTop = style()->isHorizontalWritingMode() ? point.y() : point.x(); + int pointLogicalLeft = isHorizontalWritingMode() ? point.x() : point.y(); + int pointLogicalTop = isHorizontalWritingMode() ? point.y() : point.x(); if (pointLogicalTop < 0 || (pointLogicalTop < logicalHeight() && pointLogicalLeft < 0)) return createVisiblePosition(caretMinOffset(), DOWNSTREAM); @@ -4135,7 +4208,7 @@ VisiblePosition RenderBlock::positionForPoint(const IntPoint& point) offsetForContents(contentsX, contentsY); IntPoint pointInContents(contentsX, contentsY); IntPoint pointInLogicalContents(pointInContents); - if (!style()->isHorizontalWritingMode()) + if (!isHorizontalWritingMode()) pointInLogicalContents = pointInLogicalContents.transposedPoint(); if (childrenInline()) @@ -4303,7 +4376,7 @@ IntRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const logicalLeftOffsetForContent() + (index * (colLogicalWidth + colGap)) : logicalLeftOffsetForContent() + contentLogicalWidth() - colLogicalWidth - (index * (colLogicalWidth + colGap)); IntRect rect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight); - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return IntRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight); return IntRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); } @@ -4370,7 +4443,7 @@ void RenderBlock::adjustPointToColumnContents(IntPoint& point) const for (unsigned i = 0; i < colInfo->columnCount(); i++) { // Add in half the column gap to the left and right of the rect. IntRect colRect = columnRectAt(colInfo, i); - if (style()->isHorizontalWritingMode()) { + if (isHorizontalWritingMode()) { IntRect gapAndColumnRect(colRect.x() - halfColGap, colRect.y(), colRect.width() + colGap, colRect.height()); if (point.x() >= gapAndColumnRect.x() && point.x() < gapAndColumnRect.maxX()) { // FIXME: The clamping that follows is not completely right for right-to-left @@ -4440,7 +4513,7 @@ void RenderBlock::adjustRectForColumns(IntRect& r) const for (unsigned i = 0; i < colCount; i++) { IntRect colRect = columnRectAt(colInfo, i); IntRect repaintRect = r; - if (style()->isHorizontalWritingMode()) { + if (isHorizontalWritingMode()) { int currXOffset = colRect.x() - logicalLeft; repaintRect.move(currXOffset, currLogicalOffset); currLogicalOffset -= colRect.height(); @@ -4464,7 +4537,7 @@ IntPoint RenderBlock::flipForWritingModeIncludingColumns(const IntPoint& point) ColumnInfo* colInfo = columnInfo(); int columnLogicalHeight = colInfo->columnHeight(); int expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight(); - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return IntPoint(point.x(), expandedLogicalHeight - point.y()); return IntPoint(expandedLogicalHeight - point.x(), point.y()); } @@ -4478,7 +4551,7 @@ void RenderBlock::flipForWritingModeIncludingColumns(IntRect& rect) const ColumnInfo* colInfo = columnInfo(); int columnLogicalHeight = colInfo->columnHeight(); int expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight(); - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) rect.setY(expandedLogicalHeight - rect.maxY()); else rect.setX(expandedLogicalHeight - rect.maxX()); @@ -4499,7 +4572,7 @@ void RenderBlock::adjustForColumns(IntSize& offset, const IntPoint& point) const for (size_t i = 0; i < colCount; ++i) { // Compute the edges for a given column in the block progression direction. IntRect sliceRect = IntRect(logicalLeft, borderBefore() + paddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight); - if (!style()->isHorizontalWritingMode()) + if (!isHorizontalWritingMode()) sliceRect = sliceRect.transposedRect(); // If we have a flipped blocks writing mode, then convert the column so that it's coming from the after edge (either top or left edge). @@ -4508,7 +4581,7 @@ void RenderBlock::adjustForColumns(IntSize& offset, const IntPoint& point) const int logicalOffset = style()->isFlippedBlocksWritingMode() ? (colCount - 1 - i) * colLogicalHeight : i * colLogicalHeight; // Now we're in the same coordinate space as the point. See if it is inside the rectangle. - if (style()->isHorizontalWritingMode()) { + if (isHorizontalWritingMode()) { if (point.y() >= sliceRect.y() && point.y() < sliceRect.maxY()) { offset.expand(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset); return; @@ -4851,7 +4924,7 @@ void RenderBlock::computeInlinePreferredLogicalWidths() continue; } - if (t->style()->hasTextCombine()) + if (t->style()->hasTextCombine() && t->isCombineText()) toRenderCombineText(t)->combineText(); // Determine if we have a breakable character. Pass in @@ -5064,7 +5137,7 @@ bool RenderBlock::hasLineIfEmpty() const if (!node()) return false; - if (node()->isContentEditable() && node()->rootEditableElement() == node()) + if (node()->rendererIsEditable() && node()->rootEditableElement() == node()) return true; if (node()->isShadowRoot() && (node()->shadowHost()->hasTagName(inputTag))) @@ -5159,7 +5232,7 @@ int RenderBlock::lastLineBoxBaseline() const if (!isBlockFlow() || (isWritingModeRoot() && !isRubyRun())) return -1; - LineDirectionMode lineDirection = style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine; + LineDirectionMode lineDirection = isHorizontalWritingMode() ? HorizontalLine : VerticalLine; if (childrenInline()) { if (!firstLineBox() && hasLineIfEmpty()) { @@ -5766,6 +5839,14 @@ IntRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, int* case WEBKIT_RIGHT: alignment = alignRight; break; + case TASTART: + if (!currentStyle->isLeftToRightDirection()) + alignment = alignRight; + break; + case TAEND: + if (currentStyle->isLeftToRightDirection()) + alignment = alignRight; + break; } int x = borderLeft() + paddingLeft(); @@ -5857,8 +5938,7 @@ void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) RenderBlock* RenderBlock::createAnonymousBlock(bool isFlexibleBox) const { - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(style()); + RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style()); RenderBlock* newBox = 0; if (isFlexibleBox) { @@ -5884,8 +5964,7 @@ RenderBlock* RenderBlock::createAnonymousBlockWithSameTypeAs(RenderBlock* otherA RenderBlock* RenderBlock::createAnonymousColumnsBlock() const { - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(style()); + RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style()); newStyle->inheritColumnPropertiesFrom(style()); newStyle->setDisplay(BLOCK); @@ -5896,8 +5975,7 @@ RenderBlock* RenderBlock::createAnonymousColumnsBlock() const RenderBlock* RenderBlock::createAnonymousColumnSpanBlock() const { - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(style()); + RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style()); newStyle->setColumnSpan(true); newStyle->setDisplay(BLOCK); @@ -5915,7 +5993,7 @@ int RenderBlock::nextPageLogicalTop(int logicalOffset) const // The logicalOffset is in our coordinate space. We can add in our pushed offset. int pageLogicalHeight = layoutState->m_pageLogicalHeight; IntSize delta = layoutState->m_layoutOffset - layoutState->m_pageOffset; - int offset = style()->isHorizontalWritingMode() ? delta.height() : delta.width(); + int offset = isHorizontalWritingMode() ? delta.height() : delta.width(); int remainingLogicalHeight = (pageLogicalHeight - (offset + logicalOffset) % pageLogicalHeight) % pageLogicalHeight; return logicalOffset + remainingLogicalHeight; } @@ -5976,7 +6054,7 @@ int RenderBlock::adjustForUnsplittableChild(RenderBox* child, int logicalOffset, if (!pageLogicalHeight || childLogicalHeight > pageLogicalHeight) return logicalOffset; IntSize delta = layoutState->m_layoutOffset - layoutState->m_pageOffset; - int offset = style()->isHorizontalWritingMode() ? delta.height() : delta.width(); + int offset = isHorizontalWritingMode() ? delta.height() : delta.width(); int remainingLogicalHeight = (pageLogicalHeight - (offset + logicalOffset) % pageLogicalHeight) % pageLogicalHeight; if (remainingLogicalHeight < childLogicalHeight) return logicalOffset + remainingLogicalHeight; @@ -6012,7 +6090,7 @@ void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, int& d if (!pageLogicalHeight || lineHeight > pageLogicalHeight) return; IntSize offsetDelta = layoutState->m_layoutOffset - layoutState->m_pageOffset; - int offset = style()->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width(); + int offset = isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width(); int remainingLogicalHeight = pageLogicalHeight - (offset + logicalOffset) % pageLogicalHeight; if (remainingLogicalHeight < lineHeight) { int totalLogicalHeight = lineHeight + max(0, logicalOffset); @@ -6034,7 +6112,7 @@ int RenderBlock::collapsedMarginBeforeForChild(RenderBox* child) const // 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()) + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) return child->collapsedMarginAfter(); // The child is perpendicular to us, which means its margins don't collapse but are on the @@ -6051,7 +6129,7 @@ int RenderBlock::collapsedMarginAfterForChild(RenderBox* child) const // 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()) + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) return child->collapsedMarginBefore(); // The child is perpendicular to us, which means its margins don't collapse but are on the @@ -6093,21 +6171,21 @@ int RenderBlock::marginAfterForChild(RenderBoxModelObject* child) const int RenderBlock::marginStartForChild(RenderBoxModelObject* child) const { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return style()->isLeftToRightDirection() ? child->marginLeft() : child->marginRight(); return style()->isLeftToRightDirection() ? child->marginTop() : child->marginBottom(); } int RenderBlock::marginEndForChild(RenderBoxModelObject* child) const { - if (style()->isHorizontalWritingMode()) + if (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 (isHorizontalWritingMode()) { if (style()->isLeftToRightDirection()) child->setMarginLeft(margin); else @@ -6122,7 +6200,7 @@ void RenderBlock::setMarginStartForChild(RenderBox* child, int margin) void RenderBlock::setMarginEndForChild(RenderBox* child, int margin) { - if (style()->isHorizontalWritingMode()) { + if (isHorizontalWritingMode()) { if (style()->isLeftToRightDirection()) child->setMarginRight(margin); else @@ -6195,7 +6273,7 @@ RenderBlock::MarginValues RenderBlock::marginValuesForChild(RenderBox* child) beforeMargin = child->marginBefore(); afterMargin = child->marginAfter(); } - } else if (child->style()->isHorizontalWritingMode() == style()->isHorizontalWritingMode()) { + } else if (child->isHorizontalWritingMode() == 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) { diff --git a/Source/WebCore/rendering/RenderBlock.h b/Source/WebCore/rendering/RenderBlock.h index 8d054a6..74751e1 100644 --- a/Source/WebCore/rendering/RenderBlock.h +++ b/Source/WebCore/rendering/RenderBlock.h @@ -171,9 +171,9 @@ public: // 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(); } + int logicalWidthForChild(RenderBox* child) { return isHorizontalWritingMode() ? child->width() : child->height(); } + int logicalHeightForChild(RenderBox* child) { return isHorizontalWritingMode() ? child->height() : child->width(); } + int logicalTopForChild(RenderBox* child) { return 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; @@ -218,8 +218,8 @@ public: virtual void scrollbarsChanged(bool /*horizontalScrollbarChanged*/, bool /*verticalScrollbarChanged*/) { }; - int logicalRightOffsetForContent() const { return style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() + availableLogicalWidth() : borderTop() + paddingTop() + availableLogicalWidth(); } - int logicalLeftOffsetForContent() const { return style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); } + int logicalRightOffsetForContent() const { return isHorizontalWritingMode() ? borderLeft() + paddingLeft() + availableLogicalWidth() : borderTop() + paddingTop() + availableLogicalWidth(); } + int logicalLeftOffsetForContent() const { return isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); } protected: // These functions are only used internally to manipulate the render tree structure via remove/insert/appendChildNode. @@ -297,7 +297,9 @@ protected: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); virtual bool hasLineIfEmpty() const; - bool layoutOnlyPositionedObjects(); + + bool simplifiedLayout(); + void simplifiedNormalFlowLayout(); void computeOverflow(int oldClientAfterEdge, bool recomputeFloats = false); virtual void addOverflowFromChildren(); @@ -429,35 +431,35 @@ private: IntPoint flipFloatForWritingMode(const FloatingObject*, const IntPoint&) const; - int logicalTopForFloat(const FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->y() : child->x(); } - int logicalBottomForFloat(const FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->maxY() : child->maxX(); } - int logicalLeftForFloat(const FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->x() : child->y(); } - int logicalRightForFloat(const FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->maxX() : child->maxY(); } - int logicalWidthForFloat(const FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->width() : child->height(); } + int logicalTopForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->y() : child->x(); } + int logicalBottomForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->maxY() : child->maxX(); } + int logicalLeftForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->x() : child->y(); } + int logicalRightForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->maxX() : child->maxY(); } + int logicalWidthForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->width() : child->height(); } void setLogicalTopForFloat(FloatingObject* child, int logicalTop) { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) child->setY(logicalTop); else child->setX(logicalTop); } void setLogicalLeftForFloat(FloatingObject* child, int logicalLeft) { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) child->setX(logicalLeft); else child->setY(logicalLeft); } void setLogicalHeightForFloat(FloatingObject* child, int logicalHeight) { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) child->setHeight(logicalHeight); else child->setWidth(logicalHeight); } void setLogicalWidthForFloat(FloatingObject* child, int logicalWidth) { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) child->setWidth(logicalWidth); else child->setHeight(logicalWidth); @@ -465,7 +467,7 @@ private: int xPositionForFloatIncludingMargin(const FloatingObject* child) const { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return child->x() + child->renderer()->marginLeft(); else return child->x() + marginBeforeForChild(child->renderer()); @@ -473,31 +475,32 @@ private: int yPositionForFloatIncludingMargin(const FloatingObject* child) const { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return child->y() + marginBeforeForChild(child->renderer()); else return child->y() + child->renderer()->marginTop(); } // The following functions' implementations are in RenderBlockLineLayout.cpp. + void checkFloatsInCleanLine(RootInlineBox*, Vector<FloatWithRect>&, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat); RootInlineBox* determineStartPosition(bool& firstLine, bool& fullLayout, bool& previousLineBrokeCleanly, InlineBidiResolver&, Vector<FloatWithRect>& floats, unsigned& numCleanFloats, bool& useRepaintBounds, int& repaintTop, int& repaintBottom); - RootInlineBox* determineEndPosition(RootInlineBox* startBox, InlineIterator& cleanLineStart, - BidiStatus& cleanLineBidiStatus, - int& yPos); + RootInlineBox* determineEndPosition(RootInlineBox* startBox, Vector<FloatWithRect>& floats, size_t floatIndex, InlineIterator& cleanLineStart, + BidiStatus& cleanLineBidiStatus, int& yPos); bool matchedEndLine(const InlineBidiResolver&, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, RootInlineBox*& endLine, int& endYPos, int& repaintBottom, int& repaintTop); void skipTrailingWhitespace(InlineIterator&, bool isLineEmpty, bool previousLineBrokeCleanly); - int skipLeadingWhitespace(InlineBidiResolver&, bool firstLine, bool isLineEmpty, bool previousLineBrokeCleanly, FloatingObject* lastFloatFromPreviousLine); + void skipLeadingWhitespace(InlineBidiResolver&, bool firstLine, bool isLineEmpty, bool previousLineBrokeCleanly, FloatingObject* lastFloatFromPreviousLine, int& lineLeftOffset, int& lineRightOffset); void fitBelowFloats(float widthToFit, bool firstLine, float& availableWidth); typedef std::pair<RenderText*, LazyLineBreakIterator> LineBreakIteratorInfo; - InlineIterator findNextLineBreak(InlineBidiResolver&, bool firstLine, bool& isLineEmpty, LineBreakIteratorInfo&, bool& previousLineBrokeCleanly, bool& hyphenated, EClear*, FloatingObject* lastFloatFromPreviousLine); - RootInlineBox* constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool firstLine, bool lastLine, RenderObject* endObject); - InlineFlowBox* createLineBoxes(RenderObject*, bool firstLine); + InlineIterator findNextLineBreak(InlineBidiResolver&, bool firstLine, bool& isLineEmpty, LineBreakIteratorInfo&, bool& previousLineBrokeCleanly, bool& hyphenated, + EClear*, FloatingObject* lastFloatFromPreviousLine, Vector<RenderBox*>& positionedObjects); + RootInlineBox* constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool firstLine, bool lastLine, RenderObject* endObject, RenderObject* logicallyLastRunRenderer); + InlineFlowBox* createLineBoxes(RenderObject*, bool firstLine, InlineBox* childBox); - void computeInlineDirectionPositionsForLine(RootInlineBox*, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap&); + void computeInlineDirectionPositionsForLine(RootInlineBox*, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&); void computeBlockDirectionPositionsForLine(RootInlineBox*, BidiRun*, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&); void deleteEllipsisLineBoxes(); void checkLinesForTextOverflow(); @@ -526,7 +529,7 @@ private: // Positions new floats and also adjust all floats encountered on the line if any of them // have to move to the next page/column. - bool positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine); + bool positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, bool firstLine, int& lineLeftOffset, int& lineRightOffset); void clearFloats(); int getClearDelta(RenderBox* child, int yPos); diff --git a/Source/WebCore/rendering/RenderBlockLineLayout.cpp b/Source/WebCore/rendering/RenderBlockLineLayout.cpp index d1fce88..3a0f016 100644 --- a/Source/WebCore/rendering/RenderBlockLineLayout.cpp +++ b/Source/WebCore/rendering/RenderBlockLineLayout.cpp @@ -95,7 +95,7 @@ static void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& // Check to see if our last midpoint is a start point beyond the line break. If so, // shave it off the list, and shave off a trailing space if the previous end point doesn't // preserve whitespace. - if (lBreak.obj && lineMidpointState.numMidpoints && !(lineMidpointState.numMidpoints % 2)) { + if (lBreak.m_obj && lineMidpointState.numMidpoints && !(lineMidpointState.numMidpoints % 2)) { InlineIterator* midpoints = lineMidpointState.midpoints.data(); InlineIterator& endpoint = midpoints[lineMidpointState.numMidpoints - 2]; const InlineIterator& startpoint = midpoints[lineMidpointState.numMidpoints - 1]; @@ -105,8 +105,8 @@ static void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& if (currpoint == lBreak) { // We hit the line break before the start point. Shave off the start point. lineMidpointState.numMidpoints--; - if (endpoint.obj->style()->collapseWhiteSpace()) - endpoint.pos--; + if (endpoint.m_obj->style()->collapseWhiteSpace()) + endpoint.m_pos--; } } } @@ -132,31 +132,31 @@ void RenderBlock::appendRunsForObject(int start, int end, RenderObject* obj, Inl if (haveNextMidpoint) nextMidpoint = lineMidpointState.midpoints[lineMidpointState.currentMidpoint]; if (lineMidpointState.betweenMidpoints) { - if (!(haveNextMidpoint && nextMidpoint.obj == obj)) + if (!(haveNextMidpoint && nextMidpoint.m_obj == obj)) return; // This is a new start point. Stop ignoring objects and // adjust our start. lineMidpointState.betweenMidpoints = false; - start = nextMidpoint.pos; + start = nextMidpoint.m_pos; lineMidpointState.currentMidpoint++; if (start < end) return appendRunsForObject(start, end, obj, resolver); } else { - if (!haveNextMidpoint || (obj != nextMidpoint.obj)) { + if (!haveNextMidpoint || (obj != nextMidpoint.m_obj)) { resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir())); return; } // An end midpoint has been encountered within our object. We // need to go ahead and append a run with our endpoint. - if (static_cast<int>(nextMidpoint.pos + 1) <= end) { + if (static_cast<int>(nextMidpoint.m_pos + 1) <= end) { lineMidpointState.betweenMidpoints = true; lineMidpointState.currentMidpoint++; - if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it. - if (static_cast<int>(nextMidpoint.pos + 1) > start) + if (nextMidpoint.m_pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it. + if (static_cast<int>(nextMidpoint.m_pos + 1) > start) resolver.addRun(new (obj->renderArena()) - BidiRun(start, nextMidpoint.pos + 1, obj, resolver.context(), resolver.dir())); - return appendRunsForObject(nextMidpoint.pos + 1, end, obj, resolver); + BidiRun(start, nextMidpoint.m_pos + 1, obj, resolver.context(), resolver.dir())); + return appendRunsForObject(nextMidpoint.m_pos + 1, end, obj, resolver); } } else resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir())); @@ -203,14 +203,14 @@ static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox) return false; } -InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, bool firstLine) +InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, bool firstLine, InlineBox* childBox) { // See if we have an unconstructed line box for this object that is also // the last item on the line. unsigned lineDepth = 1; - InlineFlowBox* childBox = 0; InlineFlowBox* parentBox = 0; InlineFlowBox* result = 0; + bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain(); do { ASSERT(obj->isRenderInline() || obj == this); @@ -230,7 +230,9 @@ InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, bool firstLine) ASSERT(newBox->isInlineFlowBox()); parentBox = static_cast<InlineFlowBox*>(newBox); parentBox->setFirstLineStyleBit(firstLine); - parentBox->setIsHorizontal(style()->isHorizontalWritingMode()); + parentBox->setIsHorizontal(isHorizontalWritingMode()); + if (!hasDefaultLineBoxContain) + parentBox->clearDescendantsHaveSameLineHeightAndBaseline(); constructedNewBox = true; } @@ -257,7 +259,7 @@ InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, bool firstLine) return result; } -RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool firstLine, bool lastLine, RenderObject* endObject) +RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool firstLine, bool lastLine, RenderObject* endObject, RenderObject* logicallyLastRunRenderer) { ASSERT(firstRun); @@ -284,10 +286,11 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, // run's inline box. if (!parentBox || parentBox->renderer() != r->m_object->parent()) // Create new inline boxes all the way back to the appropriate insertion point. - parentBox = createLineBoxes(r->m_object->parent(), firstLine); - - // Append the inline box to this line. - parentBox->addToLine(box); + parentBox = createLineBoxes(r->m_object->parent(), firstLine, box); + else { + // Append the inline box to this line. + parentBox->addToLine(box); + } bool visuallyOrdered = r->m_object->style()->visuallyOrdered(); box->setBidiLevel(r->level()); @@ -315,7 +318,7 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, // paint borders/margins/padding. This knowledge will ultimately be used when // we determine the horizontal positions and widths of all the inline boxes on // the line. - lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject); + lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject, logicallyLastRunRenderer); // Now mark the line boxes as being constructed. lastLineBox()->setConstructed(); @@ -333,7 +336,60 @@ ETextAlign RenderBlock::textAlignmentForLine(bool endsWithSoftBreak) const return alignment; } -void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) +static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) +{ + // 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 (isLeftToRightDirection) { + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) + trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); + return; + } + + if (trailingSpaceRun) + trailingSpaceRun->m_box->setLogicalWidth(0); + else if (totalLogicalWidth > availableLogicalWidth) + logicalLeft -= (totalLogicalWidth - availableLogicalWidth); +} + +static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) +{ + // 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 (isLeftToRightDirection) { + if (trailingSpaceRun) { + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + trailingSpaceRun->m_box->setLogicalWidth(0); + } + if (totalLogicalWidth < availableLogicalWidth) + logicalLeft += availableLogicalWidth - totalLogicalWidth; + return; + } + + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) { + trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + } else + logicalLeft += availableLogicalWidth - totalLogicalWidth; +} + +static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) +{ + float trailingSpaceWidth = 0; + if (trailingSpaceRun) { + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2); + trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceWidth)); + } + if (isLeftToRightDirection) + logicalLeft += max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0); + else + logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth; +} + +void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache) { ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak()); float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), firstLine); @@ -354,6 +410,8 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, RenderText* rt = toRenderText(r->m_object); if (textAlign == JUSTIFY && r != trailingSpaceRun) { + if (!isAfterExpansion) + static_cast<InlineTextBox*>(r->m_box)->setCanHaveLeadingExpansion(true); unsigned opportunitiesInRun = Font::expansionOpportunityCount(rt->characters() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion); expansionOpportunities.append(opportunitiesInRun); expansionOpportunityCount += opportunitiesInRun; @@ -366,6 +424,21 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, } HashSet<const SimpleFontData*> fallbackFonts; GlyphOverflow glyphOverflow; + + // Always compute glyph overflow if the block's line-box-contain value is "glyphs". + if (lineBox->fitsToGlyphs()) { + // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization + // will keep us from computing glyph bounds in nearly all cases. + bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading(); + int baselineShift = lineBox->verticalPositionForBox(r->m_box, verticalPositionCache); + int rootDescent = includeRootLine ? lineBox->renderer()->style(firstLine)->font().fontMetrics().descent() : 0; + int rootAscent = includeRootLine ? lineBox->renderer()->style(firstLine)->font().fontMetrics().ascent() : 0; + int boxAscent = rt->style(firstLine)->font().fontMetrics().ascent() - baselineShift; + int boxDescent = rt->style(firstLine)->font().fontMetrics().descent() + baselineShift; + if (boxAscent > rootDescent || boxDescent > rootAscent) + glyphOverflow.computeBounds = true; + } + int hyphenWidth = 0; if (static_cast<InlineTextBox*>(r->m_box)->hasHyphen()) { const AtomicString& hyphenString = rt->style()->hyphenString(); @@ -377,6 +450,7 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast<InlineTextBox*>(r->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).first; ASSERT(it->second.first.isEmpty()); copyToVector(fallbackFonts, it->second.first); + r->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline(); } if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) { ASSERT(r->m_box->isText()); @@ -408,17 +482,7 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, 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()->isLeftToRightDirection()) { - if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) - trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); - } else { - if (trailingSpaceRun) - trailingSpaceRun->m_box->setLogicalWidth(0); - else if (totalLogicalWidth > availableLogicalWidth) - logicalLeft -= (totalLogicalWidth - availableLogicalWidth); - } + updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); break; case JUSTIFY: adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth); @@ -439,40 +503,27 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, } case RIGHT: case WEBKIT_RIGHT: - // 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()->isLeftToRightDirection()) { - if (trailingSpaceRun) { - totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); - trailingSpaceRun->m_box->setLogicalWidth(0); - } - if (totalLogicalWidth < availableLogicalWidth) - logicalLeft += availableLogicalWidth - totalLogicalWidth; - } else { - if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) { - trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); - totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); - } else - logicalLeft += availableLogicalWidth - totalLogicalWidth; - } + updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); break; case CENTER: case WEBKIT_CENTER: - float trailingSpaceWidth = 0; - if (trailingSpaceRun) { - totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); - trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2); - trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceWidth)); - } + updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); + break; + case TASTART: if (style()->isLeftToRightDirection()) - logicalLeft += max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0); + updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); else - logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth; + updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); + break; + case TAEND: + if (style()->isLeftToRightDirection()) + updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); + else + updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); break; } - if (expansionOpportunityCount) { + if (expansionOpportunityCount && availableLogicalWidth > totalLogicalWidth) { size_t i = 0; for (BidiRun* r = firstRun; r; r = r->next()) { if (!r->m_box || r == trailingSpaceRun) @@ -543,6 +594,49 @@ static inline bool isCollapsibleSpace(UChar character, RenderText* renderer) return false; } + +static void setStaticPositions(RenderBlock* block, RenderBox* child) +{ + // FIXME: The math here is actually not really right. It's a best-guess approximation that + // will work for the common cases + RenderObject* containerBlock = child->container(); + int blockHeight = block->logicalHeight(); + if (containerBlock->isRenderInline()) { + // A relative positioned inline encloses us. In this case, we also have to determine our + // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned + // inline so that we can obtain the value later. + toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startOffsetForLine(blockHeight, false)); + toRenderInline(containerBlock)->layer()->setStaticBlockPosition(blockHeight); + } + + if (child->style()->isOriginalDisplayInlineType()) + child->layer()->setStaticInlinePosition(block->startOffsetForLine(blockHeight, false)); + else + child->layer()->setStaticInlinePosition(block->borderAndPaddingStart()); + child->layer()->setStaticBlockPosition(blockHeight); +} + +static bool reachedEndOfTextRenderer(InlineBidiResolver& resolver) +{ + BidiRun* run = resolver.logicallyLastRun(); + if (!run) + return true; + unsigned int pos = run->stop(); + RenderObject* r = run->m_object; + if (!r->isText() || r->isBR()) + return false; + RenderText* renderText = toRenderText(r); + if (pos >= renderText->textLength()) + return true; + + while (isASCIISpace(renderText->characters()[pos])) { + pos++; + if (pos >= renderText->textLength()) + return true; + } + return false; +} + void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogicalTop, int& repaintLogicalBottom) { bool useRepaintBounds = false; @@ -748,7 +842,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogica BidiStatus cleanLineBidiStatus; int endLineLogicalTop = 0; RootInlineBox* endLine = (fullLayout || !startLine) ? - 0 : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, endLineLogicalTop); + 0 : determineEndPosition(startLine, floats, floatIndex, cleanLineStart, cleanLineBidiStatus, endLineLogicalTop); if (startLine) { if (!useRepaintBounds) { @@ -806,10 +900,11 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogica EClear clear = CNONE; bool hyphenated; + Vector<RenderBox*> positionedObjects; InlineIterator oldEnd = end; FloatingObject* lastFloatFromPreviousLine = (m_floatingObjects && !m_floatingObjects->set().isEmpty()) ? m_floatingObjects->set().last() : 0; - end = findNextLineBreak(resolver, firstLine, isLineEmpty, lineBreakIteratorInfo, previousLineBrokeCleanly, hyphenated, &clear, lastFloatFromPreviousLine); + end = findNextLineBreak(resolver, firstLine, isLineEmpty, lineBreakIteratorInfo, previousLineBrokeCleanly, hyphenated, &clear, lastFloatFromPreviousLine, positionedObjects); if (resolver.position().atEnd()) { resolver.deleteRuns(); checkForFloatsFromLastLine = true; @@ -817,7 +912,10 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogica } ASSERT(end != resolver.position()); - if (!isLineEmpty) { + if (isLineEmpty) { + if (lastRootBox()) + lastRootBox()->setLineBreakInfo(end.m_obj, end.m_pos, resolver.status()); + } else { VisualDirectionOverride override = (style()->visuallyOrdered() ? (style()->direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride); resolver.createBidiRunsForLine(end, override, previousLineBrokeCleanly); ASSERT(resolver.position() == end); @@ -879,7 +977,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogica if (resolver.runCount()) { if (hyphenated) resolver.logicallyLastRun()->m_hasHyphen = true; - lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), firstLine, !end.obj, end.obj && !end.pos ? end.obj : 0); + bool lastLine = end.m_obj && end.m_obj->isText() ? reachedEndOfTextRenderer(resolver) : !end.m_obj; + lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), firstLine, lastLine, end.m_obj && !end.m_pos ? end.m_obj : 0, resolver.logicallyLastRun()->m_object); if (lineBox) { lineBox->setEndsWithBreak(previousLineBrokeCleanly); @@ -890,10 +989,10 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogica #endif GlyphOverflowAndFallbackFontsMap textBoxDataMap; - + // Now we position all of our text runs horizontally. if (!isSVGRootInlineBox) - computeInlineDirectionPositionsForLine(lineBox, firstLine, resolver.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap); + computeInlineDirectionPositionsForLine(lineBox, firstLine, resolver.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache); // Now position our text runs vertically. computeBlockDirectionPositionsForLine(lineBox, resolver.firstRun(), textBoxDataMap, verticalPositionCache); @@ -924,7 +1023,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogica resolver.deleteRuns(); if (lineBox) { - lineBox->setLineBreakInfo(end.obj, end.pos, resolver.status()); + lineBox->setLineBreakInfo(end.m_obj, end.m_pos, resolver.status()); if (useRepaintBounds) { repaintLogicalTop = min(repaintLogicalTop, beforeSideVisualOverflowForLine(lineBox)); repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisualOverflowForLine(lineBox)); @@ -954,6 +1053,9 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogica } } + for (size_t i = 0; i < positionedObjects.size(); ++i) + setStaticPositions(this, positionedObjects[i]); + firstLine = false; newLine(clear); } @@ -1081,7 +1183,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogica setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAfter() + paddingAfter() + scrollbarLogicalHeight()); if (!firstLineBox() && hasLineIfEmpty()) - setLogicalHeight(logicalHeight() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); + setLogicalHeight(logicalHeight() + lineHeight(true, 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. @@ -1089,6 +1191,35 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogica checkLinesForTextOverflow(); } +void RenderBlock::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat) +{ + Vector<RenderBox*>* cleanLineFloats = line->floatsPtr(); + if (!cleanLineFloats) + return; + + Vector<RenderBox*>::iterator end = cleanLineFloats->end(); + for (Vector<RenderBox*>::iterator it = cleanLineFloats->begin(); it != end; ++it) { + RenderBox* floatingBox = *it; + floatingBox->layoutIfNeeded(); + IntSize newSize(floatingBox->width() + floatingBox->marginLeft() + floatingBox->marginRight(), floatingBox->height() + floatingBox->marginTop() + floatingBox->marginBottom()); + ASSERT(floatIndex < floats.size()); + if (floats[floatIndex].object != floatingBox) { + encounteredNewFloat = true; + return; + } + if (floats[floatIndex].rect.size() != newSize) { + int floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x(); + int floatHeight = isHorizontalWritingMode() ? max(floats[floatIndex].rect.height(), newSize.height()) + : max(floats[floatIndex].rect.width(), newSize.width()); + line->markDirty(); + markLinesDirtyInBlockRange(line->blockLogicalHeight(), floatTop + floatHeight, line); + floats[floatIndex].rect.setSize(newSize); + dirtiedByFloat = true; + } + floatIndex++; + } +} + RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLayout, bool& previousLineBrokeCleanly, InlineBidiResolver& resolver, Vector<FloatWithRect>& floats, unsigned& numCleanFloats, bool& useRepaintBounds, int& repaintLogicalTop, int& repaintLogicalBottom) @@ -1121,32 +1252,9 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa curr->adjustBlockDirectionPosition(paginationDelta); } } - - if (Vector<RenderBox*>* cleanLineFloats = curr->floatsPtr()) { - Vector<RenderBox*>::iterator end = cleanLineFloats->end(); - for (Vector<RenderBox*>::iterator o = cleanLineFloats->begin(); o != end; ++o) { - RenderBox* f = *o; - f->layoutIfNeeded(); - IntSize newSize(f->width() + f->marginLeft() + f->marginRight(), f->height() + f->marginTop() + f->marginBottom()); - ASSERT(floatIndex < floats.size()); - if (floats[floatIndex].object != f) { - // A new float has been inserted before this line or before its last known float. - // Just do a full layout. - fullLayout = true; - break; - } - if (floats[floatIndex].rect.size() != newSize) { - 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(); - markLinesDirtyInBlockRange(curr->blockLogicalHeight(), floatTop + floatHeight, curr); - floats[floatIndex].rect.setSize(newSize); - dirtiedByFloat = true; - } - floatIndex++; - } - } + + // If a new float has been inserted before this line or before its last known float,just do a full layout. + checkFloatsInCleanLine(curr, floats, floatIndex, fullLayout, dirtiedByFloat); if (dirtiedByFloat || fullLayout) break; } @@ -1220,17 +1328,12 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa pos = last->lineBreakPos(); resolver.setStatus(last->lineBreakBidiStatus()); } else { - bool ltr = style()->isLeftToRightDirection() - #if ENABLE(SVG) - || (style()->unicodeBidi() == UBNormal && isSVGText()) - #endif - ; - + bool ltr = style()->isLeftToRightDirection(); Direction direction = ltr ? LeftToRight : RightToLeft; resolver.setLastStrongDir(direction); resolver.setLastDir(direction); resolver.setEorDir(direction); - resolver.setContext(BidiContext::create(ltr ? 0 : 1, direction, style()->unicodeBidi() == Override)); + resolver.setContext(BidiContext::create(ltr ? 0 : 1, direction, style()->unicodeBidi() == Override, FromStyleOrDOM)); startObj = bidiFirst(this, &resolver); } @@ -1240,23 +1343,29 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa return curr; } -RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& logicalTop) +RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, Vector<FloatWithRect>& floats, size_t floatIndex, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& logicalTop) { RootInlineBox* last = 0; - if (!startLine) - last = 0; - else { - for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) { - if (curr->isDirty()) - last = 0; - else if (!last) - last = curr; + for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) { + if (!curr->isDirty()) { + bool encounteredNewFloat = false; + bool dirtiedByFloat = false; + checkFloatsInCleanLine(curr, floats, floatIndex, encounteredNewFloat, dirtiedByFloat); + if (encounteredNewFloat) + return 0; } + if (curr->isDirty()) + last = 0; + else if (!last) + last = curr; } if (!last) return 0; + // At this point, |last| is the first line in a run of clean lines that ends with the last line + // in the block. + RootInlineBox* prev = last->prevRootBox(); cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos()); cleanLineBidiStatus = prev->lineBreakBidiStatus(); @@ -1305,7 +1414,7 @@ bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const Inlin static int numLines = 8; // The # of lines we're willing to match against. RootInlineBox* line = endLine; for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) { - if (line->lineBreakObj() == resolver.position().obj && line->lineBreakPos() == resolver.position().pos) { + if (line->lineBreakObj() == resolver.position().m_obj && line->lineBreakPos() == resolver.position().m_pos) { // We have a match. if (line->lineBreakBidiStatus() != resolver.status()) return false; // ...but the bidi state doesn't match. @@ -1356,7 +1465,7 @@ bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const Inlin static inline bool skipNonBreakingSpace(const InlineIterator& it, bool isLineEmpty, bool previousLineBrokeCleanly) { - if (it.obj->style()->nbspMode() != SPACE || it.current() != noBreakSpace) + if (it.m_obj->style()->nbspMode() != SPACE || it.current() != noBreakSpace) return false; // FIXME: This is bad. It makes nbsp inconsistent with space and won't work correctly @@ -1395,17 +1504,17 @@ static bool inlineFlowRequiresLineBox(RenderInline* flow) bool RenderBlock::requiresLineBox(const InlineIterator& it, bool isLineEmpty, bool previousLineBrokeCleanly) { - if (it.obj->isFloatingOrPositioned()) + if (it.m_obj->isFloatingOrPositioned()) return false; - if (it.obj->isRenderInline() && !inlineFlowRequiresLineBox(toRenderInline(it.obj))) + if (it.m_obj->isRenderInline() && !inlineFlowRequiresLineBox(toRenderInline(it.m_obj))) return false; - if (!shouldCollapseWhiteSpace(it.obj->style(), isLineEmpty, previousLineBrokeCleanly) || it.obj->isBR()) + if (!shouldCollapseWhiteSpace(it.m_obj->style(), isLineEmpty, previousLineBrokeCleanly) || it.m_obj->isBR()) return true; UChar current = it.current(); - return current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || shouldPreserveNewline(it.obj)) + return current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || shouldPreserveNewline(it.m_obj)) && !skipNonBreakingSpace(it, isLineEmpty, previousLineBrokeCleanly); } @@ -1420,34 +1529,6 @@ bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj, bool return !it.atEnd(); } -static void setStaticPositions(RenderBlock* block, RenderBox* child) -{ - // FIXME: The math here is actually not really right. It's a best-guess approximation that - // will work for the common cases - RenderObject* containerBlock = child->container(); - if (containerBlock->isRenderInline()) { - // A relative positioned inline encloses us. In this case, we also have to determine our - // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned - // inline so that we can obtain the value later. - toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startOffsetForLine(block->logicalHeight(), false)); - toRenderInline(containerBlock)->layer()->setStaticBlockPosition(block->logicalHeight()); - } - - bool isHorizontal = block->style()->isHorizontalWritingMode(); - bool hasStaticInlinePosition = child->style()->hasStaticInlinePosition(isHorizontal); - bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontal); - - if (hasStaticInlinePosition) { - if (child->style()->isOriginalDisplayInlineType()) - child->layer()->setStaticInlinePosition(block->startOffsetForLine(block->logicalHeight(), false)); - else - child->layer()->setStaticInlinePosition(block->borderAndPaddingStart()); - } - - if (hasStaticBlockPosition) - child->layer()->setStaticBlockPosition(block->logicalHeight()); -} - // FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building // line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned // elements quite right. In other words, we need to build this function's work into the normal line @@ -1457,7 +1538,7 @@ static void setStaticPositions(RenderBlock* block, RenderBox* child) void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator, bool isLineEmpty, bool previousLineBrokeCleanly) { while (!iterator.atEnd() && !requiresLineBox(iterator, isLineEmpty, previousLineBrokeCleanly)) { - RenderObject* object = iterator.obj; + RenderObject* object = iterator.m_obj; if (object->isFloating()) { insertFloatingObject(toRenderBox(object)); } else if (object->isPositioned()) @@ -1466,21 +1547,18 @@ void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator, bool isLineEm } } -int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver, bool firstLine, bool isLineEmpty, bool previousLineBrokeCleanly, - FloatingObject* lastFloatFromPreviousLine) +void RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver, bool firstLine, bool isLineEmpty, bool previousLineBrokeCleanly, + FloatingObject* lastFloatFromPreviousLine, int& lineLeftOffset, int& lineRightOffset) { - 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(logicalHeight(), firstLine); - } else if (object->isPositioned()) + RenderObject* object = resolver.position().m_obj; + if (object->isFloating()) + positionNewFloatOnLine(insertFloatingObject(toRenderBox(object)), lastFloatFromPreviousLine, firstLine, lineLeftOffset, lineRightOffset); + else if (object->isPositioned()) setStaticPositions(this, toRenderBox(object)); resolver.increment(); } resolver.commitExplicitEmbedding(); - return availableWidth; } // This is currently just used for list markers and inline flows that have line boxes. Neither should @@ -1560,7 +1638,7 @@ static void tryHyphenating(RenderText* text, const Font& font, const AtomicStrin // FIXME: The following assumes that the character at lastSpace is a space (and therefore should not factor // into hyphenate-limit-before) unless lastSpace is 0. This is wrong in the rare case of hyphenating // the first word in a text node which has leading whitespace. - if (prefixLength - (lastSpace ? 1 : 0) < static_cast<unsigned>(minimumPrefixLength)) + if (!prefixLength || prefixLength - (lastSpace ? 1 : 0) < static_cast<unsigned>(minimumPrefixLength)) return; ASSERT(pos - lastSpace - prefixLength >= static_cast<unsigned>(minimumSuffixLength)); @@ -1572,22 +1650,25 @@ static void tryHyphenating(RenderText* text, const Font& font, const AtomicStrin UNUSED_PARAM(isFixedPitch); #endif - lineBreak.obj = text; - lineBreak.pos = lastSpace + prefixLength; - lineBreak.nextBreakablePosition = nextBreakable; + lineBreak.moveTo(text, lastSpace + prefixLength, nextBreakable); hyphenated = true; } InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool firstLine, bool& isLineEmpty, LineBreakIteratorInfo& lineBreakIteratorInfo, bool& previousLineBrokeCleanly, - bool& hyphenated, EClear* clear, FloatingObject* lastFloatFromPreviousLine) + bool& hyphenated, EClear* clear, FloatingObject* lastFloatFromPreviousLine, Vector<RenderBox*>& positionedBoxes) { - ASSERT(resolver.position().block == this); + ASSERT(resolver.position().m_block == this); - bool appliedStartWidth = resolver.position().pos > 0; + bool appliedStartWidth = resolver.position().m_pos > 0; LineMidpointState& lineMidpointState = resolver.midpointState(); - float width = skipLeadingWhitespace(resolver, firstLine, isLineEmpty, previousLineBrokeCleanly, lastFloatFromPreviousLine); + int blockOffset = logicalHeight(); + int lineLeftOffset = logicalLeftOffsetForLine(blockOffset, firstLine); + int lineRightOffset = logicalRightOffsetForLine(blockOffset, firstLine); + + skipLeadingWhitespace(resolver, firstLine, isLineEmpty, previousLineBrokeCleanly, lastFloatFromPreviousLine, lineLeftOffset, lineRightOffset); + float width = max(0, lineRightOffset - lineLeftOffset); float w = 0; float tmpW = 0; @@ -1605,13 +1686,16 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool bool currentCharacterIsSpace = false; bool currentCharacterIsWS = false; RenderObject* trailingSpaceObject = 0; + Vector<RenderBox*, 4> trailingPositionedBoxes; InlineIterator lBreak = resolver.position(); - RenderObject* o = resolver.position().obj; + // FIXME: It is error-prone to split the position object out like this. + // Teach this code to work with objects instead of this split tuple. + RenderObject* o = resolver.position().m_obj; RenderObject* last = o; - unsigned pos = resolver.position().pos; - int nextBreakable = resolver.position().nextBreakablePosition; + unsigned pos = resolver.position().m_pos; + int nextBreakable = resolver.position().m_nextBreakablePosition; bool atStart = true; bool prevLineBrokeCleanly = previousLineBrokeCleanly; @@ -1646,9 +1730,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool if (o->isBR()) { if (w + tmpW <= width) { - lBreak.obj = o; - lBreak.pos = 0; - lBreak.nextBreakablePosition = -1; + lBreak.moveToStartOf(o); lBreak.increment(); // A <br> always breaks a line, so don't let the line be collapsed @@ -1676,8 +1758,12 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // If it does, position it now, otherwise, position // it after moving to next line (in newLine() func) if (floatsFitOnLine && logicalWidthForFloat(f) + w + tmpW <= width) { - positionNewFloatOnLine(f, lastFloatFromPreviousLine); - width = availableLogicalWidthForLine(logicalHeight(), firstLine); + positionNewFloatOnLine(f, lastFloatFromPreviousLine, firstLine, lineLeftOffset, lineRightOffset); + width = max(0, lineRightOffset - lineLeftOffset); + if (lBreak.m_obj == o) { + ASSERT(!lBreak.m_pos); + lBreak.increment(); + } } else floatsFitOnLine = false; } else if (o->isPositioned()) { @@ -1685,37 +1771,27 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // go ahead and determine our static inline position now. RenderBox* box = toRenderBox(o); bool isInlineType = box->style()->isOriginalDisplayInlineType(); - bool needToSetStaticInlinePosition = box->style()->hasStaticInlinePosition(style()->isHorizontalWritingMode()); - if (needToSetStaticInlinePosition && !isInlineType) { + if (!isInlineType) box->layer()->setStaticInlinePosition(borderAndPaddingStart()); - needToSetStaticInlinePosition = false; - } - - // If our original display was an INLINE type, then we can go ahead - // and determine our static y position now. - bool needToSetStaticBlockPosition = box->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()); - if (needToSetStaticBlockPosition && isInlineType) { + else { + // If our original display was an INLINE type, then we can go ahead + // and determine our static y position now. box->layer()->setStaticBlockPosition(logicalHeight()); - needToSetStaticBlockPosition = false; } - bool needToCreateLineBox = needToSetStaticInlinePosition || needToSetStaticBlockPosition; - RenderObject* c = o->container(); - if (c->isRenderInline() && (!needToSetStaticInlinePosition || !needToSetStaticBlockPosition)) - needToCreateLineBox = true; - // If we're ignoring spaces, we have to stop and include this object and // then start ignoring spaces again. - if (needToCreateLineBox) { - trailingSpaceObject = 0; - ignoreStart.obj = o; - ignoreStart.pos = 0; + if (isInlineType || o->container()->isRenderInline()) { if (ignoringSpaces) { + ignoreStart.m_obj = o; + ignoreStart.m_pos = 0; addMidpoint(lineMidpointState, ignoreStart); // Stop ignoring spaces. addMidpoint(lineMidpointState, ignoreStart); // Start ignoring again. } - - } + if (trailingSpaceObject) + trailingPositionedBoxes.append(box); + } else + positionedBoxes.append(box); } } else if (o->isRenderInline()) { // Right now, we should only encounter empty inlines here. @@ -1731,9 +1807,10 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool isLineEmpty = false; if (ignoringSpaces) { trailingSpaceObject = 0; + trailingPositionedBoxes.clear(); addMidpoint(lineMidpointState, InlineIterator(0, o, 0)); // Stop ignoring spaces. addMidpoint(lineMidpointState, InlineIterator(0, o, 0)); // Start ignoring again. - } else if (style()->collapseWhiteSpace() && resolver.position().obj == o + } else if (style()->collapseWhiteSpace() && resolver.position().m_obj == o && shouldSkipWhitespaceAfterStartObject(this, o, lineMidpointState)) { // Like with list markers, we start ignoring spaces to make sure that any // additional spaces we see will be discarded. @@ -1752,9 +1829,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!o->isImage() || allowImagesToBreak)) { w += tmpW; tmpW = 0; - lBreak.obj = o; - lBreak.pos = 0; - lBreak.nextBreakablePosition = -1; + lBreak.moveToStartOf(o); } if (ignoringSpaces) @@ -1765,6 +1840,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool currentCharacterIsSpace = false; currentCharacterIsWS = false; trailingSpaceObject = 0; + trailingPositionedBoxes.clear(); // Optimize for a common case. If we can't find whitespace after the list // item, then this is all moot. @@ -1792,7 +1868,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool #endif RenderStyle* style = t->style(firstLine); - if (style->hasTextCombine()) + if (style->hasTextCombine() && o->isCombineText()) toRenderCombineText(o)->combineText(); int strlen = t->textLength(); @@ -1824,9 +1900,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool if (t->isWordBreak()) { w += tmpW; tmpW = 0; - lBreak.obj = o; - lBreak.pos = 0; - lBreak.nextBreakablePosition = -1; + lBreak.moveToStartOf(o); ASSERT(!len); } @@ -1922,9 +1996,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // additional whitespace. if (w + tmpW + charWidth > width) { lineWasTooWide = true; - lBreak.obj = o; - lBreak.pos = pos; - lBreak.nextBreakablePosition = nextBreakable; + lBreak.moveTo(o, pos, nextBreakable); skipTrailingWhitespace(lBreak, isLineEmpty, previousLineBrokeCleanly); } } @@ -1934,7 +2006,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool if (hyphenated) goto end; } - if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && toRenderText(lBreak.obj)->textLength() && !toRenderText(lBreak.obj)->isWordBreak() && toRenderText(lBreak.obj)->characters()[lBreak.pos] == '\n') { + if (lBreak.m_obj && shouldPreserveNewline(lBreak.m_obj) && lBreak.m_obj->isText() && toRenderText(lBreak.m_obj)->textLength() && !toRenderText(lBreak.m_obj)->isWordBreak() && toRenderText(lBreak.m_obj)->characters()[lBreak.m_pos] == '\n') { if (!stoppedIgnoringSpaces && pos > 0) { // We need to stop right before the newline and then start up again. addMidpoint(lineMidpointState, InlineIterator(0, o, pos - 1)); // Stop @@ -1943,7 +2015,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool lBreak.increment(); previousLineBrokeCleanly = true; } - if (lBreak.obj && lBreak.pos && lBreak.obj->isText() && toRenderText(lBreak.obj)->textLength() && toRenderText(lBreak.obj)->characters()[lBreak.pos - 1] == softHyphen && style->hyphens() != HyphensNone) + if (lBreak.m_obj && lBreak.m_pos && lBreak.m_obj->isText() && toRenderText(lBreak.m_obj)->textLength() && toRenderText(lBreak.m_obj)->characters()[lBreak.m_pos - 1] == softHyphen && style->hyphens() != HyphensNone) hyphenated = true; goto end; // Didn't fit. Jump to the end. } else { @@ -1963,9 +2035,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool addMidpoint(lineMidpointState, InlineIterator(0, o, pos - 1)); // Stop addMidpoint(lineMidpointState, InlineIterator(0, o, pos)); // Start } - lBreak.obj = o; - lBreak.pos = pos; - lBreak.nextBreakablePosition = nextBreakable; + lBreak.moveTo(o, pos, nextBreakable); lBreak.increment(); previousLineBrokeCleanly = true; return lBreak; @@ -1975,9 +2045,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool w += tmpW; wrapW = 0; tmpW = 0; - lBreak.obj = o; - lBreak.pos = pos; - lBreak.nextBreakablePosition = nextBreakable; + lBreak.moveTo(o, pos, nextBreakable); // Auto-wrapping text should not wrap in the middle of a word once it has had an // opportunity to break after a word. breakWords = false; @@ -1986,9 +2054,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool if (midWordBreak) { // Remember this as a breakable position in case // adding the end width forces a break. - lBreak.obj = o; - lBreak.pos = pos; - lBreak.nextBreakablePosition = nextBreakable; + lBreak.moveTo(o, pos, nextBreakable); midWordBreak &= (breakWords || breakAll); } @@ -2020,22 +2086,21 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } if (currentCharacterIsSpace && !previousCharacterIsSpace) { - ignoreStart.obj = o; - ignoreStart.pos = pos; + ignoreStart.m_obj = o; + ignoreStart.m_pos = pos; } if (!currentCharacterIsWS && previousCharacterIsWS) { - if (autoWrap && o->style()->breakOnlyAfterWhiteSpace()) { - lBreak.obj = o; - lBreak.pos = pos; - lBreak.nextBreakablePosition = nextBreakable; - } + if (autoWrap && o->style()->breakOnlyAfterWhiteSpace()) + lBreak.moveTo(o, pos, nextBreakable); } if (collapseWhiteSpace && currentCharacterIsSpace && !ignoringSpaces) trailingSpaceObject = o; - else if (!o->style()->collapseWhiteSpace() || !currentCharacterIsSpace) + else if (!o->style()->collapseWhiteSpace() || !currentCharacterIsSpace) { trailingSpaceObject = 0; + trailingPositionedBoxes.clear(); + } pos++; len--; @@ -2047,8 +2112,13 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool tmpW += additionalTmpW; tmpW += inlineLogicalWidth(o, !appliedStartWidth, true); - if (canHyphenate && w + tmpW > width) { - tryHyphenating(t, f, style->locale(), style->hyphenationLimitBefore(), style->hyphenationLimitAfter(), lastSpace, pos, w + tmpW - additionalTmpW, width, isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, nextBreakable, hyphenated); + if (w + tmpW > width) { + if (canHyphenate) + tryHyphenating(t, f, style->locale(), style->hyphenationLimitBefore(), style->hyphenationLimitAfter(), lastSpace, pos, w + tmpW - additionalTmpW, width, isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, nextBreakable, hyphenated); + + if (!hyphenated && lBreak.m_obj && lBreak.m_pos && lBreak.m_obj->isText() && toRenderText(lBreak.m_obj)->textLength() && toRenderText(lBreak.m_obj)->characters()[lBreak.m_pos - 1] == softHyphen && style->hyphens() != HyphensNone) + hyphenated = true; + if (hyphenated) goto end; } @@ -2057,7 +2127,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool RenderObject* next = bidiNext(this, o); bool checkForBreak = autoWrap; - if (w && w + tmpW > width && lBreak.obj && currWS == NOWRAP) + if (w && w + tmpW > width && lBreak.m_obj && currWS == NOWRAP) checkForBreak = true; else if (next && o->isText() && next->isText() && !next->isBR()) { if (autoWrap || (next->style()->autoWrap())) { @@ -2084,9 +2154,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool if (canPlaceOnLine && checkForBreak) { w += tmpW; tmpW = 0; - lBreak.obj = next; - lBreak.pos = 0; - lBreak.nextBreakablePosition = -1; + lBreak.moveToStartOf(next); } } } @@ -2094,8 +2162,10 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool if (checkForBreak && (w + tmpW > width)) { // if we have floats, try to get below them. - if (currentCharacterIsSpace && !ignoringSpaces && o->style()->collapseWhiteSpace()) + if (currentCharacterIsSpace && !ignoringSpaces && o->style()->collapseWhiteSpace()) { trailingSpaceObject = 0; + trailingPositionedBoxes.clear(); + } if (w) goto end; @@ -2114,9 +2184,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || toRenderListMarker(last)->isInside())) { w += tmpW; tmpW = 0; - lBreak.obj = next; - lBreak.pos = 0; - lBreak.nextBreakablePosition = -1; + lBreak.moveToStartOf(next); } } @@ -2133,32 +2201,26 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } - if (w + tmpW <= width || lastWS == NOWRAP) { - lBreak.obj = 0; - lBreak.pos = 0; - lBreak.nextBreakablePosition = -1; - } + if (w + tmpW <= width || lastWS == NOWRAP) + lBreak.clear(); end: - if (lBreak == resolver.position() && (!lBreak.obj || !lBreak.obj->isBR())) { + if (lBreak == resolver.position() && (!lBreak.m_obj || !lBreak.m_obj->isBR())) { // we just add as much as possible if (style()->whiteSpace() == PRE) { // FIXME: Don't really understand this case. if (pos != 0) { - lBreak.obj = o; - lBreak.pos = pos - 1; - } else { - lBreak.obj = last; - lBreak.pos = last->isText() ? last->length() : 0; - lBreak.nextBreakablePosition = -1; - } - } else if (lBreak.obj) { + // FIXME: This should call moveTo which would clear m_nextBreakablePosition + // this code as-is is likely wrong. + lBreak.m_obj = o; + lBreak.m_pos = pos - 1; + } else + lBreak.moveTo(last, last->isText() ? last->length() : 0); + } else if (lBreak.m_obj) { // Don't ever break in the middle of a word if we can help it. // There's no room at all. We just have to be on this line, // even though we'll spill out. - lBreak.obj = o; - lBreak.pos = pos; - lBreak.nextBreakablePosition = -1; + lBreak.moveTo(o, pos); } } @@ -2174,18 +2236,40 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // to be the actual endpoint. In both cases we just decrease our pos by 1 level to // exclude the space, allowing it to - in effect - collapse into the newline. if (lineMidpointState.numMidpoints % 2) { - InlineIterator* midpoints = lineMidpointState.midpoints.data(); - midpoints[lineMidpointState.numMidpoints - 1].pos--; - } - //else if (lBreak.pos > 0) - // lBreak.pos--; - else if (lBreak.obj == 0 && trailingSpaceObject->isText()) { + // Find the trailing space object's midpoint. + int trailingSpaceMidpoint = lineMidpointState.numMidpoints - 1; + for ( ; trailingSpaceMidpoint >= 0 && lineMidpointState.midpoints[trailingSpaceMidpoint].m_obj != trailingSpaceObject; --trailingSpaceMidpoint) { } + ASSERT(trailingSpaceMidpoint >= 0); + lineMidpointState.midpoints[trailingSpaceMidpoint].m_pos--; + + // Now make sure every single trailingPositionedBox following the trailingSpaceMidpoint properly stops and starts + // ignoring spaces. + size_t currentMidpoint = trailingSpaceMidpoint + 1; + for (size_t i = 0; i < trailingPositionedBoxes.size(); ++i) { + if (currentMidpoint >= lineMidpointState.numMidpoints) { + // We don't have a midpoint for this box yet. + InlineIterator ignoreStart(this, trailingPositionedBoxes[i], 0); + addMidpoint(lineMidpointState, ignoreStart); // Stop ignoring. + addMidpoint(lineMidpointState, ignoreStart); // Start ignoring again. + } else { + ASSERT(lineMidpointState.midpoints[currentMidpoint].m_obj == trailingPositionedBoxes[i]); + ASSERT(lineMidpointState.midpoints[currentMidpoint + 1].m_obj == trailingPositionedBoxes[i]); + } + currentMidpoint += 2; + } + } else if (!lBreak.m_obj && trailingSpaceObject->isText()) { // Add a new end midpoint that stops right at the very end. RenderText* text = toRenderText(trailingSpaceObject); unsigned length = text->textLength(); unsigned pos = length >= 2 ? length - 2 : UINT_MAX; InlineIterator endMid(0, trailingSpaceObject, pos); addMidpoint(lineMidpointState, endMid); + for (size_t i = 0; i < trailingPositionedBoxes.size(); ++i) { + ignoreStart.m_obj = trailingPositionedBoxes[i]; + ignoreStart.m_pos = 0; + addMidpoint(lineMidpointState, ignoreStart); // Stop ignoring spaces. + addMidpoint(lineMidpointState, ignoreStart); // Start ignoring again. + } } } @@ -2193,8 +2277,8 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool // of the object. Do this adjustment to make it point to the start // of the next object instead to avoid confusing the rest of the // code. - if (lBreak.pos > 0) { - lBreak.pos--; + if (lBreak.m_pos > 0) { + lBreak.m_pos--; lBreak.increment(); } @@ -2205,7 +2289,7 @@ void RenderBlock::addOverflowFromInlineChildren() { int endPadding = hasOverflowClip() ? paddingEnd() : 0; // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to. - if (hasOverflowClip() && !endPadding && node() && node()->isContentEditable() && node() == node()->rootEditableElement() && style()->isLeftToRightDirection()) + if (hasOverflowClip() && !endPadding && node() && node()->rendererIsEditable() && node() == node()->rootEditableElement() && style()->isLeftToRightDirection()) endPadding = 1; for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding)); @@ -2217,7 +2301,7 @@ void RenderBlock::addOverflowFromInlineChildren() int RenderBlock::beforeSideVisualOverflowForLine(RootInlineBox* line) const { // Overflow is in the block's coordinate space, which means it isn't purely physical. - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return line->minYVisualOverflow(); return line->minXVisualOverflow(); } @@ -2225,7 +2309,7 @@ int RenderBlock::beforeSideVisualOverflowForLine(RootInlineBox* line) const int RenderBlock::afterSideVisualOverflowForLine(RootInlineBox* line) const { // Overflow is in the block's coordinate space, which means it isn't purely physical. - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return line->maxYVisualOverflow(); return line->maxXVisualOverflow(); } @@ -2233,7 +2317,7 @@ int RenderBlock::afterSideVisualOverflowForLine(RootInlineBox* line) const int RenderBlock::beforeSideLayoutOverflowForLine(RootInlineBox* line) const { // Overflow is in the block's coordinate space, which means it isn't purely physical. - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return line->minYLayoutOverflow(); return line->minXLayoutOverflow(); } @@ -2241,7 +2325,7 @@ int RenderBlock::beforeSideLayoutOverflowForLine(RootInlineBox* line) const int RenderBlock::afterSideLayoutOverflowForLine(RootInlineBox* line) const { // Overflow is in the block's coordinate space, which means it isn't purely physical. - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return line->maxYLayoutOverflow(); return line->maxXLayoutOverflow(); } diff --git a/Source/WebCore/rendering/RenderBox.cpp b/Source/WebCore/rendering/RenderBox.cpp index 6e7c0f2..28334dd 100644 --- a/Source/WebCore/rendering/RenderBox.cpp +++ b/Source/WebCore/rendering/RenderBox.cpp @@ -130,21 +130,21 @@ int RenderBox::marginAfter() const int RenderBox::marginStart() const { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return style()->isLeftToRightDirection() ? m_marginLeft : m_marginRight; return style()->isLeftToRightDirection() ? m_marginTop : m_marginBottom; } int RenderBox::marginEnd() const { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return style()->isLeftToRightDirection() ? m_marginRight : m_marginLeft; return style()->isLeftToRightDirection() ? m_marginBottom : m_marginTop; } void RenderBox::setMarginStart(int margin) { - if (style()->isHorizontalWritingMode()) { + if (isHorizontalWritingMode()) { if (style()->isLeftToRightDirection()) m_marginLeft = margin; else @@ -159,7 +159,7 @@ void RenderBox::setMarginStart(int margin) void RenderBox::setMarginEnd(int margin) { - if (style()->isHorizontalWritingMode()) { + if (isHorizontalWritingMode()) { if (style()->isLeftToRightDirection()) m_marginRight = margin; else @@ -216,7 +216,7 @@ void RenderBox::destroy() if (hasOverrideSize()) gOverrideSizeMap->remove(this); - if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent())) + if (style() && (style()->logicalHeight().isPercent() || style()->logicalMinHeight().isPercent() || style()->logicalMaxHeight().isPercent())) RenderBlock::removePercentHeightDescendant(this); RenderBoxModelObject::destroy(); @@ -299,8 +299,17 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle { RenderBoxModelObject::styleDidChange(diff, oldStyle); - if (needsLayout() && oldStyle && (oldStyle->height().isPercent() || oldStyle->minHeight().isPercent() || oldStyle->maxHeight().isPercent())) - RenderBlock::removePercentHeightDescendant(this); + if (needsLayout() && oldStyle) { + if (oldStyle && (oldStyle->logicalHeight().isPercent() || oldStyle->logicalMinHeight().isPercent() || oldStyle->logicalMaxHeight().isPercent())) + RenderBlock::removePercentHeightDescendant(this); + + // Normally we can do optimized positioning layout for absolute/fixed positioned objects. There is one special case, however, which is + // when the positioned object's margin-before is changed. In this case the parent has to get a layout in order to run margin collapsing + // to determine the new static position. + if (isPositioned() && style()->hasStaticBlockPosition(isHorizontalWritingMode()) && oldStyle->marginBefore() != style()->marginBefore() + && parent() && !parent()->normalChildNeedsLayout()) + parent()->setChildNeedsLayout(true); + } // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the // new zoomed coordinate space. @@ -335,8 +344,11 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle if (viewStyle->writingMode() != style()->writingMode() && (isRootRenderer || !document()->writingModeSetOnDocumentElement())) { viewStyle->setWritingMode(style()->writingMode()); - if (isBodyRenderer) + viewRenderer->setHorizontalWritingMode(style()->isHorizontalWritingMode()); + if (isBodyRenderer) { document()->documentElement()->renderer()->style()->setWritingMode(style()->writingMode()); + document()->documentElement()->renderer()->setHorizontalWritingMode(style()->isHorizontalWritingMode()); + } setNeedsLayoutAndPrefWidthsRecalc(); } } @@ -621,9 +633,9 @@ bool RenderBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularit #if PLATFORM(MAC) // On Mac only we reset the inline direction position when doing a document scroll (e.g., hitting Home/End). if (granularity == ScrollByDocument) - scrolled = l->scroll(logicalToPhysical(ScrollInlineDirectionBackward, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), ScrollByDocument, multiplier); + scrolled = l->scroll(logicalToPhysical(ScrollInlineDirectionBackward, isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), ScrollByDocument, multiplier); #endif - if (l->scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier)) + if (l->scroll(logicalToPhysical(direction, isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier)) scrolled = true; if (scrolled) { @@ -649,7 +661,7 @@ bool RenderBox::canBeScrolledAndHasScrollableArea() const bool RenderBox::canBeProgramaticallyScrolled(bool) const { - return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->isContentEditable()))) || (node() && node()->isDocumentNode()); + return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->rendererIsEditable()))) || (node() && node()->isDocumentNode()); } void RenderBox::autoscroll() @@ -782,7 +794,7 @@ void RenderBox::paint(PaintInfo& paintInfo, int tx, int ty) child->paint(childInfo, tx, ty); } -void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty) +void RenderBox::paintRootBoxFillLayers(const PaintInfo& paintInfo) { const FillLayer* bgLayer = style()->backgroundLayers(); Color bgColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); @@ -803,21 +815,12 @@ void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty) // The background of the box generated by the root element covers the entire canvas, so just use // the RenderView's docTop/Left/Width/Height accessors. paintFillLayers(paintInfo, bgColor, bgLayer, view()->docLeft(), view()->docTop(), view()->docWidth(), view()->docHeight(), CompositeSourceOver, bodyObject); - - if (style()->hasBorder() && style()->display() != INLINE) - paintBorder(paintInfo.context, tx, ty, width(), height(), style()); } void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) { if (!paintInfo.shouldPaintWithinRoot(this)) return; - - if (isRoot()) { - paintRootBoxDecorations(paintInfo, tx, ty); - return; - } - return paintBoxDecorationsWithSize(paintInfo, tx, ty, width(), height()); } @@ -835,11 +838,13 @@ void RenderBox::paintBoxDecorationsWithSize(PaintInfo& paintInfo, int tx, int ty // The theme will tell us whether or not we should also paint the CSS background. bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, IntRect(tx, ty, width, height)); if (!themePainted) { - // The <body> only paints its background if the root element has defined a background - // independent of the body. Go through the DOM to get to the root element's render object, - // since the root could be inline and wrapped in an anonymous block. - if (!isBody() || document()->documentElement()->renderer()->hasBackground()) + if (isRoot()) + paintRootBoxFillLayers(paintInfo); + else if (!isBody() || document()->documentElement()->renderer()->hasBackground()) { + // The <body> only paints its background if the root element has defined a background + // independent of the body. paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), tx, ty, width, height); + } if (style()->hasAppearance()) theme()->paintDecorations(this, paintInfo, IntRect(tx, ty, width, height)); } @@ -1108,7 +1113,7 @@ void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, paintInfo.phase = originalPhase; } -IntRect RenderBox::overflowClipRect(int tx, int ty) +IntRect RenderBox::overflowClipRect(int tx, int ty, OverlayScrollbarSizeRelevancy relevancy) { // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property // here. @@ -1123,8 +1128,8 @@ IntRect RenderBox::overflowClipRect(int tx, int ty) // Subtract out scrollbars if we have them. if (layer()) { - clipWidth -= layer()->verticalScrollbarWidth(); - clipHeight -= layer()->horizontalScrollbarHeight(); + clipWidth -= layer()->verticalScrollbarWidth(relevancy); + clipHeight -= layer()->horizontalScrollbarHeight(relevancy); } return IntRect(clipX, clipY, clipWidth, clipHeight); @@ -1319,19 +1324,21 @@ void RenderBox::positionLineBox(InlineBox* box) if (isPositioned()) { // Cache the x position only if we were an INLINE type originally. bool wasInline = style()->isOriginalDisplayInlineType(); - if (wasInline && style()->hasStaticInlinePosition(box->isHorizontal())) { + if (wasInline) { // The value is cached in the xPos of the box. We only need this value if // our object was inline originally, since otherwise it would have ended up underneath // the inlines. layer()->setStaticInlinePosition(lroundf(box->logicalLeft())); - setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. - } else if (!wasInline && style()->hasStaticBlockPosition(box->isHorizontal())) { + if (style()->hasStaticInlinePosition(box->isHorizontal())) + setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. + } else { // Our object was a block originally, so we make our normal flow position be // just below the line box (as though all the inlines that came before us got // wrapped in an anonymous block, which is what would have happened had we been // in flow). This value was cached in the y() of the box. layer()->setStaticBlockPosition(box->logicalTop()); - setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. + if (style()->hasStaticBlockPosition(box->isHorizontal())) + setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. } // Nuke the box. @@ -1569,7 +1576,7 @@ void RenderBox::computeLogicalWidth() RenderBlock* cb = containingBlock(); int containerLogicalWidth = max(0, containingBlockLogicalWidthForContent()); - bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode(); + bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); int containerWidthInInlineDirection = containerLogicalWidth; if (hasPerpendicularContainingBlock) containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight(); @@ -1776,7 +1783,7 @@ void RenderBox::computeLogicalHeight() computePositionedLogicalHeight(); } else { RenderBlock* cb = containingBlock(); - bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode(); + bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); if (!hasPerpendicularContainingBlock) computeBlockDirectionMargins(cb); @@ -1863,7 +1870,7 @@ void RenderBox::computeLogicalHeight() if (document()->printing()) visHeight = static_cast<int>(view()->pageLogicalHeight()); else { - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) visHeight = view()->viewHeight(); else visHeight = view()->viewWidth(); @@ -1896,18 +1903,20 @@ int RenderBox::computeLogicalHeightUsing(const Length& h) int RenderBox::computePercentageLogicalHeight(const Length& height) { int result = -1; + + // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing + // block that may have a specified height and then use it. In strict mode, this violates the + // specification, which states that percentage heights just revert to auto if the containing + // block has an auto height. We still skip anonymous containing blocks in both modes, though, and look + // only at explicit containers. bool skippedAutoHeightContainingBlock = false; RenderBlock* cb = containingBlock(); - if (document()->inQuirksMode()) { - // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing - // block that may have a specified height and then use it. In strict mode, this violates the - // specification, which states that percentage heights just revert to auto if the containing - // block has an auto height. - while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->logicalHeight().isAuto()) { - skippedAutoHeightContainingBlock = true; - cb = cb->containingBlock(); - cb->addPercentHeightDescendant(this); - } + while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->logicalHeight().isAuto()) { + if (!document()->inQuirksMode() && !cb->isAnonymousBlock()) + break; + skippedAutoHeightContainingBlock = true; + cb = cb->containingBlock(); + cb->addPercentHeightDescendant(this); } // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height @@ -2071,7 +2080,7 @@ int RenderBox::availableLogicalHeightUsing(const Length& h) const return computeContentBoxLogicalHeight(h.value()); if (isRenderView()) - return style()->isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth(); + return 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 @@ -2117,6 +2126,7 @@ void RenderBox::computeBlockDirectionMargins(RenderBlock* containingBlock) int RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const { +<<<<<<< HEAD #if PLATFORM(ANDROID) // Fixed element's position should be decided by the visible screen size. // That is in the doc coordindate. @@ -2127,6 +2137,9 @@ int RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObje #endif if (checkForPerpendicularWritingMode && containingBlock->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode()) +======= + if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) +>>>>>>> webkit.org at r82507 return containingBlockLogicalHeightForPositioned(containingBlock, false); if (containingBlock->isBox()) @@ -2157,6 +2170,7 @@ int RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObje int RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const { +<<<<<<< HEAD #if PLATFORM(ANDROID) // Fixed element's position should be decided by the visible screen size. // That is in the doc coordindate. @@ -2167,6 +2181,9 @@ int RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObj #endif if (checkForPerpendicularWritingMode && containingBlock->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode()) +======= + if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) +>>>>>>> webkit.org at r82507 return containingBlockLogicalWidthForPositioned(containingBlock, false); if (containingBlock->isBox()) @@ -2184,7 +2201,7 @@ int RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObj int heightResult; IntRect boundingBox = flow->linesBoundingBox(); - if (containingBlock->style()->isHorizontalWritingMode()) + if (containingBlock->isHorizontalWritingMode()) heightResult = boundingBox.height(); else heightResult = boundingBox.width(); @@ -2201,7 +2218,7 @@ static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRigh // FIXME: The static distance computation has not been patched for mixed writing modes yet. if (containerDirection == LTR) { int staticPosition = child->layer()->staticInlinePosition() - containerBlock->borderLogicalLeft(); - for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->parent()) { + for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { if (curr->isBox()) staticPosition += toRenderBox(curr)->logicalLeft(); } @@ -2210,7 +2227,7 @@ static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRigh RenderBox* enclosingBox = child->parent()->enclosingBox(); int staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock->borderLogicalRight(); staticPosition -= enclosingBox->logicalWidth(); - for (RenderObject* curr = enclosingBox; curr && curr != containerBlock; curr = curr->parent()) { + for (RenderObject* curr = enclosingBox; curr && curr != containerBlock; curr = curr->container()) { if (curr->isBox()) staticPosition -= toRenderBox(curr)->logicalLeft(); } @@ -2260,7 +2277,7 @@ void RenderBox::computePositionedLogicalWidth() // instead of the the container block's. TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction(); - bool isHorizontal = style()->isHorizontalWritingMode(); + bool isHorizontal = isHorizontalWritingMode(); const int bordersPlusPadding = borderAndPaddingLogicalWidth(); const Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); const Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); @@ -2365,11 +2382,11 @@ static void computeLogicalLeftPositionedOffset(int& logicalLeftPos, const Render { // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped // along this axis, then we need to flip the coordinate. This can only happen if the containing block is both a flipped mode and perpendicular to us. - if (containerBlock->style()->isHorizontalWritingMode() != child->style()->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) { + if (containerBlock->isHorizontalWritingMode() != child->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) { logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos; - logicalLeftPos += (child->style()->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom()); + logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom()); } else - logicalLeftPos += (child->style()->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop()); + logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop()); } void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, @@ -2552,7 +2569,7 @@ static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom // FIXME: The static distance computation has not been patched for mixed writing modes. int staticLogicalTop = child->layer()->staticBlockPosition() - containerBlock->borderBefore(); - for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->parent()) { + for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { if (curr->isBox() && !curr->isTableRow()) staticLogicalTop += toRenderBox(curr)->logicalTop(); } @@ -2578,7 +2595,7 @@ void RenderBox::computePositionedLogicalHeight() const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); - bool isHorizontal = style()->isHorizontalWritingMode(); + bool isHorizontal = isHorizontalWritingMode(); bool isFlipped = style()->isFlippedBlocksWritingMode(); const int bordersPlusPadding = borderAndPaddingLogicalHeight(); const Length marginBefore = style()->marginBefore(); @@ -2668,18 +2685,18 @@ static void computeLogicalTopPositionedOffset(int& logicalTopPos, const RenderBo { // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped // along this axis, then we need to flip the coordinate. This can only happen if the containing block is both a flipped mode and perpendicular to us. - if ((child->style()->isFlippedBlocksWritingMode() && child->style()->isHorizontalWritingMode() != containerBlock->style()->isHorizontalWritingMode()) - || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->style()->isHorizontalWritingMode() == containerBlock->style()->isHorizontalWritingMode())) + if ((child->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode()) + || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode())) logicalTopPos = containerLogicalHeight - logicalHeightValue - logicalTopPos; // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt. - if (containerBlock->style()->isFlippedBlocksWritingMode() && child->style()->isHorizontalWritingMode() == containerBlock->style()->isHorizontalWritingMode()) { - if (child->style()->isHorizontalWritingMode()) + if (containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) { + if (child->isHorizontalWritingMode()) logicalTopPos += containerBlock->borderBottom(); else logicalTopPos += containerBlock->borderRight(); } else { - if (child->style()->isHorizontalWritingMode()) + if (child->isHorizontalWritingMode()) logicalTopPos += containerBlock->borderTop(); else logicalTopPos += containerBlock->borderLeft(); @@ -2825,7 +2842,7 @@ void RenderBox::computePositionedLogicalWidthReplaced() TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction(); // Variables to solve. - bool isHorizontal = style()->isHorizontalWritingMode(); + bool isHorizontal = isHorizontalWritingMode(); Length logicalLeft = style()->logicalLeft(); Length logicalRight = style()->logicalRight(); Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); @@ -2985,7 +3002,7 @@ void RenderBox::computePositionedLogicalHeightReplaced() const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); // Variables to solve. - bool isHorizontal = style()->isHorizontalWritingMode(); + bool isHorizontal = isHorizontalWritingMode(); bool isFlipped = style()->isFlippedBlocksWritingMode(); Length marginBefore = style()->marginBefore(); Length marginAfter = style()->marginAfter(); @@ -3146,7 +3163,7 @@ VisiblePosition RenderBox::positionForPoint(const IntPoint& point) { // no children...return this render object's element, if there is one, and offset 0 if (!firstChild()) - return createVisiblePosition(node() ? firstDeepEditingPositionForNode(node()) : Position(0, 0)); + return createVisiblePosition(node() ? firstPositionInOrBeforeNode(node()) : Position(0, 0)); int xPos = point.x(); int yPos = point.y(); @@ -3157,8 +3174,8 @@ VisiblePosition RenderBox::positionForPoint(const IntPoint& point) if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) { if (xPos <= right / 2) - return createVisiblePosition(firstDeepEditingPositionForNode(node())); - return createVisiblePosition(lastDeepEditingPositionForNode(node())); + return createVisiblePosition(firstPositionInOrBeforeNode(node())); + return createVisiblePosition(lastPositionInOrAfterNode(node())); } } @@ -3229,7 +3246,7 @@ VisiblePosition RenderBox::positionForPoint(const IntPoint& point) if (closestRenderer) return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y()); - return createVisiblePosition(firstDeepEditingPositionForNode(node())); + return createVisiblePosition(firstPositionInOrBeforeNode(node())); } bool RenderBox::shrinkToAvoidFloats() const @@ -3293,8 +3310,8 @@ void RenderBox::addLayoutOverflow(const IntRect& rect) // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same // and vertical-lr/rl as the same. - bool hasTopOverflow = !style()->isLeftToRightDirection() && !style()->isHorizontalWritingMode(); - bool hasLeftOverflow = !style()->isLeftToRightDirection() && style()->isHorizontalWritingMode(); + bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode(); + bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode(); if (!hasTopOverflow) overflowRect.shiftYEdgeTo(max(overflowRect.y(), clientBox.y())); @@ -3414,13 +3431,14 @@ IntRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle) co if (!hasOverflowClip()) rect.unite(layoutOverflowRect()); - if (isRelPositioned() || hasTransform()) { + bool hasTransform = hasLayer() && layer()->transform(); + if (isRelPositioned() || hasTransform) { // If we are relatively positioned or if we have a transform, then we have to convert // this rectangle into physical coordinates, apply relative positioning and transforms // to it, and then convert it back. flipForWritingMode(rect); - if (hasTransform()) + if (hasTransform) rect = layer()->currentTransform().mapRect(rect); if (isRelPositioned()) @@ -3452,7 +3470,7 @@ IntPoint RenderBox::flipForWritingMode(const RenderBox* child, const IntPoint& p // The child is going to add in its x() and y(), so we have to make sure it ends up in // the right place. - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) return IntPoint(point.x(), point.y() + height() - child->height() - child->y() - (adjustment == ParentToChildFlippingAdjustment ? child->y() : 0)); return IntPoint(point.x() + width() - child->width() - child->x() - (adjustment == ParentToChildFlippingAdjustment ? child->x() : 0), point.y()); } @@ -3462,7 +3480,7 @@ void RenderBox::flipForWritingMode(IntRect& rect) const if (!style()->isFlippedBlocksWritingMode()) return; - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) rect.setY(height() - rect.maxY()); else rect.setX(width() - rect.maxX()); @@ -3479,7 +3497,7 @@ IntPoint RenderBox::flipForWritingMode(const IntPoint& position) const { if (!style()->isFlippedBlocksWritingMode()) return position; - return style()->isHorizontalWritingMode() ? IntPoint(position.x(), height() - position.y()) : IntPoint(width() - position.x(), position.y()); + return isHorizontalWritingMode() ? IntPoint(position.x(), height() - position.y()) : IntPoint(width() - position.x(), position.y()); } IntPoint RenderBox::flipForWritingModeIncludingColumns(const IntPoint& point) const @@ -3493,14 +3511,14 @@ IntSize RenderBox::flipForWritingMode(const IntSize& offset) const { if (!style()->isFlippedBlocksWritingMode()) return offset; - return style()->isHorizontalWritingMode() ? IntSize(offset.width(), height() - offset.height()) : IntSize(width() - offset.width(), offset.height()); + return isHorizontalWritingMode() ? IntSize(offset.width(), height() - offset.height()) : IntSize(width() - offset.width(), offset.height()); } FloatPoint RenderBox::flipForWritingMode(const FloatPoint& position) const { if (!style()->isFlippedBlocksWritingMode()) return position; - return style()->isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y()); + return isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y()); } void RenderBox::flipForWritingMode(FloatRect& rect) const @@ -3508,7 +3526,7 @@ void RenderBox::flipForWritingMode(FloatRect& rect) const if (!style()->isFlippedBlocksWritingMode()) return; - if (style()->isHorizontalWritingMode()) + if (isHorizontalWritingMode()) rect.setY(height() - rect.maxY()); else rect.setX(width() - rect.maxX()); diff --git a/Source/WebCore/rendering/RenderBox.h b/Source/WebCore/rendering/RenderBox.h index f0bd30d..ccedc66 100644 --- a/Source/WebCore/rendering/RenderBox.h +++ b/Source/WebCore/rendering/RenderBox.h @@ -33,6 +33,8 @@ struct PaintInfo; enum LogicalWidthType { LogicalWidth, MinLogicalWidth, MaxLogicalWidth }; +enum OverlayScrollbarSizeRelevancy { IgnoreOverlayScrollbarSize, IncludeOverlayScrollbarSize }; + class RenderBox : public RenderBoxModelObject { public: RenderBox(Node*); @@ -334,7 +336,7 @@ public: virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); - virtual IntRect overflowClipRect(int tx, int ty); + virtual IntRect overflowClipRect(int tx, int ty, OverlayScrollbarSizeRelevancy relevancy = IgnoreOverlayScrollbarSize); IntRect clipRect(int tx, int ty); virtual bool hasControlClip() const { return false; } virtual IntRect controlClipRect(int /*tx*/, int /*ty*/) const { return IntRect(); } @@ -422,11 +424,12 @@ protected: virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState&) const; virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const; + void paintRootBoxFillLayers(const PaintInfo&); + private: bool includeVerticalScrollbarSize() const; bool includeHorizontalScrollbarSize() const; - void paintRootBoxDecorations(PaintInfo&, int tx, int ty); // Returns true if we did a full repaint bool repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground); diff --git a/Source/WebCore/rendering/RenderBoxModelObject.cpp b/Source/WebCore/rendering/RenderBoxModelObject.cpp index eec048e..3e2974d 100644 --- a/Source/WebCore/rendering/RenderBoxModelObject.cpp +++ b/Source/WebCore/rendering/RenderBoxModelObject.cpp @@ -303,7 +303,7 @@ void RenderBoxModelObject::styleWillChange(StyleDifference diff, const RenderSty repaint(); } - if (diff == StyleDifferenceLayout) { + if (diff == StyleDifferenceLayout || diff == StyleDifferenceSimplifiedLayout) { // When a layout hint happens, we go ahead and do a repaint of the layer, since the layer could // end up being destroyed. if (hasLayer()) { @@ -346,8 +346,10 @@ void RenderBoxModelObject::styleDidChange(StyleDifference diff, const RenderStyl m_layer = new (renderArena()) RenderLayer(this); setHasLayer(true); m_layer->insertOnlyThisLayer(); - if (parent() && !needsLayout() && containingBlock()) + if (parent() && !needsLayout() && containingBlock()) { + m_layer->setNeedsFullRepaint(); m_layer->updateLayerPositions(); + } } } else if (layer() && layer()->parent()) { setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit. @@ -371,6 +373,7 @@ void RenderBoxModelObject::updateBoxModelInfoFromStyle() setHasBoxDecorations(hasBackground() || style()->hasBorder() || style()->hasAppearance() || style()->boxShadow()); setInline(style()->isDisplayInlineType()); setRelPositioned(style()->position() == RelativePosition); + setHorizontalWritingMode(style()->isHorizontalWritingMode()); } int RenderBoxModelObject::relativePositionOffsetX() const diff --git a/Source/WebCore/rendering/RenderCombineText.cpp b/Source/WebCore/rendering/RenderCombineText.cpp index 250ec9b..37ca1ed 100644 --- a/Source/WebCore/rendering/RenderCombineText.cpp +++ b/Source/WebCore/rendering/RenderCombineText.cpp @@ -37,6 +37,7 @@ RenderCombineText::RenderCombineText(Node* node, PassRefPtr<StringImpl> string) void RenderCombineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { + setStyleInternal(RenderStyle::clone(style())); RenderText::styleDidChange(diff, oldStyle); if (m_isCombined) @@ -93,12 +94,12 @@ void RenderCombineText::combineText() return; TextRun run = TextRun(String(text())); - FontDescription description = style()->font().fontDescription(); + FontDescription description = originalFont().fontDescription(); float emWidth = description.computedSize() * textCombineMargin; bool shouldUpdateFont = false; description.setOrientation(Horizontal); // We are going to draw combined text horizontally. - m_combinedTextWidth = style()->font().width(run); + m_combinedTextWidth = originalFont().width(run); m_isCombined = m_combinedTextWidth <= emWidth; if (m_isCombined) @@ -122,12 +123,14 @@ void RenderCombineText::combineText() } } + if (!m_isCombined) + shouldUpdateFont = style()->setFontDescription(originalFont().fontDescription()); + if (shouldUpdateFont) style()->font().update(style()->font().fontSelector()); if (m_isCombined) { - static const UChar newCharacter = objectReplacementCharacter; - DEFINE_STATIC_LOCAL(String, objectReplacementCharacterString, (&newCharacter, 1)); + DEFINE_STATIC_LOCAL(String, objectReplacementCharacterString, (&objectReplacementCharacter, 1)); RenderText::setTextInternal(objectReplacementCharacterString.impl()); } } diff --git a/Source/WebCore/rendering/RenderCombineText.h b/Source/WebCore/rendering/RenderCombineText.h index 3484ab7..406c3f6 100644 --- a/Source/WebCore/rendering/RenderCombineText.h +++ b/Source/WebCore/rendering/RenderCombineText.h @@ -34,8 +34,10 @@ public: void charactersToRender(int start, const UChar*& characters, int& length) const; bool isCombined() const { return m_isCombined; } float combinedTextWidth(const Font& font) const { return font.size(); } + const Font& originalFont() const { return parent()->style()->font(); } private: + virtual bool isCombineText() const { return true; } virtual float width(unsigned from, unsigned length, const Font&, float xPosition, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const; virtual const char* renderName() const { return "RenderCombineText"; } virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); @@ -48,13 +50,13 @@ private: inline RenderCombineText* toRenderCombineText(RenderObject* object) { - ASSERT(!object || object->isText()); + ASSERT(!object || object->isCombineText()); return static_cast<RenderCombineText*>(object); } inline const RenderCombineText* toRenderCombineText(const RenderObject* object) { - ASSERT(!object || object->isText()); + ASSERT(!object || object->isCombineText()); return static_cast<const RenderCombineText*>(object); } diff --git a/Source/WebCore/rendering/RenderCounter.cpp b/Source/WebCore/rendering/RenderCounter.cpp index f4a8736..9b8c493 100644 --- a/Source/WebCore/rendering/RenderCounter.cpp +++ b/Source/WebCore/rendering/RenderCounter.cpp @@ -316,7 +316,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& previousSibling = parent ? currentCounter : 0; return parent; } - // We are not a reset node or the previous reset must be on an ancestor of our renderer + // We are not a reset node or the previous reset must be on an ancestor of our owner renderer // hence we must be a child of that reset counter. parent = currentCounter; ASSERT(previousSibling->parent() == currentCounter); @@ -325,7 +325,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& // CurrentCounter, the counter at the EndSearchRenderer, is not reset. if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) { // If the node we are placing is not reset or we have found a counter that is attached - // to an ancestor of the placed counter's renderer we know we are a sibling of that node. + // to an ancestor of the placed counter's owner renderer we know we are a sibling of that node. ASSERT(currentCounter->parent() == previousSibling->parent()); parent = currentCounter->parent(); return true; @@ -352,9 +352,9 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& previousSibling = currentCounter; } } - // We come here if the previous sibling or parent of our renderer had no + // We come here if the previous sibling or parent of our owner renderer had no // good counter, or we are a reset node and the counter on the previous sibling - // of our renderer was not a reset counter. + // of our owner renderer was not a reset counter. // Set a new goal for the end of the search. searchEndRenderer = previousSiblingOrParent(currentRenderer); } else { @@ -450,11 +450,16 @@ RenderCounter::RenderCounter(Document* node, const CounterContent& counter) : RenderText(node, StringImpl::empty()) , m_counter(counter) , m_counterNode(0) + , m_nextForSameCounter(0) { } RenderCounter::~RenderCounter() { + if (m_counterNode) { + m_counterNode->removeRenderer(this); + ASSERT(!m_counterNode); + } } const char* RenderCounter::renderName() const @@ -469,12 +474,21 @@ bool RenderCounter::isCounter() const PassRefPtr<StringImpl> RenderCounter::originalText() const { - if (!parent()) - return 0; - - if (!m_counterNode) - m_counterNode = makeCounterNode(parent(), m_counter.identifier(), true); - + if (!m_counterNode) { + RenderObject* beforeAfterContainer = parent(); + while (true) { + if (!beforeAfterContainer) + return 0; + if (!beforeAfterContainer->isAnonymous()) + return 0; // RenderCounters are restricted to before and after pseudo elements + PseudoId containerStyle = beforeAfterContainer->style()->styleType(); + if ((containerStyle == BEFORE) || (containerStyle == AFTER)) + break; + beforeAfterContainer = beforeAfterContainer->parent(); + } + makeCounterNode(beforeAfterContainer, m_counter.identifier(), true)->addRenderer(const_cast<RenderCounter*>(this)); + ASSERT(m_counterNode); + } CounterNode* child = m_counterNode; int value = child->actsAsReset() ? child->value() : child->countInParent(); @@ -499,11 +513,12 @@ void RenderCounter::computePreferredLogicalWidths(float lead) RenderText::computePreferredLogicalWidths(lead); } -void RenderCounter::invalidate(const AtomicString& identifier) +void RenderCounter::invalidate() { - if (m_counter.identifier() != identifier) + m_counterNode->removeRenderer(this); + ASSERT(!m_counterNode); + if (documentBeingDestroyed()) return; - m_counterNode = 0; setNeedsLayoutAndPrefWidthsRecalc(); } @@ -512,28 +527,18 @@ static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* previous; for (RefPtr<CounterNode> child = node->lastDescendant(); child && child != node; child = previous) { previous = child->previousInPreOrder(); - 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()) { - RenderObjectChildList* children = child->renderer()->virtualChildren(); - if (children) - children->invalidateCounters(child->renderer(), identifier); - } - } - RenderObject* renderer = node->renderer(); - if (!renderer->documentBeingDestroyed()) { - if (RenderObjectChildList* children = renderer->virtualChildren()) - children->invalidateCounters(renderer, identifier); + child->parent()->removeChild(child.get()); + ASSERT(counterMaps().get(child->owner())->get(identifier.impl()) == child); + counterMaps().get(child->owner())->remove(identifier.impl()); } if (CounterNode* parent = node->parent()) - parent->removeChild(node, identifier); + parent->removeChild(node); } -void RenderCounter::destroyCounterNodes(RenderObject* renderer) +void RenderCounter::destroyCounterNodes(RenderObject* owner) { CounterMaps& maps = counterMaps(); - CounterMaps::iterator mapsIterator = maps.find(renderer); + CounterMaps::iterator mapsIterator = maps.find(owner); if (mapsIterator == maps.end()) return; CounterMap* map = mapsIterator->second; @@ -544,12 +549,12 @@ void RenderCounter::destroyCounterNodes(RenderObject* renderer) } maps.remove(mapsIterator); delete map; - renderer->m_hasCounterNodeMap = false; + owner->m_hasCounterNodeMap = false; } -void RenderCounter::destroyCounterNode(RenderObject* renderer, const AtomicString& identifier) +void RenderCounter::destroyCounterNode(RenderObject* owner, const AtomicString& identifier) { - CounterMap* map = counterMaps().get(renderer); + CounterMap* map = counterMaps().get(owner); if (!map) return; CounterMap::iterator mapIterator = map->find(identifier.impl()); @@ -600,7 +605,7 @@ static void updateCounters(RenderObject* renderer) if (newParent == parent && newPreviousSibling == node->previousSibling()) continue; if (parent) - parent->removeChild(node.get(), it->first.get()); + parent->removeChild(node.get()); if (newParent) newParent->insertAfter(node.get(), newPreviousSibling, it->first.get()); } @@ -684,6 +689,7 @@ void showCounterRendererTree(const WebCore::RenderObject* renderer, const char* current->nextSibling(), current->m_hasCounterNodeMap? counterName ? WebCore::counterMaps().get(current)->get(identifier.impl()).get() : (WebCore::CounterNode*)1 : (WebCore::CounterNode*)0); } + fflush(stderr); } #endif // NDEBUG diff --git a/Source/WebCore/rendering/RenderCounter.h b/Source/WebCore/rendering/RenderCounter.h index 35ffc35..9557ae3 100644 --- a/Source/WebCore/rendering/RenderCounter.h +++ b/Source/WebCore/rendering/RenderCounter.h @@ -34,12 +34,6 @@ public: RenderCounter(Document*, const CounterContent&); virtual ~RenderCounter(); - // Removes the reference to the CounterNode associated with this renderer - // if its identifier matches the argument. - // This is used to cause a counter display update when the CounterNode - // tree for identifier changes. - void invalidate(const AtomicString& identifier); - static void destroyCounterNodes(RenderObject*); static void destroyCounterNode(RenderObject*, const AtomicString& identifier); static void rendererSubtreeAttached(RenderObject*); @@ -52,8 +46,14 @@ private: virtual void computePreferredLogicalWidths(float leadWidth); + // Removes the reference to the CounterNode associated with this renderer. + // This is used to cause a counter display update when the CounterNode tree changes. + void invalidate(); + CounterContent m_counter; - mutable CounterNode* m_counterNode; + CounterNode* m_counterNode; + RenderCounter* m_nextForSameCounter; + friend class CounterNode; }; inline RenderCounter* toRenderCounter(RenderObject* object) diff --git a/Source/WebCore/rendering/RenderDataGrid.cpp b/Source/WebCore/rendering/RenderDataGrid.cpp index c322389..5057a5f 100644 --- a/Source/WebCore/rendering/RenderDataGrid.cpp +++ b/Source/WebCore/rendering/RenderDataGrid.cpp @@ -46,10 +46,16 @@ static const int cDefaultWidth = 300; RenderDataGrid::RenderDataGrid(Element* elt) : RenderBlock(elt) { + if (Page* page = frame()->page()) { + m_page = page; + m_page->addScrollableArea(this); + } } RenderDataGrid::~RenderDataGrid() { + if (m_page) + m_page->removeScrollableArea(this); } void RenderDataGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) @@ -256,6 +262,14 @@ IntPoint RenderDataGrid::convertFromContainingViewToScrollbar(const Scrollbar* s return point; } +bool RenderDataGrid::shouldSuspendScrollAnimations() const +{ + RenderView* view = this->view(); + if (!view) + return true; + return view->frameView()->shouldSuspendScrollAnimations(); +} + } #endif diff --git a/Source/WebCore/rendering/RenderDataGrid.h b/Source/WebCore/rendering/RenderDataGrid.h index 852010c..4f897d1 100644 --- a/Source/WebCore/rendering/RenderDataGrid.h +++ b/Source/WebCore/rendering/RenderDataGrid.h @@ -77,8 +77,13 @@ private: virtual IntRect convertFromContainingViewToScrollbar(const Scrollbar*, const IntRect&) const; virtual IntPoint convertFromScrollbarToContainingView(const Scrollbar*, const IntPoint&) const; virtual IntPoint convertFromContainingViewToScrollbar(const Scrollbar*, const IntPoint&) const; + virtual bool shouldSuspendScrollAnimations() const; + + virtual void disconnectFromPage() { m_page = 0; } RefPtr<Scrollbar> m_vBar; + + Page* m_page; }; } diff --git a/Source/WebCore/rendering/RenderDetails.cpp b/Source/WebCore/rendering/RenderDetails.cpp index a1039f9..be2b435 100644 --- a/Source/WebCore/rendering/RenderDetails.cpp +++ b/Source/WebCore/rendering/RenderDetails.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,20 +21,316 @@ #include "config.h" #include "RenderDetails.h" +#include "CSSStyleSelector.h" +#include "HTMLDetailsElement.h" +#include "HTMLNames.h" +#include "LocalizedStrings.h" +#include "RenderDetailsMarker.h" +#include "RenderTextFragment.h" +#include "RenderView.h" + namespace WebCore { -RenderDetails::RenderDetails(Node* element) - : RenderBlock(element) +using namespace HTMLNames; + +RenderDetails::RenderDetails(Node* node) + : RenderBlock(node) + , m_summaryBlock(0) + , m_contentBlock(0) + , m_defaultSummaryBlock(0) + , m_defaultSummaryText(0) + , m_marker(0) + , m_mainSummary(0) +{ +} + +void RenderDetails::destroy() +{ + if (m_marker) { + m_marker->destroy(); + m_marker = 0; + } + + RenderBlock::destroy(); +} + +RenderBlock* RenderDetails::summaryBlock() +{ + if (!m_summaryBlock) { + m_summaryBlock = createAnonymousBlock(); + RenderBlock::addChild(m_summaryBlock, m_contentBlock); + } + return m_summaryBlock; +} + +RenderBlock* RenderDetails::contentBlock() { + if (!m_contentBlock) { + m_contentBlock = createAnonymousBlock(); + RenderBlock::addChild(m_contentBlock); + } + return m_contentBlock; +} + +void RenderDetails::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + if (beforeChild && beforeChild == m_mainSummary) + beforeChild = getRenderPosition(m_mainSummary); + contentBlock()->addChild(newChild, beforeChild); +} + +void RenderDetails::removeChild(RenderObject* oldChild) +{ + if (oldChild == m_summaryBlock) { + RenderBlock::removeChild(oldChild); + m_summaryBlock = 0; + return; + } + + if (oldChild == m_contentBlock) { + RenderBlock::removeChild(oldChild); + m_contentBlock = 0; + return; + } + + if (oldChild == m_mainSummary && m_summaryBlock) { + m_summaryBlock->removeChild(m_mainSummary); + return; + } + + if (m_contentBlock) { + m_contentBlock->removeChild(oldChild); + return; + } + + ASSERT_NOT_REACHED(); +} + +void RenderDetails::setMarkerStyle() +{ + if (m_marker) { + RefPtr<RenderStyle> markerStyle = RenderStyle::create(); + markerStyle->inheritFrom(style()); + m_marker->setStyle(markerStyle.release()); + } } void RenderDetails::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); + if (m_defaultSummaryBlock) { + m_defaultSummaryBlock->setStyle(createSummaryStyle()); + m_defaultSummaryText->setStyle(m_defaultSummaryBlock->style()); + } + + setMarkerStyle(); + // Ensure that if we ended up being inline that we set our replaced flag // so that we're treated like an inline-block. setReplaced(isInline()); } +RenderObject* RenderDetails::getRenderPosition(RenderObject* object) +{ + if (!object || !object->node()) + return 0; + + Node* element = object->node()->nextSibling(); + + while (element && !element->renderer()) + element = element->nextSibling(); + + return element ? element->renderer() : 0; +} + +void RenderDetails::markerDestroyed() +{ + m_marker = 0; +} + +void RenderDetails::summaryDestroyed(RenderObject* summary) +{ + if (summary == m_mainSummary) + m_mainSummary = 0; +} + +void RenderDetails::moveSummaryToContents() +{ + if (m_defaultSummaryBlock) { + ASSERT(!m_mainSummary); + m_defaultSummaryBlock->destroy(); + m_defaultSummaryBlock = 0; + m_defaultSummaryText = 0; + return; + } + + if (!m_mainSummary) + return; + + m_mainSummary->remove(); + contentBlock()->addChild(m_mainSummary, getRenderPosition(m_mainSummary)); + m_mainSummary = 0; +} + +PassRefPtr<RenderStyle> RenderDetails::createSummaryStyle() +{ + RefPtr<HTMLElement> summary(HTMLElement::create(summaryTag, document())); + return document()->styleSelector()->styleForElement(summary.get(), style(), true); +} + +void RenderDetails::replaceMainSummary(RenderObject* newSummary) +{ + ASSERT(newSummary); + if (m_mainSummary == newSummary) + return; + + moveSummaryToContents(); + newSummary->remove(); + summaryBlock()->addChild(newSummary); + m_mainSummary = newSummary; +} + +void RenderDetails::createDefaultSummary() +{ + if (m_defaultSummaryBlock) + return; + + moveSummaryToContents(); + + m_defaultSummaryBlock = summaryBlock()->createAnonymousBlock(); + m_defaultSummaryBlock->setStyle(createSummaryStyle()); + + m_defaultSummaryText = new (renderArena()) RenderTextFragment(document(), defaultDetailsSummaryText().impl()); + m_defaultSummaryText->setStyle(m_defaultSummaryBlock->style()); + m_defaultSummaryBlock->addChild(m_defaultSummaryText); + + summaryBlock()->addChild(m_defaultSummaryBlock); +} + +void RenderDetails::checkMainSummary() +{ + if (!node() || !node()->hasTagName(detailsTag)) + return; + + Node* mainSummaryNode = static_cast<HTMLDetailsElement*>(node())->mainSummary(); + + if (!mainSummaryNode || !mainSummaryNode->renderer() || mainSummaryNode->renderer()->isFloatingOrPositioned()) + createDefaultSummary(); + else + replaceMainSummary(mainSummaryNode->renderer()); + +} + +void RenderDetails::layout() +{ + ASSERT(needsLayout()); + + checkMainSummary(); + ASSERT(m_summaryBlock); + + if (!m_marker) { + m_marker = new (renderArena()) RenderDetailsMarker(this); + setMarkerStyle(); + } + updateMarkerLocation(); + + RenderBlock::layout(); + + m_interactiveArea = m_summaryBlock->frameRect(); + + // FIXME: the following code will not be needed once absoluteToLocal get patched to handle flipped blocks writing modes. + switch (style()->writingMode()) { + case TopToBottomWritingMode: + case LeftToRightWritingMode: + break; + case RightToLeftWritingMode: { + m_interactiveArea.setX(width() - m_interactiveArea.x() - m_interactiveArea.width()); + break; + } + case BottomToTopWritingMode: { + m_interactiveArea.setY(height() - m_interactiveArea.y() - m_interactiveArea.height()); + break; + } + } } + +bool RenderDetails::isOpen() const +{ + return node() && node()->isElementNode() ? !static_cast<Element*>(node())->getAttribute(openAttr).isNull() : false; +} + +RenderObject* RenderDetails::getParentOfFirstLineBox(RenderBlock* curr) +{ + RenderObject* firstChild = curr->firstChild(); + if (!firstChild) + return 0; + + for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) { + if (currChild == m_marker) + continue; + + if (currChild->isInline() && (!currChild->isRenderInline() || curr->generatesLineBoxesForInlineChild(currChild))) + return curr; + + if (currChild->isFloating() || currChild->isPositioned()) + continue; + + if (currChild->isTable() || !currChild->isRenderBlock() || (currChild->isBox() && toRenderBox(currChild)->isWritingModeRoot())) + break; + + if (currChild->isDetails()) + break; + + RenderObject* lineBox = getParentOfFirstLineBox(toRenderBlock(currChild)); + if (lineBox) + return lineBox; + } + + return 0; +} + +RenderObject* RenderDetails::firstNonMarkerChild(RenderObject* parent) +{ + RenderObject* result = parent->firstChild(); + while (result && result->isDetailsMarker()) + result = result->nextSibling(); + return result; +} + +void RenderDetails::updateMarkerLocation() +{ + // Sanity check the location of our marker. + if (m_marker) { + RenderObject* markerPar = m_marker->parent(); + RenderObject* lineBoxParent = getParentOfFirstLineBox(m_summaryBlock); + if (!lineBoxParent) { + // If the marker is currently contained inside an anonymous box, + // then we are the only item in that anonymous box (since no line box + // parent was found). It's ok to just leave the marker where it is + // in this case. + if (markerPar && markerPar->isAnonymousBlock()) + lineBoxParent = markerPar; + else + lineBoxParent = m_summaryBlock; + } + + if (markerPar != lineBoxParent || m_marker->preferredLogicalWidthsDirty()) { + // Removing and adding the marker can trigger repainting in + // containers other than ourselves, so we need to disable LayoutState. + view()->disableLayoutState(); + m_marker->remove(); + if (!lineBoxParent) + lineBoxParent = m_summaryBlock; + lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent)); + + if (m_marker->preferredLogicalWidthsDirty()) + m_marker->computePreferredLogicalWidths(); + + view()->enableLayoutState(); + } + } +} + +} // namespace WebCore + diff --git a/Source/WebCore/rendering/RenderDetails.h b/Source/WebCore/rendering/RenderDetails.h index b8aebab..0b56c13 100644 --- a/Source/WebCore/rendering/RenderDetails.h +++ b/Source/WebCore/rendering/RenderDetails.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,18 +21,65 @@ #ifndef RenderDetails_h #define RenderDetails_h -#include "RenderBlock.h" +#include "RenderFlexibleBox.h" +#include "Timer.h" +#include <wtf/OwnPtr.h> namespace WebCore { +class RenderDetailsMarker; + class RenderDetails : public RenderBlock { public: explicit RenderDetails(Node*); -private: virtual const char* renderName() const { return "RenderDetails"; } virtual bool isDetails() const { return true; } + + bool isOpen() const; + IntRect interactiveArea() const { return m_interactiveArea; } + void markerDestroyed(); + void summaryDestroyed(RenderObject*); + +private: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + virtual void addChild(RenderObject* newChild, RenderObject *beforeChild = 0); + virtual void removeChild(RenderObject*); + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { } + virtual bool createsAnonymousWrapper() const { return true; } + + virtual bool requiresForcedStyleRecalcPropagation() const { return true; } + + virtual void layout(); + virtual void destroy(); + + void createDefaultSummary(); + void replaceMainSummary(RenderObject*); + void moveSummaryToContents(); + void checkMainSummary(); + RenderObject* getRenderPosition(RenderObject*); + PassRefPtr<RenderStyle> createSummaryStyle(); + void setMarkerStyle(); + + RenderBlock* summaryBlock(); + RenderBlock* contentBlock(); + + RenderObject* getParentOfFirstLineBox(RenderBlock* curr); + RenderObject* firstNonMarkerChild(RenderObject* parent); + void updateMarkerLocation(); + + RenderBlock* m_summaryBlock; + RenderBlock* m_contentBlock; + + RenderObject* m_defaultSummaryBlock; + RenderObject* m_defaultSummaryText; + + IntRect m_interactiveArea; + + RenderDetailsMarker* m_marker; + + RenderObject* m_mainSummary; }; inline RenderDetails* toRenderDetails(RenderObject* object) @@ -44,6 +91,6 @@ inline RenderDetails* toRenderDetails(RenderObject* object) // This will catch anyone doing an unnecessary cast. void toRenderDetails(const RenderDetails*); -} +} // namespace WebCore #endif // RenderDetails_h diff --git a/Source/WebCore/rendering/RenderDetailsMarker.cpp b/Source/WebCore/rendering/RenderDetailsMarker.cpp index 26e49d9..e040eb3 100644 --- a/Source/WebCore/rendering/RenderDetailsMarker.cpp +++ b/Source/WebCore/rendering/RenderDetailsMarker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,11 +21,182 @@ #include "config.h" #include "RenderDetailsMarker.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "PaintInfo.h" +#include "RenderDetails.h" +#include "RenderSummary.h" + namespace WebCore { -RenderDetailsMarker::RenderDetailsMarker(Node* element) - : RenderBlock(element) +using namespace HTMLNames; + +RenderDetailsMarker::RenderDetailsMarker(RenderDetails* item) + : RenderBox(item->document()) + , m_details(item) +{ + setInline(true); + setReplaced(true); +} + +void RenderDetailsMarker::destroy() +{ + if (m_details) + m_details->markerDestroyed(); + + RenderBox::destroy(); +} + +int RenderDetailsMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode) const +{ + return m_details->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes); +} + +int RenderDetailsMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode) const +{ + return m_details->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes); +} + +void RenderDetailsMarker::computePreferredLogicalWidths() +{ + ASSERT(preferredLogicalWidthsDirty()); + + m_minPreferredLogicalWidth = 2 * style()->fontMetrics().ascent() / 3; + m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth; + + setPreferredLogicalWidthsDirty(false); +} + +void RenderDetailsMarker::layout() +{ + ASSERT(needsLayout()); + + setLogicalWidth(minPreferredLogicalWidth()); + setLogicalHeight(style()->fontMetrics().height()); + + setMarginStart(0); + setMarginEnd(style()->fontMetrics().ascent() - minPreferredLogicalWidth() + 1); + + setNeedsLayout(false); +} + +IntRect RenderDetailsMarker::getRelativeMarkerRect() const +{ + IntRect relativeRect; + + int bulletWidth = minPreferredLogicalWidth(); + relativeRect = IntRect((logicalWidth() - bulletWidth) / 2, (logicalHeight() - bulletWidth) / 2, bulletWidth, bulletWidth); + + if (!style()->isHorizontalWritingMode()) { + relativeRect = relativeRect.transposedRect(); + relativeRect.setX(width() - relativeRect.x() - relativeRect.width()); + } + + return relativeRect; +} + +bool RenderDetailsMarker::isOpen() const { + return m_details && m_details->isOpen(); +} + +static Path createPath(const FloatPoint* path) +{ + Path result; + result.moveTo(FloatPoint(path[0].x(), path[0].y())); + for (int i = 1; i < 4; ++i) + result.addLineTo(FloatPoint(path[i].x(), path[i].y())); + return result; +} + +static Path createDownArrowPath() +{ + FloatPoint points[4] = { FloatPoint(0.0f, 0.07f), FloatPoint(0.5f, 0.93f), FloatPoint(1.0f, 0.07f), FloatPoint(0.0f, 0.07f) }; + return createPath(points); +} + +static Path createUpArrowPath() +{ + FloatPoint points[4] = { FloatPoint(0.0f, 0.93f), FloatPoint(0.5f, 0.07f), FloatPoint(1.0f, 0.93f), FloatPoint(0.0f, 0.93f) }; + return createPath(points); +} + +static Path createLeftArrowPath() +{ + FloatPoint points[4] = { FloatPoint(1.0f, 0.0f), FloatPoint(0.14f, 0.5f), FloatPoint(1.0f, 1.0f), FloatPoint(1.0f, 0.0f) }; + return createPath(points); +} + +static Path createRightArrowPath() +{ + FloatPoint points[4] = { FloatPoint(0.0f, 0.0f), FloatPoint(0.86f, 0.5f), FloatPoint(0.0f, 1.0f), FloatPoint(0.0f, 0.0f) }; + return createPath(points); +} + +RenderDetailsMarker::Orientation RenderDetailsMarker::orientation() const +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + if (style()->isLeftToRightDirection()) + return isOpen() ? Down : Right; + return isOpen() ? Down : Left; + case RightToLeftWritingMode: + if (style()->isLeftToRightDirection()) + return isOpen() ? Left : Down; + return isOpen() ? Left : Up; + case LeftToRightWritingMode: + if (style()->isLeftToRightDirection()) + return isOpen() ? Right : Down; + return isOpen() ? Right : Up; + case BottomToTopWritingMode: + if (style()->isLeftToRightDirection()) + return isOpen() ? Up : Right; + return isOpen() ? Up : Left; + } + return Right; +} + +Path RenderDetailsMarker::getCanonicalPath() const +{ + switch (orientation()) { + case Left: return createLeftArrowPath(); + case Right: return createRightArrowPath(); + case Up: return createUpArrowPath(); + case Down: return createDownArrowPath(); + } + + return Path(); +} + +Path RenderDetailsMarker::getPath(const IntPoint& origin) const +{ + IntRect rect = getRelativeMarkerRect(); + Path result = getCanonicalPath(); + result.transform(AffineTransform().scale(rect.width())); + result.translate(FloatSize(origin.x() + rect.x(), origin.y() + rect.y())); + return result; +} + +void RenderDetailsMarker::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (paintInfo.phase != PaintPhaseForeground || style()->visibility() != VISIBLE) + return; + + IntPoint boxOrigin(tx + x(), ty + y()); + IntRect overflowRect(visualOverflowRect()); + overflowRect.move(boxOrigin.x(), boxOrigin.y()); + overflowRect.inflate(maximalOutlineSize(paintInfo.phase)); + + if (!paintInfo.rect.intersects(overflowRect)) + return; + + const Color color(style()->visitedDependentColor(CSSPropertyColor)); + paintInfo.context->setStrokeColor(color, style()->colorSpace()); + paintInfo.context->setStrokeStyle(SolidStroke); + paintInfo.context->setStrokeThickness(1.0f); + paintInfo.context->setFillColor(color, style()->colorSpace()); + + paintInfo.context->fillPath(getPath(boxOrigin)); } } diff --git a/Source/WebCore/rendering/RenderDetailsMarker.h b/Source/WebCore/rendering/RenderDetailsMarker.h index 08bdbd8..de8f60b 100644 --- a/Source/WebCore/rendering/RenderDetailsMarker.h +++ b/Source/WebCore/rendering/RenderDetailsMarker.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,25 +21,52 @@ #ifndef RenderDetailsMarker_h #define RenderDetailsMarker_h -#include "RenderBlock.h" +#include "RenderBox.h" namespace WebCore { -class RenderDetailsMarker : public RenderBlock { +class RenderDetails; + +class RenderDetailsMarker : public RenderBox { public: - explicit RenderDetailsMarker(Node*); + RenderDetailsMarker(RenderDetails*); + + enum Orientation { Up, Down, Left, Right }; + + Orientation orientation() const; + + virtual void computePreferredLogicalWidths(); + virtual void destroy(); private: virtual const char* renderName() const { return "RenderDetailsMarker"; } virtual bool isDetailsMarker() const { return true; } + virtual void paint(PaintInfo&, int tx, int ty); + virtual void layout(); + virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + + IntRect getRelativeMarkerRect() const; + + bool isOpen() const; + Path getCanonicalPath() const; + Path getPath(const IntPoint& origin) const; + + RenderDetails* m_details; }; inline RenderDetailsMarker* toRenderDetailsMarker(RenderObject* object) { - ASSERT(!object || object->isDetails()); + ASSERT(!object || object->isDetailsMarker()); return static_cast<RenderDetailsMarker*>(object); } +inline const RenderDetailsMarker* toRenderDetailsMarker(const RenderObject* object) +{ + ASSERT(!object || object->isDetailsMarker()); + return static_cast<const RenderDetailsMarker*>(object); +} + // This will catch anyone doing an unnecessary cast. void toRenderDetailsMarker(const RenderDetailsMarker*); diff --git a/Source/WebCore/rendering/RenderFlexibleBox.cpp b/Source/WebCore/rendering/RenderFlexibleBox.cpp index 8241dcd..4d97da0 100644 --- a/Source/WebCore/rendering/RenderFlexibleBox.cpp +++ b/Source/WebCore/rendering/RenderFlexibleBox.cpp @@ -214,7 +214,7 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int /*pageHeight FIXM { ASSERT(needsLayout()); - if (!relayoutChildren && layoutOnlyPositionedObjects()) + if (!relayoutChildren && simplifiedLayout()) return; LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); @@ -421,13 +421,11 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) if (child->isPositioned()) { child->containingBlock()->insertPositionedObject(child); RenderLayer* childLayer = child->layer(); - if (child->style()->hasStaticInlinePosition(style()->isHorizontalWritingMode())) - childLayer->setStaticInlinePosition(xPos); - if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) { - if (childLayer->staticBlockPosition() != yPos) { - childLayer->setStaticBlockPosition(yPos); + childLayer->setStaticInlinePosition(xPos); + if (childLayer->staticBlockPosition() != yPos) { + childLayer->setStaticBlockPosition(yPos); + if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) child->setChildNeedsLayout(true, false); - } } child = iterator.next(); continue; @@ -684,17 +682,11 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) if (child->isPositioned()) { child->containingBlock()->insertPositionedObject(child); RenderLayer* childLayer = child->layer(); - if (child->style()->hasStaticInlinePosition(style()->isHorizontalWritingMode())) { - if (style()->isLeftToRightDirection()) - childLayer->setStaticInlinePosition(borderLeft() + paddingLeft()); - else - childLayer->setStaticInlinePosition(borderRight() + paddingRight()); - } - if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) { - if (childLayer->staticBlockPosition() != height()) { - childLayer->setStaticBlockPosition(height()); + childLayer->setStaticInlinePosition(borderStart() + paddingStart()); + if (childLayer->staticBlockPosition() != height()) { + childLayer->setStaticBlockPosition(height()); + if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) child->setChildNeedsLayout(true, false); - } } child = iterator.next(); continue; diff --git a/Source/WebCore/rendering/RenderFullScreen.cpp b/Source/WebCore/rendering/RenderFullScreen.cpp index cc53a6e..a685064 100644 --- a/Source/WebCore/rendering/RenderFullScreen.cpp +++ b/Source/WebCore/rendering/RenderFullScreen.cpp @@ -64,7 +64,7 @@ PassRefPtr<RenderStyle> RenderFullScreen::createFullScreenStyle() fullscreenStyle->setBackgroundColor(Color::black); - return fullscreenStyle; + return fullscreenStyle.release(); } #endif diff --git a/Source/WebCore/rendering/RenderInline.cpp b/Source/WebCore/rendering/RenderInline.cpp index 234d63c..3a27307 100644 --- a/Source/WebCore/rendering/RenderInline.cpp +++ b/Source/WebCore/rendering/RenderInline.cpp @@ -201,8 +201,7 @@ void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderOb // inline into continuations. This involves creating an anonymous block box to hold // |newChild|. We then make that block box a continuation of this inline. We take all of // the children after |beforeChild| and put them in a clone of this object. - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(style()); + RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style()); newStyle->setDisplay(BLOCK); RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); @@ -485,29 +484,21 @@ static int computeMargin(const RenderInline* renderer, const Length& margin) int RenderInline::marginLeft() const { - if (!style()->isHorizontalWritingMode()) - return 0; return computeMargin(this, style()->marginLeft()); } int RenderInline::marginRight() const { - if (!style()->isHorizontalWritingMode()) - return 0; return computeMargin(this, style()->marginRight()); } int RenderInline::marginTop() const { - if (style()->isHorizontalWritingMode()) - return 0; return computeMargin(this, style()->marginTop()); } int RenderInline::marginBottom() const { - if (style()->isHorizontalWritingMode()) - return 0; return computeMargin(this, style()->marginBottom()); } @@ -521,6 +512,16 @@ int RenderInline::marginEnd() const return computeMargin(this, style()->marginEnd()); } +int RenderInline::marginBefore() const +{ + return computeMargin(this, style()->marginBefore()); +} + +int RenderInline::marginAfter() const +{ + return computeMargin(this, style()->marginAfter()); +} + const char* RenderInline::renderName() const { if (isRelPositioned()) diff --git a/Source/WebCore/rendering/RenderInline.h b/Source/WebCore/rendering/RenderInline.h index 18b4a3c..08ac002 100644 --- a/Source/WebCore/rendering/RenderInline.h +++ b/Source/WebCore/rendering/RenderInline.h @@ -42,8 +42,8 @@ public: virtual int marginRight() const; virtual int marginTop() const; virtual int marginBottom() const; - virtual int marginBefore() const { return 0; } - virtual int marginAfter() const { return 0; } + virtual int marginBefore() const; + virtual int marginAfter() const; virtual int marginStart() const; virtual int marginEnd() const; diff --git a/Source/WebCore/rendering/RenderLayer.cpp b/Source/WebCore/rendering/RenderLayer.cpp index bc0d440..1c60500 100644 --- a/Source/WebCore/rendering/RenderLayer.cpp +++ b/Source/WebCore/rendering/RenderLayer.cpp @@ -178,9 +178,13 @@ RenderLayer::RenderLayer(RenderBoxModelObject* renderer) , m_hasCompositingDescendant(false) , m_mustOverlapCompositedLayers(false) #endif +<<<<<<< HEAD #if ENABLE(ANDROID_OVERFLOW_SCROLL) , m_hasOverflowScroll(false) #endif +======= + , m_containsDirtyOverlayScrollbars(false) +>>>>>>> webkit.org at r82507 , m_marquee(0) , m_staticInlinePosition(0) , m_staticBlockPosition(0) @@ -194,6 +198,13 @@ RenderLayer::RenderLayer(RenderBoxModelObject* renderer) m_visibleContentStatusDirty = false; m_hasVisibleContent = renderer->style()->visibility() == VISIBLE; } + + if (Frame* frame = renderer->frame()) { + if (Page* page = frame->page()) { + m_page = page; + m_page->addScrollableArea(this); + } + } } RenderLayer::~RenderLayer() @@ -203,6 +214,9 @@ RenderLayer::~RenderLayer() frame->eventHandler()->resizeLayerDestroyed(); } + if (m_page) + m_page->removeScrollableArea(this); + destroyScrollbar(HorizontalScrollbar); destroyScrollbar(VerticalScrollbar); @@ -268,19 +282,6 @@ bool RenderLayer::canRender3DTransforms() const void RenderLayer::updateLayerPositions(UpdateLayerPositionsFlags flags, IntPoint* cachedOffset) { - if (flags & DoFullRepaint) { - renderer()->repaint(); -#if USE(ACCELERATED_COMPOSITING) - flags &= ~CheckForRepaint; - // We need the full repaint to propagate to child layers if we are hardware compositing. - if (!compositor()->inCompositingMode()) - flags &= ~DoFullRepaint; -#else - flags &= ~(CheckForRepaint | DoFullRepaint); -#endif - } - - updateLayerPosition(); // For relpositioned layers or non-positioned layers, // we need to keep in sync, since we may have shifted relative // to our parent layer. @@ -1121,6 +1122,7 @@ void RenderLayer::removeOnlyThisLayer() RenderLayer* next = current->nextSibling(); removeChild(current); parent->addChild(current, nextSib); + current->setNeedsFullRepaint(); current->updateLayerPositions(); // Depends on hasLayer() already being false for proper layout. current = next; } @@ -1806,6 +1808,19 @@ int RenderLayer::visibleWidth() const return m_width; } +bool RenderLayer::shouldSuspendScrollAnimations() const +{ + RenderView* view = renderer()->view(); + if (!view) + return true; + return view->frameView()->shouldSuspendScrollAnimations(); +} + +IntPoint RenderLayer::currentMousePosition() const +{ + return renderer()->frame() ? renderer()->frame()->eventHandler()->currentMousePosition() : IntPoint(); +} + IntSize RenderLayer::scrollbarOffset(const Scrollbar* scrollbar) const { RenderBox* box = renderBox(); @@ -1915,16 +1930,16 @@ void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar) #endif } -int RenderLayer::verticalScrollbarWidth() const +int RenderLayer::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy) const { - if (!m_vBar || m_vBar->isOverlayScrollbar()) + if (!m_vBar || (m_vBar->isOverlayScrollbar() && relevancy == IgnoreOverlayScrollbarSize)) return 0; return m_vBar->width(); } -int RenderLayer::horizontalScrollbarHeight() const +int RenderLayer::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const { - if (!m_hBar || m_hBar->isOverlayScrollbar()) + if (!m_hBar || (m_hBar->isOverlayScrollbar() && relevancy == IgnoreOverlayScrollbarSize)) return 0; return m_hBar->height(); } @@ -2214,16 +2229,36 @@ void RenderLayer::updateScrollInfoAfterLayout() #endif } -void RenderLayer::paintOverflowControls(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) +void RenderLayer::paintOverflowControls(GraphicsContext* context, int tx, int ty, const IntRect& damageRect, bool paintingOverlayControls) { // Don't do anything if we have no overflow. if (!renderer()->hasOverflowClip()) return; - + + // Overlay scrollbars paint in a second pass through the layer tree so that they will paint + // on top of everything else. If this is the normal painting pass, paintingOverlayControls + // will be false, and we should just tell the root layer that there are overlay scrollbars + // that need to be painted. That will cause the second pass through the layer tree to run, + // and we'll paint the scrollbars then. In the meantime, cache tx and ty so that the + // second pass doesn't need to re-enter the RenderTree to get it right. + if (hasOverlayScrollbars() && !paintingOverlayControls) { + RenderLayer* rootLayer = renderer()->view()->layer(); + rootLayer->setContainsDirtyOverlayScrollbars(true); + m_cachedOverlayScrollbarOffset = IntPoint(tx, ty); + return; + } + + int offsetX = tx; + int offsetY = ty; + if (paintingOverlayControls) { + offsetX = m_cachedOverlayScrollbarOffset.x(); + offsetY = m_cachedOverlayScrollbarOffset.y(); + } + // Move the scrollbar widgets if necessary. We normally move and resize widgets during layout, but sometimes // widgets can move without layout occurring (most notably when you scroll a document that // contains fixed positioned elements). - positionOverflowControls(tx, ty); + positionOverflowControls(offsetX, offsetY); // Now that we're sure the scrollbars are in the right place, paint them. if (m_hBar) @@ -2233,10 +2268,10 @@ void RenderLayer::paintOverflowControls(GraphicsContext* context, int tx, int ty // We fill our scroll corner with white if we have a scrollbar that doesn't run all the way up to the // edge of the box. - paintScrollCorner(context, tx, ty, damageRect); + paintScrollCorner(context, offsetX, offsetY, damageRect); // Paint our resizer last, since it sits on top of the scroll corner. - paintResizer(context, tx, ty, damageRect); + paintResizer(context, offsetX, offsetY, damageRect); } void RenderLayer::paintScrollCorner(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) @@ -2258,8 +2293,11 @@ void RenderLayer::paintScrollCorner(GraphicsContext* context, int tx, int ty, co m_scrollCorner->paintIntoRect(context, tx, ty, absRect); return; } - - context->fillRect(absRect, Color::white, box->style()->colorSpace()); + + // We don't want to paint white if we have overlay scrollbars, since we need + // to see what is behind it. + if (!hasOverlayScrollbars()) + context->fillRect(absRect, Color::white, box->style()->colorSpace()); } void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) @@ -2376,6 +2414,15 @@ void RenderLayer::paint(GraphicsContext* p, const IntRect& damageRect, PaintBeha it->first->setOverlapTestResult(false); } +void RenderLayer::paintOverlayScrollbars(GraphicsContext* p, const IntRect& damageRect, PaintBehavior paintBehavior, RenderObject *paintingRoot) +{ + if (!m_containsDirtyOverlayScrollbars) + return; + paintLayer(this, p, damageRect, paintBehavior, paintingRoot, 0, PaintLayerHaveTransparency | PaintLayerTemporaryClipRects + | PaintLayerPaintingOverlayScrollbars); + m_containsDirtyOverlayScrollbars = false; +} + static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) { if (paintDirtyRect == clipRect) @@ -2526,9 +2573,11 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, if (overlapTestRequests && isSelfPaintingLayer()) performOverlapTests(*overlapTestRequests, rootLayer, this); + bool paintingOverlayScrollbars = paintFlags & PaintLayerPaintingOverlayScrollbars; + // We want to paint our layer, but only if we intersect the damage rect. bool shouldPaint = intersectsDamageRect(layerBounds, damageRect, rootLayer) && m_hasVisibleContent && isSelfPaintingLayer(); - if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) { + if (shouldPaint && !selectionOnly && !damageRect.isEmpty() && !paintingOverlayScrollbars) { // Begin transparency layers lazily now that we know we have to paint something. if (haveTransparency) beginTransparencyLayers(p, rootLayer, paintBehavior); @@ -2549,7 +2598,7 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, paintList(m_negZOrderList, rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags); // Now establish the appropriate clip and paint our child RenderObjects. - if (shouldPaint && !clipRectToApply.isEmpty()) { + if (shouldPaint && !clipRectToApply.isEmpty() && !paintingOverlayScrollbars) { // Begin transparency layers lazily now that we know we have to paint something. if (haveTransparency) beginTransparencyLayers(p, rootLayer, paintBehavior); @@ -2574,7 +2623,7 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, restoreClip(p, paintDirtyRect, clipRectToApply); } - if (!outlineRect.isEmpty() && isSelfPaintingLayer()) { + if (!outlineRect.isEmpty() && isSelfPaintingLayer() && !paintingOverlayScrollbars) { // Paint our own outline PaintInfo paintInfo(p, outlineRect, PaintPhaseSelfOutline, false, paintingRootForRenderer, 0); setClip(p, paintDirtyRect, outlineRect); @@ -2588,7 +2637,7 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, // Now walk the sorted list of children with positive z-indices. paintList(m_posZOrderList, rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags); - if (renderer()->hasMask() && shouldPaint && !selectionOnly && !damageRect.isEmpty()) { + if (renderer()->hasMask() && shouldPaint && !selectionOnly && !damageRect.isEmpty() && !paintingOverlayScrollbars) { setClip(p, paintDirtyRect, damageRect); // Paint the mask. @@ -2599,6 +2648,12 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, restoreClip(p, paintDirtyRect, damageRect); } + if (paintingOverlayScrollbars) { + setClip(p, paintDirtyRect, damageRect); + paintOverflowControls(p, tx, ty, damageRect, true); + restoreClip(p, paintDirtyRect, damageRect); + } + // End our transparency layer if (haveTransparency && m_usedTransparency && !m_paintingInsideReflection) { p->endTransparencyLayer(); diff --git a/Source/WebCore/rendering/RenderLayer.h b/Source/WebCore/rendering/RenderLayer.h index 7dddbc8..d2d41df 100644 --- a/Source/WebCore/rendering/RenderLayer.h +++ b/Source/WebCore/rendering/RenderLayer.h @@ -282,8 +282,8 @@ public: Scrollbar* horizontalScrollbar() const { return m_hBar.get(); } Scrollbar* verticalScrollbar() const { return m_vBar.get(); } - int verticalScrollbarWidth() const; - int horizontalScrollbarHeight() const; + int verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy = IgnoreOverlayScrollbarSize) const; + int horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy = IgnoreOverlayScrollbarSize) const; bool hasOverflowControls() const; #if ENABLE(ANDROID_OVERFLOW_SCROLL) @@ -295,7 +295,7 @@ public: bool hitTestOverflowControls(HitTestResult&, const IntPoint& localPoint); IntSize offsetFromResizeCorner(const IntPoint& absolutePoint) const; - void paintOverflowControls(GraphicsContext*, int tx, int ty, const IntRect& damageRect); + void paintOverflowControls(GraphicsContext*, int tx, int ty, const IntRect& damageRect, bool paintingOverlayControls = false); void paintScrollCorner(GraphicsContext*, int tx, int ty, const IntRect& damageRect); void paintResizer(GraphicsContext*, int tx, int ty, const IntRect& damageRect); @@ -327,14 +327,13 @@ public: void updateLayerPosition(); enum UpdateLayerPositionsFlag { - DoFullRepaint = 1, - CheckForRepaint = 1 << 1, - IsCompositingUpdateRoot = 1 << 2, - UpdateCompositingLayers = 1 << 3, - UpdatePagination = 1 << 4 + CheckForRepaint = 1, + IsCompositingUpdateRoot = 1 << 1, + UpdateCompositingLayers = 1 << 2, + UpdatePagination = 1 << 3 }; typedef unsigned UpdateLayerPositionsFlags; - void updateLayerPositions(UpdateLayerPositionsFlags = DoFullRepaint | IsCompositingUpdateRoot | UpdateCompositingLayers, IntPoint* cachedOffset = 0); + void updateLayerPositions(UpdateLayerPositionsFlags = CheckForRepaint | IsCompositingUpdateRoot | UpdateCompositingLayers, IntPoint* cachedOffset = 0); void updateTransform(); @@ -409,6 +408,7 @@ public: // layers that intersect the point from front to back. void paint(GraphicsContext*, const IntRect& damageRect, PaintBehavior = PaintBehaviorNormal, RenderObject* paintingRoot = 0); bool hitTest(const HitTestRequest&, HitTestResult&); + void paintOverlayScrollbars(GraphicsContext*, const IntRect& damageRect, PaintBehavior, RenderObject* paintingRoot); // This method figures out our layerBounds in coordinates relative to // |rootLayer}. It also computes our background and foreground clip rects @@ -491,6 +491,9 @@ public: bool paintsWithTransform(PaintBehavior) const; + bool containsDirtyOverlayScrollbars() const { return m_containsDirtyOverlayScrollbars; } + void setContainsDirtyOverlayScrollbars(bool dirtyScrollbars) { m_containsDirtyOverlayScrollbars = dirtyScrollbars; } + private: // The normal operator new is disallowed on all render objects. void* operator new(size_t) throw(); @@ -513,7 +516,8 @@ private: PaintLayerHaveTransparency = 1, PaintLayerAppliedTransform = 1 << 1, PaintLayerTemporaryClipRects = 1 << 2, - PaintLayerPaintingReflection = 1 << 3 + PaintLayerPaintingReflection = 1 << 3, + PaintLayerPaintingOverlayScrollbars = 1 << 4 }; typedef unsigned PaintLayerFlags; @@ -573,6 +577,10 @@ private: virtual IntSize contentsSize() const; virtual int visibleHeight() const; virtual int visibleWidth() const; + virtual IntPoint currentMousePosition() const; + virtual bool shouldSuspendScrollAnimations() const; + + virtual void disconnectFromPage() { m_page = 0; } // NOTE: This should only be called by the overriden setScrollOffset from ScrollableArea. void scrollTo(int x, int y); @@ -666,18 +674,6 @@ protected: int m_scrollX; int m_scrollY; - // There are 8 possible combinations of writing mode and direction. Scroll origin (and its corresponding left/top overflow) - // will be non-zero in the x or y axis if there is any reversed direction or writing-mode. The combinations are: - // writing-mode / direction scrollOrigin.x() set scrollOrigin.y() set - // horizontal-tb / ltr NO NO - // horizontal-tb / rtl YES NO - // horizontal-bt / ltr NO YES - // horizontal-bt / rtl YES YES - // vertical-lr / ltr NO NO - // vertical-lr / rtl NO YES - // vertical-rl / ltr YES NO - // vertical-rl / rtl YES YES - IntPoint m_scrollOrigin; int m_scrollLeftOverflow; int m_scrollTopOverflow; @@ -741,6 +737,10 @@ protected: bool m_hasOverflowScroll : 1; #endif + bool m_containsDirtyOverlayScrollbars : 1; + + IntPoint m_cachedOverlayScrollbarOffset; + RenderMarquee* m_marquee; // Used by layers with overflow:marquee // Cached normal flow values for absolute positioned elements with static left/top values. @@ -762,6 +762,8 @@ private: #if USE(ACCELERATED_COMPOSITING) OwnPtr<RenderLayerBacking> m_backing; #endif + + Page* m_page; }; } // namespace WebCore diff --git a/Source/WebCore/rendering/RenderLayerBacking.cpp b/Source/WebCore/rendering/RenderLayerBacking.cpp index bda34ee..ec3b6fd 100644 --- a/Source/WebCore/rendering/RenderLayerBacking.cpp +++ b/Source/WebCore/rendering/RenderLayerBacking.cpp @@ -380,6 +380,7 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() IntSize oldOffsetFromRenderer = m_graphicsLayer->offsetFromRenderer(); m_graphicsLayer->setOffsetFromRenderer(localCompositingBounds.location() - IntPoint()); + // If the compositing layer offset changes, we need to repaint. if (oldOffsetFromRenderer != m_graphicsLayer->offsetFromRenderer()) m_graphicsLayer->setNeedsDisplay(); @@ -449,10 +450,9 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() FloatPoint foregroundPosition; FloatSize foregroundSize = newSize; IntSize foregroundOffset = m_graphicsLayer->offsetFromRenderer(); - // If we have a clipping layer (which clips descendants), then the foreground layer is a child of it, - // so that it gets correctly sorted with children. In that case, position relative to the clipping layer. if (m_clippingLayer) { - foregroundPosition = FloatPoint() + (localCompositingBounds.location() - clippingBox.location()); + // If we have a clipping layer (which clips descendants), then the foreground layer is a child of it, + // so that it gets correctly sorted with children. In that case, position relative to the clipping layer. foregroundSize = FloatSize(clippingBox.size()); foregroundOffset = clippingBox.location() - IntPoint(); } @@ -930,18 +930,6 @@ IntRect RenderLayerBacking::contentsBox() const return contentsRect; } -// Map the given point from coordinates in the GraphicsLayer to RenderLayer coordinates. -FloatPoint RenderLayerBacking::graphicsLayerToContentsCoordinates(const GraphicsLayer* graphicsLayer, const FloatPoint& point) -{ - return point + FloatSize(graphicsLayer->offsetFromRenderer()); -} - -// Map the given point from coordinates in the RenderLayer to GraphicsLayer coordinates. -FloatPoint RenderLayerBacking::contentsToGraphicsLayerCoordinates(const GraphicsLayer* graphicsLayer, const FloatPoint& point) -{ - return point - FloatSize(graphicsLayer->offsetFromRenderer()); -} - bool RenderLayerBacking::paintingGoesToWindow() const { if (m_owningLayer->isRootLayer()) @@ -966,21 +954,21 @@ void RenderLayerBacking::setContentsNeedDisplay() void RenderLayerBacking::setContentsNeedDisplayInRect(const IntRect& r) { if (m_graphicsLayer && m_graphicsLayer->drawsContent()) { - FloatPoint dirtyOrigin = contentsToGraphicsLayerCoordinates(m_graphicsLayer.get(), FloatPoint(r.x(), r.y())); - FloatRect dirtyRect(dirtyOrigin, r.size()); - FloatRect bounds(FloatPoint(), m_graphicsLayer->size()); - if (bounds.intersects(dirtyRect)) - m_graphicsLayer->setNeedsDisplayInRect(dirtyRect); + IntRect layerDirtyRect = r; + layerDirtyRect.move(-m_graphicsLayer->offsetFromRenderer()); + m_graphicsLayer->setNeedsDisplayInRect(layerDirtyRect); } if (m_foregroundLayer && m_foregroundLayer->drawsContent()) { - // FIXME: do incremental repaint - m_foregroundLayer->setNeedsDisplay(); + IntRect layerDirtyRect = r; + layerDirtyRect.move(-m_foregroundLayer->offsetFromRenderer()); + m_foregroundLayer->setNeedsDisplayInRect(layerDirtyRect); } if (m_maskLayer && m_maskLayer->drawsContent()) { - // FIXME: do incremental repaint - m_maskLayer->setNeedsDisplay(); + IntRect layerDirtyRect = r; + layerDirtyRect.move(-m_maskLayer->offsetFromRenderer()); + m_maskLayer->setNeedsDisplayInRect(layerDirtyRect); } } @@ -1123,10 +1111,11 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* } // Up-call from compositing layer drawing callback. -void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase paintingPhase, const IntRect& clip) +void RenderLayerBacking::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& context, GraphicsLayerPaintingPhase paintingPhase, const IntRect& clip) { InspectorInstrumentationCookie cookie = InspectorInstrumentation::willPaint(m_owningLayer->renderer()->frame(), clip); +<<<<<<< HEAD // We have to use the same root as for hit testing, because both methods // can compute and cache clipRects. IntRect enclosingBBox = compositedBounds(); @@ -1136,19 +1125,19 @@ void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& co if (m_owningLayer->hasOverflowParent()) enclosingBBox.setSize(clip.size()); #endif +======= + IntSize offset = graphicsLayer->offsetFromRenderer(); + context.translate(-offset); +>>>>>>> webkit.org at r82507 IntRect clipRect(clip); - - // Set up the coordinate space to be in the layer's rendering coordinates. - context.translate(-enclosingBBox.x(), -enclosingBBox.y()); - - // Offset the clip. - clipRect.move(enclosingBBox.x(), enclosingBBox.y()); + clipRect.move(offset); // The dirtyRect is in the coords of the painting root. - IntRect dirtyRect = enclosingBBox; + IntRect dirtyRect = compositedBounds(); dirtyRect.intersect(clipRect); + // We have to use the same root as for hit testing, because both methods can compute and cache clipRects. paintIntoLayer(m_owningLayer, &context, dirtyRect, PaintBehaviorNormal, paintingPhase, renderer()); InspectorInstrumentation::didPaint(cookie); diff --git a/Source/WebCore/rendering/RenderLayerBacking.h b/Source/WebCore/rendering/RenderLayerBacking.h index 726b777..8f0927d 100644 --- a/Source/WebCore/rendering/RenderLayerBacking.h +++ b/Source/WebCore/rendering/RenderLayerBacking.h @@ -123,9 +123,6 @@ public: void updateAfterWidgetResize(); - FloatPoint graphicsLayerToContentsCoordinates(const GraphicsLayer*, const FloatPoint&); - FloatPoint contentsToGraphicsLayerCoordinates(const GraphicsLayer*, const FloatPoint&); - // GraphicsLayerClient interface virtual void notifyAnimationStarted(const GraphicsLayer*, double startTime); virtual void notifySyncRequired(const GraphicsLayer*); diff --git a/Source/WebCore/rendering/RenderLayerCompositor.cpp b/Source/WebCore/rendering/RenderLayerCompositor.cpp index 4ab274f..6542ffd 100644 --- a/Source/WebCore/rendering/RenderLayerCompositor.cpp +++ b/Source/WebCore/rendering/RenderLayerCompositor.cpp @@ -109,11 +109,22 @@ RenderLayerCompositor::RenderLayerCompositor(RenderView* renderView) , m_compositing(false) , m_compositingLayersNeedRebuild(false) , m_flushingLayers(false) + , m_forceCompositingMode(false) , m_rootLayerAttachment(RootLayerUnattached) #if PROFILE_LAYER_REBUILD , m_rootLayerUpdateCount(0) #endif // PROFILE_LAYER_REBUILD { + Settings* settings = m_renderView->document()->settings(); + + // Even when forcing compositing mode, ignore child frames, or this will trigger + // layer creation from the enclosing RenderIFrame. + ASSERT(m_renderView->document()->frame()); + if (settings && settings->forceCompositingMode() && settings->acceleratedCompositingEnabled() + && !m_renderView->document()->frame()->tree()->parent()) { + m_forceCompositingMode = true; + enableCompositingMode(); + } } RenderLayerCompositor::~RenderLayerCompositor() @@ -439,7 +450,7 @@ IntRect RenderLayerCompositor::calculateCompositedBounds(const RenderLayer* laye if (layer->renderer()->isRoot()) { // If the root layer becomes composited (e.g. because some descendant with negative z-index is composited), // then it has to be big enough to cover the viewport in order to display the background. This is akin - // to the code in RenderBox::paintRootBoxDecorations(). + // to the code in RenderBox::paintRootBoxFillLayers(). if (m_renderView->frameView()) { int rw = m_renderView->frameView()->contentsWidth(); int rh = m_renderView->frameView()->contentsHeight(); @@ -770,7 +781,7 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O // If we're back at the root, and no other layers need to be composited, and the root layer itself doesn't need // to be composited, then we can drop out of compositing mode altogether. - if (layer->isRootLayer() && !childState.m_subtreeIsCompositing && !requiresCompositingLayer(layer)) { + if (layer->isRootLayer() && !childState.m_subtreeIsCompositing && !requiresCompositingLayer(layer) && !m_forceCompositingMode) { enableCompositingMode(false); willBeComposited = false; } @@ -939,7 +950,7 @@ void RenderLayerCompositor::frameViewDidScroll(const IntPoint& scrollPosition) m_scrollLayer->setPosition(FloatPoint(-scrollPosition.x(), -scrollPosition.y())); } -String RenderLayerCompositor::layerTreeAsText() +String RenderLayerCompositor::layerTreeAsText(bool showDebugInfo) { if (compositingLayerUpdatePending()) updateCompositingLayers(); @@ -949,7 +960,7 @@ String RenderLayerCompositor::layerTreeAsText() // We skip dumping the scroll and clip layers to keep layerTreeAsText output // similar between platforms. - return m_rootPlatformLayer->layerTreeAsText(); + return m_rootPlatformLayer->layerTreeAsText(showDebugInfo ? LayerTreeAsTextDebug : LayerTreeAsTextBehaviorNormal); } RenderLayerCompositor* RenderLayerCompositor::iframeContentsCompositor(RenderIFrame* renderer) diff --git a/Source/WebCore/rendering/RenderLayerCompositor.h b/Source/WebCore/rendering/RenderLayerCompositor.h index 1cf9ea9..c3deb3f 100644 --- a/Source/WebCore/rendering/RenderLayerCompositor.h +++ b/Source/WebCore/rendering/RenderLayerCompositor.h @@ -175,7 +175,7 @@ public: void frameViewDidChangeSize(const IntPoint& contentsOffset = IntPoint()); void frameViewDidScroll(const IntPoint& = IntPoint()); - String layerTreeAsText(); + String layerTreeAsText(bool showDebugInfo = false); // These are named to avoid conflicts with the functions in GraphicsLayerClient // These return the actual internal variables. @@ -277,6 +277,7 @@ private: bool m_compositing; bool m_compositingLayersNeedRebuild; bool m_flushingLayers; + bool m_forceCompositingMode; RootLayerAttachment m_rootLayerAttachment; diff --git a/Source/WebCore/rendering/RenderListBox.cpp b/Source/WebCore/rendering/RenderListBox.cpp index 4457285..ab3a832 100644 --- a/Source/WebCore/rendering/RenderListBox.cpp +++ b/Source/WebCore/rendering/RenderListBox.cpp @@ -82,11 +82,17 @@ RenderListBox::RenderListBox(Element* element) , m_optionsWidth(0) , m_indexOffset(0) { + if (Page* page = frame()->page()) { + m_page = page; + m_page->addScrollableArea(this); + } } RenderListBox::~RenderListBox() { setHasVerticalScrollbar(false); + if (m_page) + m_page->removeScrollableArea(this); } void RenderListBox::updateFromElement() @@ -316,6 +322,25 @@ void RenderListBox::paintScrollbar(PaintInfo& paintInfo, int tx, int ty) } } +static IntSize itemOffsetForAlignment(TextRun textRun, RenderStyle* itemStyle, Font itemFont, IntRect itemBoudingBox) +{ + ETextAlign actualAlignment = itemStyle->textAlign(); + // FIXME: Firefox doesn't respect JUSTIFY. Should we? + if (actualAlignment == TAAUTO || actualAlignment == JUSTIFY) + actualAlignment = itemStyle->isLeftToRightDirection() ? LEFT : RIGHT; + + IntSize offset = IntSize(0, itemFont.fontMetrics().ascent()); + if (actualAlignment == RIGHT || actualAlignment == WEBKIT_RIGHT) { + float textWidth = itemFont.width(textRun); + offset.setWidth(itemBoudingBox.width() - textWidth - optionsSpacingHorizontal); + } else if (actualAlignment == CENTER || actualAlignment == WEBKIT_CENTER) { + float textWidth = itemFont.width(textRun); + offset.setWidth((itemBoudingBox.width() - textWidth) / 2); + } else + offset.setWidth(optionsSpacingHorizontal); + return offset; +} + void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, int listIndex) { SelectElement* select = toSelectElement(static_cast<Element*>(node())); @@ -323,19 +348,18 @@ void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, in Element* element = listItems[listIndex]; OptionElement* optionElement = toOptionElement(element); + RenderStyle* itemStyle = element->renderStyle(); + if (!itemStyle) + itemStyle = style(); + + if (itemStyle->visibility() == HIDDEN) + return; + String itemText; if (optionElement) itemText = optionElement->textIndentedToRespectGroupLabel(); else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) - itemText = optionGroupElement->groupLabelText(); - - // Determine where the item text should be placed - IntRect r = itemBoundingBoxRect(tx, ty, listIndex); - r.move(optionsSpacingHorizontal, style()->fontMetrics().ascent()); - - RenderStyle* itemStyle = element->renderStyle(); - if (!itemStyle) - itemStyle = style(); + itemText = optionGroupElement->groupLabelText(); Color textColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor); if (optionElement && optionElement->selected()) { @@ -349,7 +373,13 @@ void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, in ColorSpace colorSpace = itemStyle->colorSpace(); paintInfo.context->setFillColor(textColor, colorSpace); + unsigned length = itemText.length(); + const UChar* string = itemText.characters(); + TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, !itemStyle->isLeftToRightDirection(), itemStyle->unicodeBidi() == Override); Font itemFont = style()->font(); + IntRect r = itemBoundingBoxRect(tx, ty, listIndex); + r.move(itemOffsetForAlignment(textRun, itemStyle, itemFont, r)); + if (isOptionGroupElement(element)) { FontDescription d = itemFont.fontDescription(); d.setWeight(d.bolderWeight()); @@ -357,10 +387,6 @@ void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, in itemFont.update(document()->styleSelector()->fontSelector()); } - unsigned length = itemText.length(); - const UChar* string = itemText.characters(); - TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, !itemStyle->isLeftToRightDirection(), itemStyle->unicodeBidi() == Override); - // Draw the item text if (itemStyle->visibility() != HIDDEN) paintInfo.context->drawBidiText(itemFont, textRun, r.location()); @@ -747,6 +773,14 @@ IntPoint RenderListBox::currentMousePosition() const return view->frameView()->currentMousePosition(); } +bool RenderListBox::shouldSuspendScrollAnimations() const +{ + RenderView* view = this->view(); + if (!view) + return true; + return view->frameView()->shouldSuspendScrollAnimations(); +} + PassRefPtr<Scrollbar> RenderListBox::createScrollbar() { RefPtr<Scrollbar> widget; diff --git a/Source/WebCore/rendering/RenderListBox.h b/Source/WebCore/rendering/RenderListBox.h index faeede1..0e2cfd6 100644 --- a/Source/WebCore/rendering/RenderListBox.h +++ b/Source/WebCore/rendering/RenderListBox.h @@ -112,6 +112,9 @@ private: virtual int visibleHeight() const; virtual int visibleWidth() const; virtual IntPoint currentMousePosition() const; + virtual bool shouldSuspendScrollAnimations() const; + + virtual void disconnectFromPage() { m_page = 0; } // NOTE: This should only be called by the overriden setScrollOffset from ScrollableArea. void scrollTo(int newOffset); @@ -138,6 +141,8 @@ private: int m_indexOffset; RefPtr<Scrollbar> m_vBar; + + Page* m_page; }; inline RenderListBox* toRenderListBox(RenderObject* object) diff --git a/Source/WebCore/rendering/RenderMedia.cpp b/Source/WebCore/rendering/RenderMedia.cpp index bbb5880..69d71ec 100644 --- a/Source/WebCore/rendering/RenderMedia.cpp +++ b/Source/WebCore/rendering/RenderMedia.cpp @@ -89,8 +89,6 @@ void RenderMedia::layout() // and this method will be called many times per second during playback, use a LayoutStateMaintainer: LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - m_controls->updateTimeDisplayVisibility(); - controlsRenderer->setLocation(borderLeft() + paddingLeft(), borderTop() + paddingTop()); controlsRenderer->style()->setHeight(Length(newSize.height(), Fixed)); controlsRenderer->style()->setWidth(Length(newSize.width(), Fixed)); diff --git a/Source/WebCore/rendering/RenderMenuList.cpp b/Source/WebCore/rendering/RenderMenuList.cpp index 8c11959..f155614 100644 --- a/Source/WebCore/rendering/RenderMenuList.cpp +++ b/Source/WebCore/rendering/RenderMenuList.cpp @@ -110,6 +110,7 @@ void RenderMenuList::addChild(RenderObject* newChild, RenderObject* beforeChild) { createInnerBlock(); m_innerBlock->addChild(newChild, beforeChild); + ASSERT(m_innerBlock == firstChild()); } void RenderMenuList::removeChild(RenderObject* oldChild) diff --git a/Source/WebCore/rendering/RenderObject.cpp b/Source/WebCore/rendering/RenderObject.cpp index d06db14..7cc6f1f 100644 --- a/Source/WebCore/rendering/RenderObject.cpp +++ b/Source/WebCore/rendering/RenderObject.cpp @@ -194,6 +194,7 @@ RenderObject::RenderObject(Node* node) , m_needsPositionedMovementLayout(false) , m_normalChildNeedsLayout(false) , m_posChildNeedsLayout(false) + , m_needsSimplifiedNormalFlowLayout(false) , m_preferredLogicalWidthsDirty(false) , m_floating(false) , m_positioned(false) @@ -204,6 +205,7 @@ RenderObject::RenderObject(Node* node) , m_isBox(false) , m_inline(true) , m_replaced(false) + , m_horizontalWritingMode(true) , m_isDragging(false) , m_hasLayer(false) , m_hasOverflowClip(false) @@ -218,7 +220,6 @@ RenderObject::RenderObject(Node* node) , m_hasMarkupTruncation(false) , m_selectionState(SelectionNone) , m_hasColumns(false) - , m_cellWidthChanged(false) { #ifndef NDEBUG renderObjectCounter.increment(); @@ -904,7 +905,7 @@ IntRect RenderObject::borderInnerRect(const IntRect& borderRect, unsigned short } #if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING) -void RenderObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, IntRect borderRect, Path borderPath, +void RenderObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, const IntRect& borderRect, const Path& borderPath, float thickness, float drawThickness, BoxSide s, const RenderStyle* style, Color c, EBorderStyle borderStyle) { @@ -1051,7 +1052,7 @@ void RenderObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, IntRect graphicsContext->drawRect(borderRect); } #else -void RenderObject::drawArcForBoxSide(GraphicsContext* graphicsContext, int x, int y, float thickness, IntSize radius, +void RenderObject::drawArcForBoxSide(GraphicsContext* graphicsContext, int x, int y, float thickness, const IntSize& radius, int angleStart, int angleSpan, BoxSide s, Color c, EBorderStyle style, bool firstCorner) { @@ -1689,9 +1690,12 @@ StyleDifference RenderObject::adjustStyleDifference(StyleDifference diff, unsign // Text nodes share style with their parents but transforms don't apply to them, // hence the !isText() check. // FIXME: when transforms are taken into account for overflow, we will need to do a layout. - if (!isText() && (!hasLayer() || !toRenderBoxModelObject(this)->layer()->isComposited())) - diff = StyleDifferenceLayout; - else if (diff < StyleDifferenceRecompositeLayer) + if (!isText() && (!hasLayer() || !toRenderBoxModelObject(this)->layer()->isComposited())) { + if (!hasLayer()) + diff = StyleDifferenceLayout; // FIXME: Do this for now since SimplifiedLayout cannot handle updating floating objects lists. + else if (diff < StyleDifferenceSimplifiedLayout) + diff = StyleDifferenceSimplifiedLayout; + } else if (diff < StyleDifferenceRecompositeLayer) diff = StyleDifferenceRecompositeLayer; } @@ -1770,6 +1774,8 @@ void RenderObject::setStyle(PassRefPtr<RenderStyle> style) setNeedsLayoutAndPrefWidthsRecalc(); else if (updatedDiff == StyleDifferenceLayoutPositionedMovementOnly) setNeedsPositionedMovementLayout(); + else if (updatedDiff == StyleDifferenceSimplifiedLayout) + setNeedsSimplifiedNormalFlowLayout(); } if (updatedDiff == StyleDifferenceRepaintLayer || updatedDiff == StyleDifferenceRepaint) { @@ -1835,6 +1841,7 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS m_positioned = false; m_relPositioned = false; } + m_horizontalWritingMode = true; m_paintBackground = false; m_hasOverflowClip = false; m_hasTransform = false; @@ -1882,7 +1889,7 @@ void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldSt if (!m_parent) return; - if (diff == StyleDifferenceLayout) { + if (diff == StyleDifferenceLayout || diff == StyleDifferenceSimplifiedLayout) { RenderCounter::rendererStyleChanged(this, oldStyle, m_style.get()); // If the object already needs layout, then setNeedsLayout won't do @@ -1893,7 +1900,10 @@ void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldSt if (m_needsLayout && oldStyle->position() != m_style->position()) markContainingBlocksForLayout(); - setNeedsLayoutAndPrefWidthsRecalc(); + if (diff == StyleDifferenceLayout) + setNeedsLayoutAndPrefWidthsRecalc(); + else + setNeedsSimplifiedNormalFlowLayout(); } else if (diff == StyleDifferenceLayoutPositionedMovementOnly) setNeedsPositionedMovementLayout(); @@ -1939,7 +1949,7 @@ IntRect RenderObject::viewRect() const return view()->viewRect(); } -FloatPoint RenderObject::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const +FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, bool fixed, bool useTransforms) const { TransformState transformState(TransformState::ApplyTransformDirection, localPoint); mapLocalToContainer(0, fixed, useTransforms, transformState); @@ -1948,7 +1958,7 @@ FloatPoint RenderObject::localToAbsolute(FloatPoint localPoint, bool fixed, bool return transformState.lastPlanarPoint(); } -FloatPoint RenderObject::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const +FloatPoint RenderObject::absoluteToLocal(const FloatPoint& containerPoint, bool fixed, bool useTransforms) const { TransformState transformState(TransformState::UnapplyInverseTransformDirection, containerPoint); mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); @@ -2613,11 +2623,11 @@ RenderBoxModelObject* RenderObject::offsetParent() const // * The computed value of the position property of A is static and the ancestor // is one of the following HTML elements: td, th, or table. // * Our own extension: if there is a difference in the effective zoom + bool skipTables = isPositioned() || isRelPositioned(); float currZoom = style()->effectiveZoom(); RenderObject* curr = parent(); - while (curr && (!curr->node() || - (!curr->isPositioned() && !curr->isRelPositioned() && !curr->isBody()))) { + while (curr && (!curr->node() || (!curr->isPositioned() && !curr->isRelPositioned() && !curr->isBody()))) { Node* element = curr->node(); if (!skipTables && element) { bool isTableElement = element->hasTagName(tableTag) || @@ -2647,14 +2657,14 @@ VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affini { // If this is a non-anonymous renderer in an editable area, then it's simple. if (Node* node = this->node()) { - if (!node->isContentEditable()) { + if (!node->rendererIsEditable()) { // If it can be found, we prefer a visually equivalent position that is editable. Position position(node, offset); Position candidate = position.downstream(CanCrossEditingBoundary); - if (candidate.deprecatedNode()->isContentEditable()) + if (candidate.deprecatedNode()->rendererIsEditable()) return VisiblePosition(candidate, affinity); candidate = position.upstream(CanCrossEditingBoundary); - if (candidate.deprecatedNode()->isContentEditable()) + if (candidate.deprecatedNode()->rendererIsEditable()) return VisiblePosition(candidate, affinity); } // FIXME: Eliminate legacy editing positions diff --git a/Source/WebCore/rendering/RenderObject.h b/Source/WebCore/rendering/RenderObject.h index 8b9fa38..41564dd 100644 --- a/Source/WebCore/rendering/RenderObject.h +++ b/Source/WebCore/rendering/RenderObject.h @@ -320,8 +320,6 @@ public: void setChildrenInline(bool b = true) { m_childrenInline = b; } bool hasColumns() const { return m_hasColumns; } void setHasColumns(bool b = true) { m_hasColumns = b; } - bool cellWidthChanged() const { return m_cellWidthChanged; } - void setCellWidthChanged(bool b = true) { m_cellWidthChanged = b; } virtual bool requiresForcedStyleRecalcPropagation() const { return false; } @@ -388,7 +386,11 @@ public: void setIsAnonymous(bool b) { m_isAnonymous = b; } bool isAnonymousBlock() const { - return m_isAnonymous && style()->display() == BLOCK && style()->styleType() == NOPSEUDO && !isListMarker(); + // This function is kept in sync with anonymous block creation conditions in + // RenderBlock::createAnonymousBlock(). This includes creating an anonymous + // RenderBlock having a BLOCK or BOX display. Other classes such as RenderTextFragment + // are not RenderBlocks and will return false. See https://bugs.webkit.org/show_bug.cgi?id=56709. + return m_isAnonymous && (style()->display() == BLOCK || style()->display() == BOX) && style()->styleType() == NOPSEUDO && isRenderBlock() && !isListMarker(); } bool isAnonymousColumnsBlock() const { return style()->specifiesColumns() && isAnonymousBlock(); } bool isAnonymousColumnSpanBlock() const { return style()->columnSpan() && isAnonymousBlock(); } @@ -406,17 +408,19 @@ public: bool isRunIn() const { return style()->display() == RUN_IN; } // run-in object bool isDragging() const { return m_isDragging; } bool isReplaced() const { return m_replaced; } // a "replaced" element (see CSS) - + bool isHorizontalWritingMode() const { return m_horizontalWritingMode; } + bool hasLayer() const { return m_hasLayer; } bool hasBoxDecorations() const { return m_paintBackground; } bool mustRepaintBackgroundOrBorder() const; bool hasBackground() const { return style()->hasBackground(); } - bool needsLayout() const { return m_needsLayout || m_normalChildNeedsLayout || m_posChildNeedsLayout || m_needsPositionedMovementLayout; } + bool needsLayout() const { return m_needsLayout || m_normalChildNeedsLayout || m_posChildNeedsLayout || m_needsSimplifiedNormalFlowLayout || m_needsPositionedMovementLayout; } bool selfNeedsLayout() const { return m_needsLayout; } bool needsPositionedMovementLayout() const { return m_needsPositionedMovementLayout; } - bool needsPositionedMovementLayoutOnly() const { return m_needsPositionedMovementLayout && !m_needsLayout && !m_normalChildNeedsLayout && !m_posChildNeedsLayout; } + bool needsPositionedMovementLayoutOnly() const { return m_needsPositionedMovementLayout && !m_needsLayout && !m_normalChildNeedsLayout && !m_posChildNeedsLayout && !m_needsSimplifiedNormalFlowLayout; } bool posChildNeedsLayout() const { return m_posChildNeedsLayout; } + bool needsSimplifiedNormalFlowLayout() const { return m_needsSimplifiedNormalFlowLayout; } bool normalChildNeedsLayout() const { return m_normalChildNeedsLayout; } bool preferredLogicalWidthsDirty() const { return m_preferredLogicalWidthsDirty; } @@ -432,13 +436,13 @@ public: void drawLineForBoxSide(GraphicsContext*, int x1, int y1, int x2, int y2, BoxSide, Color, EBorderStyle, int adjbw1, int adjbw2); #if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING) - void drawBoxSideFromPath(GraphicsContext*, IntRect, Path, + void drawBoxSideFromPath(GraphicsContext*, const IntRect&, const Path&, float thickness, float drawThickness, BoxSide, const RenderStyle*, Color, EBorderStyle); #else // FIXME: This function should be removed when all ports implement GraphicsContext::clipConvexPolygon()!! // At that time, everyone can use RenderObject::drawBoxSideFromPath() instead. This should happen soon. - void drawArcForBoxSide(GraphicsContext*, int x, int y, float thickness, IntSize radius, int angleStart, + void drawArcForBoxSide(GraphicsContext*, int x, int y, float thickness, const IntSize& radius, int angleStart, int angleSpan, BoxSide, Color, EBorderStyle, bool firstCorner); #endif @@ -485,6 +489,7 @@ public: void setNeedsLayout(bool b, bool markParents = true); void setChildNeedsLayout(bool b, bool markParents = true); void setNeedsPositionedMovementLayout(); + void setNeedsSimplifiedNormalFlowLayout(); void setPreferredLogicalWidthsDirty(bool, bool markParents = true); void invalidateContainerPreferredLogicalWidths(); @@ -502,6 +507,7 @@ public: void setIsText() { m_isText = true; } void setIsBox() { m_isBox = true; } void setReplaced(bool b = true) { m_replaced = b; } + void setHorizontalWritingMode(bool b = true) { m_horizontalWritingMode = b; } void setHasOverflowClip(bool b = true) { m_hasOverflowClip = b; } void setHasLayer(bool b = true) { m_hasLayer = b; } void setHasTransform(bool b = true) { m_hasTransform = b; } @@ -557,8 +563,8 @@ public: // Convert the given local point to absolute coordinates // FIXME: Temporary. If useTransforms is true, take transforms into account. Eventually localToAbsolute() will always be transform-aware. - FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; - FloatPoint absoluteToLocal(FloatPoint, bool fixed = false, bool useTransforms = false) const; + FloatPoint localToAbsolute(const FloatPoint& localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; + FloatPoint absoluteToLocal(const FloatPoint&, bool fixed = false, bool useTransforms = false) const; // Convert a local quad to absolute coordinates, taking transforms into account. FloatQuad localToAbsoluteQuad(const FloatQuad& quad, bool fixed = false) const @@ -730,6 +736,8 @@ public: virtual bool isFlexingChildren() const { return false; } virtual bool isStretchingChildren() const { return false; } + virtual bool isCombineText() const { return false; } + virtual int caretMinOffset() const; virtual int caretMaxOffset() const; virtual unsigned caretMaxRenderedOffset() const; @@ -840,6 +848,7 @@ private: bool m_needsPositionedMovementLayout :1; bool m_normalChildNeedsLayout : 1; bool m_posChildNeedsLayout : 1; + bool m_needsSimplifiedNormalFlowLayout : 1; bool m_preferredLogicalWidthsDirty : 1; bool m_floating : 1; @@ -853,6 +862,7 @@ private: bool m_isBox : 1; bool m_inline : 1; bool m_replaced : 1; + bool m_horizontalWritingMode : 1; bool m_isDragging : 1; bool m_hasLayer : 1; @@ -924,6 +934,7 @@ inline void RenderObject::setNeedsLayout(bool b, bool markParents) } else { m_everHadLayout = true; m_posChildNeedsLayout = false; + m_needsSimplifiedNormalFlowLayout = false; m_normalChildNeedsLayout = false; m_needsPositionedMovementLayout = false; } @@ -939,6 +950,7 @@ inline void RenderObject::setChildNeedsLayout(bool b, bool markParents) markContainingBlocksForLayout(); } else { m_posChildNeedsLayout = false; + m_needsSimplifiedNormalFlowLayout = false; m_normalChildNeedsLayout = false; m_needsPositionedMovementLayout = false; } @@ -955,6 +967,17 @@ inline void RenderObject::setNeedsPositionedMovementLayout() } } +inline void RenderObject::setNeedsSimplifiedNormalFlowLayout() +{ + bool alreadyNeededLayout = needsLayout(); + m_needsSimplifiedNormalFlowLayout = true; + if (!alreadyNeededLayout) { + markContainingBlocksForLayout(); + if (hasLayer()) + setLayerNeedsFullRepaint(); + } +} + inline bool objectIsRelayoutBoundary(const RenderObject *obj) { // FIXME: In future it may be possible to broaden this condition in order to improve performance. @@ -975,6 +998,8 @@ inline void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, R RenderObject* o = container(); RenderObject* last = this; + bool simplifiedNormalFlowLayout = needsSimplifiedNormalFlowLayout() && !selfNeedsLayout() && !normalChildNeedsLayout(); + while (o) { // Don't mark the outermost object of an unrooted subtree. That object will be // marked when the subtree is added to the document. @@ -982,17 +1007,15 @@ inline void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, R if (!container && !o->isRenderView()) return; if (!last->isText() && (last->style()->position() == FixedPosition || last->style()->position() == AbsolutePosition)) { - if (last->style()->top().isAuto() && last->style()->bottom().isAuto()) { - RenderObject* parent = last->parent(); - if (!parent->normalChildNeedsLayout()) { - parent->setChildNeedsLayout(true, false); - if (parent != newRoot) - parent->markContainingBlocksForLayout(scheduleRelayout, newRoot); - } - } if (o->m_posChildNeedsLayout) return; o->m_posChildNeedsLayout = true; + simplifiedNormalFlowLayout = true; + ASSERT(!o->isSetNeedsLayoutForbidden()); + } else if (simplifiedNormalFlowLayout) { + if (o->m_needsSimplifiedNormalFlowLayout) + return; + o->m_needsSimplifiedNormalFlowLayout = true; ASSERT(!o->isSetNeedsLayoutForbidden()); } else { if (o->m_normalChildNeedsLayout) diff --git a/Source/WebCore/rendering/RenderObjectChildList.cpp b/Source/WebCore/rendering/RenderObjectChildList.cpp index 4df7180..617067a 100644 --- a/Source/WebCore/rendering/RenderObjectChildList.cpp +++ b/Source/WebCore/rendering/RenderObjectChildList.cpp @@ -262,31 +262,6 @@ static RenderObject* findBeforeAfterParent(RenderObject* object) return beforeAfterParent; } -static void invalidateCountersInContainer(RenderObject* container, const AtomicString& identifier) -{ - if (!container) - return; - container = findBeforeAfterParent(container); - if (!container) - return; - // Sometimes the counter is attached directly on the container. - if (container->isCounter()) { - toRenderCounter(container)->invalidate(identifier); - return; - } - for (RenderObject* content = container->firstChild(); content; content = content->nextSibling()) { - if (content->isCounter()) - toRenderCounter(content)->invalidate(identifier); - } -} - -void RenderObjectChildList::invalidateCounters(const RenderObject* owner, const AtomicString& identifier) -{ - ASSERT(!owner->documentBeingDestroyed()); - invalidateCountersInContainer(beforePseudoElementRenderer(owner), identifier); - invalidateCountersInContainer(afterPseudoElementRenderer(owner), identifier); -} - RenderObject* RenderObjectChildList::beforePseudoElementRenderer(const RenderObject* owner) const { // An anonymous (generated) inline run-in that has PseudoId BEFORE must come from a grandparent. diff --git a/Source/WebCore/rendering/RenderObjectChildList.h b/Source/WebCore/rendering/RenderObjectChildList.h index 087adfb..c8fc978 100644 --- a/Source/WebCore/rendering/RenderObjectChildList.h +++ b/Source/WebCore/rendering/RenderObjectChildList.h @@ -58,7 +58,6 @@ public: void updateBeforeAfterContent(RenderObject* owner, PseudoId type, const RenderObject* styledObject = 0); RenderObject* beforePseudoElementRenderer(const RenderObject* owner) const; RenderObject* afterPseudoElementRenderer(const RenderObject* owner) const; - void invalidateCounters(const RenderObject* owner, const AtomicString& identifier); private: RenderObject* m_firstChild; diff --git a/Source/WebCore/rendering/RenderRubyRun.cpp b/Source/WebCore/rendering/RenderRubyRun.cpp index 016c2a1..3504ccf 100644 --- a/Source/WebCore/rendering/RenderRubyRun.cpp +++ b/Source/WebCore/rendering/RenderRubyRun.cpp @@ -194,8 +194,7 @@ void RenderRubyRun::removeChild(RenderObject* child) RenderRubyBase* RenderRubyRun::createRubyBase() const { RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */); - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(style()); + RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style()); newStyle->setDisplay(BLOCK); newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER? rb->setStyle(newStyle.release()); @@ -206,8 +205,7 @@ RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby { ASSERT(parentRuby && parentRuby->isRuby()); RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */); - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(parentRuby->style()); + RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(parentRuby->style()); newStyle->setDisplay(INLINE_BLOCK); rr->setStyle(newStyle.release()); return rr; diff --git a/Source/WebCore/rendering/RenderSlider.cpp b/Source/WebCore/rendering/RenderSlider.cpp index 49da396..1661b7a 100644 --- a/Source/WebCore/rendering/RenderSlider.cpp +++ b/Source/WebCore/rendering/RenderSlider.cpp @@ -156,7 +156,7 @@ void RenderSlider::layout() if (oldSize != size()) thumb->setChildNeedsLayout(true, false); - LayoutStateMaintainer statePusher(view(), this, size(), style()->isFlippedBlocksWritingMode()); + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode()); IntRect oldThumbRect = thumb->frameRect(); diff --git a/Source/WebCore/rendering/RenderSummary.cpp b/Source/WebCore/rendering/RenderSummary.cpp index 8fccf66..cfde231 100644 --- a/Source/WebCore/rendering/RenderSummary.cpp +++ b/Source/WebCore/rendering/RenderSummary.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,11 +21,41 @@ #include "config.h" #include "RenderSummary.h" +#include "RenderDetails.h" + namespace WebCore { -RenderSummary::RenderSummary(Node* element) - : RenderBlock(element) +RenderSummary::RenderSummary(Node* node) + : RenderBlock(node) +{ +} + +void RenderSummary::destroy() +{ + RenderDetails* details = parentDetails(); + if (details) + details->summaryDestroyed(this); + + RenderBlock::destroy(); +} + +RenderDetails* RenderSummary::parentDetails() { + RenderObject* obj = parent(); + while (obj && !obj->isDetails()) + obj = obj->parent(); + + return static_cast<RenderDetails*>(obj); +} + +void RenderSummary::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + + // Ensure that if we ended up being inline that we set our replaced flag + // so that we're treated like an inline-block. + setReplaced(isInline()); } } diff --git a/Source/WebCore/rendering/RenderSummary.h b/Source/WebCore/rendering/RenderSummary.h index afc18fa..94850df 100644 --- a/Source/WebCore/rendering/RenderSummary.h +++ b/Source/WebCore/rendering/RenderSummary.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,13 +25,20 @@ namespace WebCore { +class RenderDetails; + class RenderSummary : public RenderBlock { public: explicit RenderSummary(Node*); + virtual void destroy(); + private: virtual const char* renderName() const { return "RenderSummary"; } virtual bool isSummary() const { return true; } + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + RenderDetails* parentDetails(); }; inline RenderSummary* toRenderSummary(RenderObject* object) diff --git a/Source/WebCore/rendering/RenderTable.cpp b/Source/WebCore/rendering/RenderTable.cpp index 69c073c..187ffd7 100644 --- a/Source/WebCore/rendering/RenderTable.cpp +++ b/Source/WebCore/rendering/RenderTable.cpp @@ -277,7 +277,7 @@ void RenderTable::layout() { ASSERT(needsLayout()); - if (layoutOnlyPositionedObjects()) + if (simplifiedLayout()) return; recalcSectionsIfNeeded(); @@ -510,11 +510,14 @@ void RenderTable::paint(PaintInfo& paintInfo, int tx, int ty) PaintPhase paintPhase = paintInfo.phase; - int os = 2 * maximalOutlineSize(paintPhase); - if (ty + minYVisualOverflow() >= paintInfo.rect.maxY() + os || ty + maxYVisualOverflow() <= paintInfo.rect.y() - os) - return; - if (tx + minXVisualOverflow() >= paintInfo.rect.maxX() + os || tx + maxXVisualOverflow() <= paintInfo.rect.x() - os) - return; + if (!isRoot()) { + IntRect overflowBox = visualOverflowRect(); + flipForWritingMode(overflowBox); + overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); + overflowBox.move(tx, ty); + if (!overflowBox.intersects(paintInfo.rect)) + return; + } bool pushedClip = pushContentsClip(paintInfo, tx, ty); paintObject(paintInfo, tx, ty); @@ -609,7 +612,13 @@ void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) paintBoxShadow(paintInfo.context, rect.x(), rect.y(), rect.width(), rect.height(), style(), Normal); - paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), rect.x(), rect.y(), rect.width(), rect.height()); + if (isRoot()) + paintRootBoxFillLayers(paintInfo); + else if (!isBody() || document()->documentElement()->renderer()->hasBackground()) + // The <body> only paints its background if the root element has defined a background + // independent of the body. + paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), rect.x(), rect.y(), rect.width(), rect.height()); + paintBoxShadow(paintInfo.context, rect.x(), rect.y(), rect.width(), rect.height(), style(), Inset); if (style()->hasBorder() && !collapseBorders()) @@ -1199,9 +1208,9 @@ int RenderTable::firstLineBoxBaseline() const return firstNonEmptySection->logicalTop() + firstNonEmptySection->firstLineBoxBaseline(); } -IntRect RenderTable::overflowClipRect(int tx, int ty) +IntRect RenderTable::overflowClipRect(int tx, int ty, OverlayScrollbarSizeRelevancy relevancy) { - IntRect rect = RenderBlock::overflowClipRect(tx, ty); + IntRect rect = RenderBlock::overflowClipRect(tx, ty, relevancy); // If we have a caption, expand the clip to include the caption. // FIXME: Technically this is wrong, but it's virtually impossible to fix this diff --git a/Source/WebCore/rendering/RenderTable.h b/Source/WebCore/rendering/RenderTable.h index 67a311c..d781e95 100644 --- a/Source/WebCore/rendering/RenderTable.h +++ b/Source/WebCore/rendering/RenderTable.h @@ -236,7 +236,7 @@ private: virtual void computeLogicalWidth(); - virtual IntRect overflowClipRect(int tx, int ty); + virtual IntRect overflowClipRect(int tx, int ty, OverlayScrollbarSizeRelevancy relevancy = IgnoreOverlayScrollbarSize); virtual void addOverflowFromChildren(); diff --git a/Source/WebCore/rendering/RenderTableCell.cpp b/Source/WebCore/rendering/RenderTableCell.cpp index 2293bac..c9d8837 100644 --- a/Source/WebCore/rendering/RenderTableCell.cpp +++ b/Source/WebCore/rendering/RenderTableCell.cpp @@ -52,6 +52,7 @@ RenderTableCell::RenderTableCell(Node* node) , m_column(-1) , m_rowSpan(1) , m_columnSpan(1) + , m_cellWidthChanged(false) , m_intrinsicPaddingBefore(0) , m_intrinsicPaddingAfter(0) { @@ -174,7 +175,7 @@ void RenderTableCell::layout() int RenderTableCell::paddingTop(bool includeIntrinsicPadding) const { int result = RenderBlock::paddingTop(); - if (!includeIntrinsicPadding || !style()->isHorizontalWritingMode()) + if (!includeIntrinsicPadding || !isHorizontalWritingMode()) return result; return result + (style()->writingMode() == TopToBottomWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter()); } @@ -182,7 +183,7 @@ int RenderTableCell::paddingTop(bool includeIntrinsicPadding) const int RenderTableCell::paddingBottom(bool includeIntrinsicPadding) const { int result = RenderBlock::paddingBottom(); - if (!includeIntrinsicPadding || !style()->isHorizontalWritingMode()) + if (!includeIntrinsicPadding || !isHorizontalWritingMode()) return result; return result + (style()->writingMode() == TopToBottomWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore()); } @@ -190,7 +191,7 @@ int RenderTableCell::paddingBottom(bool includeIntrinsicPadding) const int RenderTableCell::paddingLeft(bool includeIntrinsicPadding) const { int result = RenderBlock::paddingLeft(); - if (!includeIntrinsicPadding || style()->isHorizontalWritingMode()) + if (!includeIntrinsicPadding || isHorizontalWritingMode()) return result; return result + (style()->writingMode() == LeftToRightWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter()); @@ -199,7 +200,7 @@ int RenderTableCell::paddingLeft(bool includeIntrinsicPadding) const int RenderTableCell::paddingRight(bool includeIntrinsicPadding) const { int result = RenderBlock::paddingRight(); - if (!includeIntrinsicPadding || style()->isHorizontalWritingMode()) + if (!includeIntrinsicPadding || isHorizontalWritingMode()) return result; return result + (style()->writingMode() == LeftToRightWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore()); } @@ -225,6 +226,12 @@ void RenderTableCell::setOverrideSize(int size) clearIntrinsicPadding(); RenderBlock::setOverrideSize(size); } + +void RenderTableCell::setOverrideSizeFromRowHeight(int rowHeight) +{ + clearIntrinsicPadding(); + RenderBlock::setOverrideSize(max(0, rowHeight - borderBefore() - paddingBefore() - borderAfter() - paddingAfter())); +} IntSize RenderTableCell::offsetFromContainer(RenderObject* o, const IntPoint& point) const { @@ -1049,8 +1056,8 @@ void RenderTableCell::scrollbarsChanged(bool horizontalScrollbarChanged, bool ve return; // Not sure if we should be doing something when a scrollbar goes away or not. // We only care if the scrollbar that affects our intrinsic padding has been added. - if ((style()->isHorizontalWritingMode() && !horizontalScrollbarChanged) || - (!style()->isHorizontalWritingMode() && !verticalScrollbarChanged)) + if ((isHorizontalWritingMode() && !horizontalScrollbarChanged) || + (!isHorizontalWritingMode() && !verticalScrollbarChanged)) return; // Shrink our intrinsic padding as much as possible to accommodate the scrollbar. diff --git a/Source/WebCore/rendering/RenderTableCell.h b/Source/WebCore/rendering/RenderTableCell.h index cfdb739..9884999 100644 --- a/Source/WebCore/rendering/RenderTableCell.h +++ b/Source/WebCore/rendering/RenderTableCell.h @@ -120,11 +120,15 @@ public: virtual int paddingAfter(bool includeIntrinsicPadding = true) const; virtual void setOverrideSize(int); + void setOverrideSizeFromRowHeight(int); bool hasVisualOverflow() const { return m_overflow && !borderBoxRect().contains(m_overflow->visualOverflowRect()); } virtual void scrollbarsChanged(bool horizontalScrollbarChanged, bool verticalScrollbarChanged); + bool cellWidthChanged() const { return m_cellWidthChanged; } + void setCellWidthChanged(bool b = true) { m_cellWidthChanged = b; } + protected: virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); @@ -136,8 +140,6 @@ private: virtual void destroy(); - virtual bool requiresLayer() const { return isPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasMask() || hasReflection(); } - virtual void computeLogicalWidth(); virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); @@ -152,7 +154,8 @@ private: int m_row; int m_column; int m_rowSpan; - int m_columnSpan; + int m_columnSpan : 31; + bool m_cellWidthChanged : 1; int m_intrinsicPaddingBefore; int m_intrinsicPaddingAfter; }; diff --git a/Source/WebCore/rendering/RenderTableRow.cpp b/Source/WebCore/rendering/RenderTableRow.cpp index 7300c19..a29f218 100644 --- a/Source/WebCore/rendering/RenderTableRow.cpp +++ b/Source/WebCore/rendering/RenderTableRow.cpp @@ -63,6 +63,17 @@ void RenderTableRow::styleWillChange(StyleDifference diff, const RenderStyle* ne RenderBox::styleWillChange(diff, newStyle); } +void RenderTableRow::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBox::styleDidChange(diff, oldStyle); + + // Update pseudos for :before and :after now. + if (!isAnonymous() && document()->usesBeforeAfterRules()) { + children()->updateBeforeAfterContent(this, BEFORE); + children()->updateBeforeAfterContent(this, AFTER); + } +} + void RenderTableRow::addChild(RenderObject* child, RenderObject* beforeChild) { // Make sure we don't append things after :after-generated content if we have it. diff --git a/Source/WebCore/rendering/RenderTableRow.h b/Source/WebCore/rendering/RenderTableRow.h index 20aa424..2af8dcb 100644 --- a/Source/WebCore/rendering/RenderTableRow.h +++ b/Source/WebCore/rendering/RenderTableRow.h @@ -62,6 +62,7 @@ private: virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); RenderObjectChildList m_children; }; diff --git a/Source/WebCore/rendering/RenderTableSection.cpp b/Source/WebCore/rendering/RenderTableSection.cpp index 3d46ece..7c0a768 100644 --- a/Source/WebCore/rendering/RenderTableSection.cpp +++ b/Source/WebCore/rendering/RenderTableSection.cpp @@ -203,7 +203,7 @@ void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row) switch (logicalHeight.type()) { case Percent: if (!(cRowLogicalHeight.isPercent()) || - (cRowLogicalHeight.isPercent() && cRowLogicalHeight.rawValue() < logicalHeight.rawValue())) + (cRowLogicalHeight.isPercent() && cRowLogicalHeight.percent() < logicalHeight.percent())) m_grid[m_cRow].logicalHeight = logicalHeight; break; case Fixed: @@ -521,22 +521,22 @@ int RenderTableSection::layoutRows(int toAdd) if (m_grid[r].logicalHeight.isAuto()) numAuto++; else if (m_grid[r].logicalHeight.isPercent()) - totalPercent += m_grid[r].logicalHeight.rawValue(); + totalPercent += m_grid[r].logicalHeight.percent(); } if (totalPercent) { // try to satisfy percent int add = 0; - totalPercent = min(totalPercent, 100 * percentScaleFactor); + totalPercent = min(totalPercent, 100); int rh = m_rowPos[1] - m_rowPos[0]; for (int r = 0; r < totalRows; r++) { if (totalPercent > 0 && m_grid[r].logicalHeight.isPercent()) { - int toAdd = min(dh, (totalHeight * m_grid[r].logicalHeight.rawValue() / (100 * percentScaleFactor)) - rh); + int toAdd = min(dh, static_cast<int>((totalHeight * m_grid[r].logicalHeight.percent() / 100) - rh)); // If toAdd is negative, then we don't want to shrink the row (this bug // affected Outlook Web Access). toAdd = max(0, toAdd); add += toAdd; dh -= toAdd; - totalPercent -= m_grid[r].logicalHeight.rawValue(); + totalPercent -= m_grid[r].logicalHeight.percent(); } if (r < totalRows - 1) rh = m_rowPos[r + 2] - m_rowPos[r + 1]; @@ -647,9 +647,7 @@ int RenderTableSection::layoutRows(int toAdd) // Alignment within a cell is based off the calculated // height, which becomes irrelevant once the cell has // been resized based off its percentage. - cell->setOverrideSize(max(0, - rHeight - cell->borderBefore() - cell->paddingBefore() - - cell->borderAfter() - cell->paddingAfter())); + cell->setOverrideSizeFromRowHeight(rHeight); cell->layoutIfNeeded(); // If the baseline moved, we may have to update the data for our row. Find out the new baseline. diff --git a/Source/WebCore/rendering/RenderText.cpp b/Source/WebCore/rendering/RenderText.cpp index 3e696d3..e660875 100644 --- a/Source/WebCore/rendering/RenderText.cpp +++ b/Source/WebCore/rendering/RenderText.cpp @@ -551,6 +551,12 @@ IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* e case CENTER: case WEBKIT_CENTER: break; + case TASTART: + rightAligned = !cbStyle->isLeftToRightDirection(); + break; + case TAEND: + rightAligned = cbStyle->isLeftToRightDirection(); + break; } if (rightAligned) { @@ -566,13 +572,13 @@ IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* e ALWAYS_INLINE float RenderText::widthFromCache(const Font& f, int start, int len, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const { - if (style()->hasTextCombine()) { + if (style()->hasTextCombine() && isCombineText()) { const RenderCombineText* combineText = toRenderCombineText(this); if (combineText->isCombined()) return combineText->combinedTextWidth(f); } - if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII) { + if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII && (!glyphOverflow || !glyphOverflow->computeBounds)) { float monospaceCharacterWidth = f.spaceWidth(); float tabWidth = allowTabs() ? monospaceCharacterWidth * 8 : 0; float w = 0; @@ -1247,7 +1253,7 @@ float RenderText::width(unsigned from, unsigned len, const Font& f, float xPos, float w; if (&f == &style()->font()) { - if (!style()->preserveNewline() && !from && len == textLength()) { + if (!style()->preserveNewline() && !from && len == textLength() && (!glyphOverflow || !glyphOverflow->computeBounds)) { if (fallbackFonts) { ASSERT(glyphOverflow); if (preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts) { @@ -1404,6 +1410,8 @@ int RenderText::previousOffset(int current) const return result; } +#if PLATFORM(MAC) + #define HANGUL_CHOSEONG_START (0x1100) #define HANGUL_CHOSEONG_END (0x115F) #define HANGUL_JUNGSEONG_START (0x1160) @@ -1428,6 +1436,14 @@ inline bool isHangulLVT(UChar32 character) return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT; } +inline bool isMark(UChar32 c) +{ + int8_t charType = u_charType(c); + return charType == U_NON_SPACING_MARK || charType == U_ENCLOSING_MARK || charType == U_COMBINING_SPACING_MARK; +} + +#endif + int RenderText::previousOffsetForBackwardDeletion(int current) const { #if PLATFORM(MAC) @@ -1446,7 +1462,7 @@ int RenderText::previousOffsetForBackwardDeletion(int current) const if ((character >= 0x0530) && (character < 0x1950)) break; - if (u_isbase(character) && (character != 0xFF9E) && (character != 0xFF9F)) + if (!isMark(character) && (character != 0xFF9E) && (character != 0xFF9F)) break; } diff --git a/Source/WebCore/rendering/RenderText.h b/Source/WebCore/rendering/RenderText.h index b88590c..7fa3518 100644 --- a/Source/WebCore/rendering/RenderText.h +++ b/Source/WebCore/rendering/RenderText.h @@ -125,6 +125,8 @@ public: virtual void computePreferredLogicalWidths(float leadWidth); bool isAllCollapsibleWhitespace(); + bool knownToHaveNoOverflowAndNoFallbackFonts() const { return m_knownToHaveNoOverflowAndNoFallbackFonts; } + protected: virtual void styleWillChange(StyleDifference, const RenderStyle*) { } virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); diff --git a/Source/WebCore/rendering/RenderTextControlSingleLine.cpp b/Source/WebCore/rendering/RenderTextControlSingleLine.cpp index 8b98335..b9697ff 100644 --- a/Source/WebCore/rendering/RenderTextControlSingleLine.cpp +++ b/Source/WebCore/rendering/RenderTextControlSingleLine.cpp @@ -1111,7 +1111,7 @@ PassRefPtr<Scrollbar> RenderTextControlSingleLine::createScrollbar(ScrollableAre InputElement* RenderTextControlSingleLine::inputElement() const { - return toInputElement(static_cast<Element*>(node())); + return node()->toInputElement(); } int RenderTextControlSingleLine::textBlockInsetLeft() const diff --git a/Source/WebCore/rendering/RenderTheme.cpp b/Source/WebCore/rendering/RenderTheme.cpp index b68429c..f254c99 100644 --- a/Source/WebCore/rendering/RenderTheme.cpp +++ b/Source/WebCore/rendering/RenderTheme.cpp @@ -779,10 +779,10 @@ bool RenderTheme::isActive(const RenderObject* o) const bool RenderTheme::isChecked(const RenderObject* o) const { - if (!o->node() || !o->node()->isElementNode()) + if (!o->node()) return false; - InputElement* inputElement = toInputElement(static_cast<Element*>(o->node())); + InputElement* inputElement = o->node()->toInputElement(); if (!inputElement) return false; @@ -791,10 +791,10 @@ bool RenderTheme::isChecked(const RenderObject* o) const bool RenderTheme::isIndeterminate(const RenderObject* o) const { - if (!o->node() || !o->node()->isElementNode()) + if (!o->node()) return false; - InputElement* inputElement = toInputElement(static_cast<Element*>(o->node())); + InputElement* inputElement = o->node()->toInputElement(); if (!inputElement) return false; diff --git a/Source/WebCore/rendering/RenderTheme.h b/Source/WebCore/rendering/RenderTheme.h index 5021712..53c23e3 100644 --- a/Source/WebCore/rendering/RenderTheme.h +++ b/Source/WebCore/rendering/RenderTheme.h @@ -88,6 +88,9 @@ public: virtual String extraQuirksStyleSheet() { return String(); } #if ENABLE(VIDEO) virtual String extraMediaControlsStyleSheet() { return String(); }; +#if ENABLE(FULLSCREEN_API) + virtual String extraFullScreenStyleSheet() { return String(); }; +#endif #endif // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline diff --git a/Source/WebCore/rendering/RenderThemeChromiumWin.cpp b/Source/WebCore/rendering/RenderThemeChromiumWin.cpp index 6f3be00..53ec9cb 100644 --- a/Source/WebCore/rendering/RenderThemeChromiumWin.cpp +++ b/Source/WebCore/rendering/RenderThemeChromiumWin.cpp @@ -43,8 +43,8 @@ #include "RenderProgress.h" #include "RenderSlider.h" #include "ScrollbarTheme.h" +#include "SystemInfo.h" #include "TransparencyWin.h" -#include "WindowsVersion.h" // FIXME: This dependency should eventually be removed. #include <skia/ext/skia_utils_win.h> @@ -141,8 +141,8 @@ bool ThemePainter::s_hasInstance = false; static void getNonClientMetrics(NONCLIENTMETRICS* metrics) { - static UINT size = isVistaOrNewer() ? - sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA; + static UINT size = (windowsVersion() >= WindowsVista) ? + (sizeof NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA; metrics->cbSize = size; bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, size, metrics, 0); ASSERT(success); diff --git a/Source/WebCore/rendering/RenderThemeMac.h b/Source/WebCore/rendering/RenderThemeMac.h index d951ff6..d64944b 100644 --- a/Source/WebCore/rendering/RenderThemeMac.h +++ b/Source/WebCore/rendering/RenderThemeMac.h @@ -162,6 +162,9 @@ protected: // Media controls virtual String extraMediaControlsStyleSheet(); +#if ENABLE(FULLSCREEN_API) + virtual String extraFullScreenStyleSheet(); +#endif virtual bool shouldRenderMediaControlPart(ControlPart, Element*); virtual bool usesMediaControlStatusDisplay(); diff --git a/Source/WebCore/rendering/RenderThemeMac.mm b/Source/WebCore/rendering/RenderThemeMac.mm index f5611dc..0e23c89 100644 --- a/Source/WebCore/rendering/RenderThemeMac.mm +++ b/Source/WebCore/rendering/RenderThemeMac.mm @@ -1961,20 +1961,45 @@ String RenderThemeMac::extraMediaControlsStyleSheet() #endif } +#if ENABLE(FULLSCREEN_API) +String RenderThemeMac::extraFullScreenStyleSheet() +{ +#if PLATFORM(MAC) + if (mediaControllerTheme() == MediaControllerThemeQuickTime) + return String(fullscreenQuickTimeUserAgentStyleSheet, sizeof(fullscreenQuickTimeUserAgentStyleSheet)); + + return String(); +#else + ASSERT_NOT_REACHED(); + return String(); +#endif +} +#endif + bool RenderThemeMac::shouldRenderMediaControlPart(ControlPart part, Element* element) { + HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(element); switch (part) { case MediaVolumeSliderContainerPart: case MediaVolumeSliderPart: case MediaVolumeSliderMuteButtonPart: case MediaVolumeSliderThumbPart: { - HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(element); return mediaControllerTheme() == MediaControllerThemeQuickTime && mediaElement->hasAudio(); } case MediaToggleClosedCaptionsButtonPart: // We rely on QTKit to render captions so don't enable the button unless it will be able to do so. if (!element->hasTagName(videoTag)) return false; + break; + case MediaRewindButtonPart: + if (mediaElement->isFullscreen()) + return mediaElement->movieLoadType() == MediaPlayer::LiveStream + || mediaElement->movieLoadType() == MediaPlayer::StoredStream; + case MediaSeekForwardButtonPart: + case MediaSeekBackButtonPart: + if (mediaElement->isFullscreen()) + return mediaElement->movieLoadType() != MediaPlayer::StoredStream + && mediaElement->movieLoadType() != MediaPlayer::LiveStream; default: break; } diff --git a/Source/WebCore/rendering/RenderThemeWin.cpp b/Source/WebCore/rendering/RenderThemeWin.cpp index d5e4155..af100b7 100644 --- a/Source/WebCore/rendering/RenderThemeWin.cpp +++ b/Source/WebCore/rendering/RenderThemeWin.cpp @@ -569,15 +569,17 @@ ThemeData RenderThemeWin::getThemeData(RenderObject* o, ControlSubPart subPart) result.m_state = determineState(o); break; case MenulistPart: - case MenulistButtonPart: - result.m_part = isRunningOnVistaOrLater() ? CP_DROPDOWNBUTTONRIGHT : CP_DROPDOWNBUTTON; - if (isRunningOnVistaOrLater() && documentIsInApplicationChromeMode(o->document())) { + case MenulistButtonPart: { + const bool isVistaOrLater = (windowsVersion() >= WindowsVista); + result.m_part = isVistaOrLater ? CP_DROPDOWNBUTTONRIGHT : CP_DROPDOWNBUTTON; + if (isVistaOrLater && documentIsInApplicationChromeMode(o->document())) { // The "readonly" look we use in application chrome mode // only uses a "normal" look for the drop down button. result.m_state = TS_NORMAL; } else result.m_state = determineState(o); break; + } case RadioPart: result.m_part = BP_RADIO; result.m_state = determineState(o); @@ -585,7 +587,7 @@ ThemeData RenderThemeWin::getThemeData(RenderObject* o, ControlSubPart subPart) case SearchFieldPart: case TextFieldPart: case TextAreaPart: - result.m_part = isRunningOnVistaOrLater() ? EP_EDITBORDER_NOSCROLL : TFP_TEXTFIELD; + result.m_part = (windowsVersion() >= WindowsVista) ? EP_EDITBORDER_NOSCROLL : TFP_TEXTFIELD; result.m_state = determineState(o); break; case SliderHorizontalPart: @@ -728,7 +730,7 @@ bool RenderThemeWin::paintMenuList(RenderObject* o, const PaintInfo& i, const In { HANDLE theme; int part; - if (haveTheme && isRunningOnVistaOrLater()) { + if (haveTheme && (windowsVersion() >= WindowsVista)) { theme = menuListTheme(); if (documentIsInApplicationChromeMode(o->document())) part = CP_READONLY; @@ -792,7 +794,7 @@ bool RenderThemeWin::paintMenuListButton(RenderObject* o, const PaintInfo& i, co buttonRect.setX(buttonRect.maxX() - dropDownButtonWidth); buttonRect.setWidth(dropDownButtonWidth); - if (isRunningOnVistaOrLater()) { + if ((windowsVersion() >= WindowsVista)) { // Outset the top, right, and bottom borders of the button so that they coincide with the <select>'s border. buttonRect.setY(buttonRect.y() - vistaMenuListButtonOutset); buttonRect.setHeight(buttonRect.height() + 2 * vistaMenuListButtonOutset); diff --git a/Source/WebCore/rendering/RenderTreeAsText.cpp b/Source/WebCore/rendering/RenderTreeAsText.cpp index cad5830..8857391 100644 --- a/Source/WebCore/rendering/RenderTreeAsText.cpp +++ b/Source/WebCore/rendering/RenderTreeAsText.cpp @@ -35,6 +35,7 @@ #include "InlineTextBox.h" #include "PrintContext.h" #include "RenderBR.h" +#include "RenderDetailsMarker.h" #include "RenderFileUploadControl.h" #include "RenderInline.h" #include "RenderLayer.h" @@ -45,6 +46,7 @@ #include "RenderView.h" #include "RenderWidget.h" #include "SelectionController.h" +#include <wtf/HexNumber.h> #include <wtf/UnusedParam.h> #include <wtf/Vector.h> #include <wtf/unicode/CharacterNames.h> @@ -209,11 +211,11 @@ String quoteAndEscapeNonPrintables(const String& s) if (c >= 0x20 && c < 0x7F) result.append(c); else { - unsigned u = c; - String hex = String::format("\\x{%X}", u); - unsigned len = hex.length(); - for (unsigned i = 0; i < len; ++i) - result.append(hex[i]); + result.append('\\'); + result.append('x'); + result.append('{'); + appendUnsignedAsHex(c, result); + result.append('}'); } } } @@ -372,6 +374,24 @@ void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]"; } + if (o.isDetailsMarker()) { + ts << ": "; + switch (toRenderDetailsMarker(&o)->orientation()) { + case RenderDetailsMarker::Left: + ts << "left"; + break; + case RenderDetailsMarker::Right: + ts << "right"; + break; + case RenderDetailsMarker::Up: + ts << "up"; + break; + case RenderDetailsMarker::Down: + ts << "down"; + break; + } + } + if (o.isListMarker()) { String text = toRenderListMarker(&o)->text(); if (!text.isEmpty()) { @@ -487,8 +507,10 @@ static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBo ts << " override"; } ts << ": " - << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len())) - << "\n"; + << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len())); + if (run.hasHyphen()) + ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString()); + ts << "\n"; } void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior) diff --git a/Source/WebCore/rendering/RenderVideo.cpp b/Source/WebCore/rendering/RenderVideo.cpp index 1ae736b..3a96ef8 100644 --- a/Source/WebCore/rendering/RenderVideo.cpp +++ b/Source/WebCore/rendering/RenderVideo.cpp @@ -202,6 +202,8 @@ void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty) if (displayingPoster) paintIntoRect(paintInfo.context, rect); + else if (document()->view() && document()->view()->paintBehavior() & PaintBehaviorFlattenCompositingLayers) + mediaPlayer->paintCurrentFrameInContext(paintInfo.context, rect); else mediaPlayer->paint(paintInfo.context, rect); } diff --git a/Source/WebCore/rendering/RenderWidget.cpp b/Source/WebCore/rendering/RenderWidget.cpp index 13b572d..33f6436 100644 --- a/Source/WebCore/rendering/RenderWidget.cpp +++ b/Source/WebCore/rendering/RenderWidget.cpp @@ -136,7 +136,7 @@ void RenderWidget::destroy() if (hasOverrideSize()) setOverrideSize(-1); - if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent())) + if (style() && (style()->logicalHeight().isPercent() || style()->logicalMinHeight().isPercent() || style()->logicalMaxHeight().isPercent())) RenderBlock::removePercentHeightDescendant(this); if (hasLayer()) { diff --git a/Source/WebCore/rendering/RootInlineBox.cpp b/Source/WebCore/rendering/RootInlineBox.cpp index 8617252..c6f1bd8 100644 --- a/Source/WebCore/rendering/RootInlineBox.cpp +++ b/Source/WebCore/rendering/RootInlineBox.cpp @@ -28,10 +28,12 @@ #include "Frame.h" #include "GraphicsContext.h" #include "HitTestResult.h" +#include "InlineTextBox.h" #include "Page.h" #include "PaintInfo.h" #include "RenderArena.h" #include "RenderBlock.h" +#include "VerticalPositionCache.h" using namespace std; @@ -52,7 +54,7 @@ RootInlineBox::RootInlineBox(RenderBlock* block) , m_hasAnnotationsBefore(false) , m_hasAnnotationsAfter(false) { - setIsHorizontal(block->style()->isHorizontalWritingMode()); + setIsHorizontal(block->isHorizontalWritingMode()); } @@ -238,7 +240,7 @@ int RootInlineBox::alignBoxesInBlockDirection(int heightOfBlock, GlyphOverflowAn m_baselineType = requiresIdeographicBaseline(textBoxDataMap) ? IdeographicBaseline : AlphabeticBaseline; - computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, setMaxAscent, setMaxDescent, noQuirksMode, + computeLogicalBoxHeights(this, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, setMaxAscent, setMaxDescent, noQuirksMode, textBoxDataMap, baselineType(), verticalPositionCache); if (maxAscent + maxDescent < max(maxPositionTop, maxPositionBottom)) @@ -335,7 +337,7 @@ GapRects RootInlineBox::lineSelectionGap(RenderBlock* rootBlock, const IntPoint& for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) { if (box->selectionState() != RenderObject::SelectionNone) { IntRect logicalRect(lastLogicalLeft, selTop, box->logicalLeft() - lastLogicalLeft, selHeight); - logicalRect.move(renderer()->style()->isHorizontalWritingMode() ? offsetFromRootBlock : IntSize(offsetFromRootBlock.height(), offsetFromRootBlock.width())); + logicalRect.move(renderer()->isHorizontalWritingMode() ? offsetFromRootBlock : IntSize(offsetFromRootBlock.height(), offsetFromRootBlock.width())); IntRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); if (isPreviousBoxSelected && gapRect.width() > 0 && gapRect.height() > 0) { if (paintInfo && box->parent()->renderer()->style()->visibility() == VISIBLE) @@ -354,13 +356,6 @@ GapRects RootInlineBox::lineSelectionGap(RenderBlock* rootBlock, const IntPoint& return result; } -void RootInlineBox::setHasSelectedChildren(bool b) -{ - if (m_hasSelectedChildren == b) - return; - m_hasSelectedChildren = b; -} - RenderObject::SelectionState RootInlineBox::selectionState() { // Walk over all of the selected boxes. @@ -460,7 +455,7 @@ RenderBlock* RootInlineBox::block() const static bool isEditableLeaf(InlineBox* leaf) { - return leaf && leaf->renderer() && leaf->renderer()->node() && leaf->renderer()->node()->isContentEditable(); + return leaf && leaf->renderer() && leaf->renderer()->node() && leaf->renderer()->node()->rendererIsEditable(); } InlineBox* RootInlineBox::closestLeafChildForLogicalLeftPosition(int leftPosition, bool onlyEditableLeaves) @@ -553,4 +548,269 @@ IntRect RootInlineBox::paddedLayoutOverflowRect(int endPadding) const return lineLayoutOverflow; } +static void setAscentAndDescent(int& ascent, int& descent, int newAscent, int newDescent, bool& ascentDescentSet) +{ + if (!ascentDescentSet) { + ascentDescentSet = true; + ascent = newAscent; + descent = newDescent; + } else { + ascent = max(ascent, newAscent); + descent = max(descent, newDescent); + } +} + +void RootInlineBox::ascentAndDescentForBox(InlineBox* box, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, int& ascent, int& descent, + bool& affectsAscent, bool& affectsDescent) const +{ + bool ascentDescentSet = false; + + // Replaced boxes will return 0 for the line-height if line-box-contain says they are + // not to be included. + if (box->renderer()->isReplaced()) { + if (renderer()->style(m_firstLine)->lineBoxContain() & LineBoxContainReplaced) { + ascent = box->baselinePosition(baselineType()); + descent = box->lineHeight() - ascent; + + // Replaced elements always affect both the ascent and descent. + affectsAscent = true; + affectsDescent = true; + } + return; + } + + Vector<const SimpleFontData*>* usedFonts = 0; + GlyphOverflow* glyphOverflow = 0; + if (box->isText()) { + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(static_cast<InlineTextBox*>(box)); + usedFonts = it == textBoxDataMap.end() ? 0 : &it->second.first; + glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->second.second; + } + + bool includeLeading = includeLeadingForBox(box); + bool includeFont = includeFontForBox(box); + + bool setUsedFont = false; + bool setUsedFontWithLeading = false; + + if (usedFonts && !usedFonts->isEmpty() && (includeFont || (box->renderer()->style(m_firstLine)->lineHeight().isNegative() && includeLeading))) { + usedFonts->append(box->renderer()->style(m_firstLine)->font().primaryFont()); + for (size_t i = 0; i < usedFonts->size(); ++i) { + const FontMetrics& fontMetrics = usedFonts->at(i)->fontMetrics(); + int usedFontAscent = fontMetrics.ascent(baselineType()); + int usedFontDescent = fontMetrics.descent(baselineType()); + int halfLeading = (fontMetrics.lineSpacing() - fontMetrics.height()) / 2; + int usedFontAscentAndLeading = usedFontAscent + halfLeading; + int usedFontDescentAndLeading = fontMetrics.lineSpacing() - usedFontAscentAndLeading; + if (includeFont) { + setAscentAndDescent(ascent, descent, usedFontAscent, usedFontDescent, ascentDescentSet); + setUsedFont = true; + } + if (includeLeading) { + setAscentAndDescent(ascent, descent, usedFontAscentAndLeading, usedFontDescentAndLeading, ascentDescentSet); + setUsedFontWithLeading = true; + } + if (!affectsAscent) + affectsAscent = usedFontAscent - box->logicalTop() > 0; + if (!affectsDescent) + affectsDescent = usedFontDescent + box->logicalTop() > 0; + } + } + + // If leading is included for the box, then we compute that box. + if (includeLeading && !setUsedFontWithLeading) { + int ascentWithLeading = box->baselinePosition(baselineType()); + int descentWithLeading = box->lineHeight() - ascentWithLeading; + setAscentAndDescent(ascent, descent, ascentWithLeading, descentWithLeading, ascentDescentSet); + + // Examine the font box for inline flows and text boxes to see if any part of it is above the baseline. + // If the top of our font box relative to the root box baseline is above the root box baseline, then + // we are contributing to the maxAscent value. Descent is similar. If any part of our font box is below + // the root box's baseline, then we contribute to the maxDescent value. + affectsAscent = ascentWithLeading - box->logicalTop() > 0; + affectsDescent = descentWithLeading + box->logicalTop() > 0; + } + + if (includeFontForBox(box) && !setUsedFont) { + int fontAscent = box->renderer()->style(m_firstLine)->fontMetrics().ascent(); + int fontDescent = box->renderer()->style(m_firstLine)->fontMetrics().descent(); + setAscentAndDescent(ascent, descent, fontAscent, fontDescent, ascentDescentSet); + affectsAscent = fontAscent - box->logicalTop() > 0; + affectsDescent = fontDescent + box->logicalTop() > 0; + } + + if (includeGlyphsForBox(box) && glyphOverflow && glyphOverflow->computeBounds) { + setAscentAndDescent(ascent, descent, glyphOverflow->top, glyphOverflow->bottom, ascentDescentSet); + affectsAscent = glyphOverflow->top - box->logicalTop() > 0; + affectsDescent = glyphOverflow->bottom + box->logicalTop() > 0; + glyphOverflow->top = min(glyphOverflow->top, max(0, glyphOverflow->top - box->renderer()->style(m_firstLine)->fontMetrics().ascent())); + glyphOverflow->bottom = min(glyphOverflow->bottom, max(0, glyphOverflow->bottom - box->renderer()->style(m_firstLine)->fontMetrics().descent())); + } + + if (includeMarginForBox(box)) { + int ascentWithMargin = box->renderer()->style(m_firstLine)->fontMetrics().ascent(); + int descentWithMargin = box->renderer()->style(m_firstLine)->fontMetrics().descent(); + if (box->parent() && !box->renderer()->isText()) { + ascentWithMargin += box->boxModelObject()->borderBefore() + box->boxModelObject()->paddingBefore() + box->boxModelObject()->marginBefore(); + descentWithMargin += box->boxModelObject()->borderAfter() + box->boxModelObject()->paddingAfter() + box->boxModelObject()->marginAfter(); + } + setAscentAndDescent(ascent, descent, ascentWithMargin, descentWithMargin, ascentDescentSet); + + // Treat like a replaced element, since we're using the margin box. + affectsAscent = true; + affectsDescent = true; + } +} + +int RootInlineBox::verticalPositionForBox(InlineBox* box, VerticalPositionCache& verticalPositionCache) +{ + if (box->renderer()->isText()) + return box->parent()->logicalTop(); + + RenderBoxModelObject* renderer = box->boxModelObject(); + ASSERT(renderer->isInline()); + if (!renderer->isInline()) + return 0; + + // This method determines the vertical position for inline elements. + bool firstLine = m_firstLine; + if (firstLine && !renderer->document()->usesFirstLineRules()) + firstLine = false; + + // Check the cache. + bool isRenderInline = renderer->isRenderInline(); + if (isRenderInline && !firstLine) { + int verticalPosition = verticalPositionCache.get(renderer, baselineType()); + if (verticalPosition != PositionUndefined) + return verticalPosition; + } + + int verticalPosition = 0; + EVerticalAlign verticalAlign = renderer->style()->verticalAlign(); + if (verticalAlign == TOP || verticalAlign == BOTTOM) + return 0; + + RenderObject* parent = renderer->parent(); + if (parent->isRenderInline() && parent->style()->verticalAlign() != TOP && parent->style()->verticalAlign() != BOTTOM) + verticalPosition = box->parent()->logicalTop(); + + if (verticalAlign != BASELINE) { + const Font& font = parent->style(firstLine)->font(); + const FontMetrics& fontMetrics = font.fontMetrics(); + int fontSize = font.pixelSize(); + + LineDirectionMode lineDirection = parent->isHorizontalWritingMode() ? HorizontalLine : VerticalLine; + + if (verticalAlign == SUB) + verticalPosition += fontSize / 5 + 1; + else if (verticalAlign == SUPER) + verticalPosition -= fontSize / 3 + 1; + else if (verticalAlign == TEXT_TOP) + verticalPosition += renderer->baselinePosition(baselineType(), firstLine, lineDirection) - fontMetrics.ascent(baselineType()); + else if (verticalAlign == MIDDLE) + verticalPosition += -static_cast<int>(fontMetrics.xHeight() / 2) - renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection); + else if (verticalAlign == TEXT_BOTTOM) { + verticalPosition += fontMetrics.descent(baselineType()); + // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case. + if (!renderer->isReplaced() || renderer->isInlineBlockOrInlineTable()) + verticalPosition -= (renderer->lineHeight(firstLine, lineDirection) - renderer->baselinePosition(baselineType(), firstLine, lineDirection)); + } else if (verticalAlign == BASELINE_MIDDLE) + verticalPosition += -renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection); + else if (verticalAlign == LENGTH) + verticalPosition -= renderer->style()->verticalAlignLength().calcValue(renderer->lineHeight(firstLine, lineDirection)); + } + + // Store the cached value. + if (isRenderInline && !firstLine) + verticalPositionCache.set(renderer, baselineType(), verticalPosition); + + return verticalPosition; +} + +bool RootInlineBox::includeLeadingForBox(InlineBox* box) const +{ + if (box->renderer()->isReplaced() || (box->renderer()->isText() && !box->isText())) + return false; + + LineBoxContain lineBoxContain = renderer()->style()->lineBoxContain(); + return (lineBoxContain & LineBoxContainInline) || (box == this && (lineBoxContain & LineBoxContainBlock)); +} + +bool RootInlineBox::includeFontForBox(InlineBox* box) const +{ + if (box->renderer()->isReplaced() || (box->renderer()->isText() && !box->isText())) + return false; + + if (!box->isText() && box->isInlineFlowBox() && !static_cast<InlineFlowBox*>(box)->hasTextChildren()) + return false; + + // For now map "glyphs" to "font" in vertical text mode until the bounds returned by glyphs aren't garbage. + LineBoxContain lineBoxContain = renderer()->style()->lineBoxContain(); + return (lineBoxContain & LineBoxContainFont) || (!isHorizontal() && (lineBoxContain & LineBoxContainGlyphs)); +} + +bool RootInlineBox::includeGlyphsForBox(InlineBox* box) const +{ + if (box->renderer()->isReplaced() || (box->renderer()->isText() && !box->isText())) + return false; + + if (!box->isText() && box->isInlineFlowBox() && !static_cast<InlineFlowBox*>(box)->hasTextChildren()) + return false; + + // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage. + LineBoxContain lineBoxContain = renderer()->style()->lineBoxContain(); + return isHorizontal() && (lineBoxContain & LineBoxContainGlyphs); +} + +bool RootInlineBox::includeMarginForBox(InlineBox* box) const +{ + if (box->renderer()->isReplaced() || (box->renderer()->isText() && !box->isText())) + return false; + + LineBoxContain lineBoxContain = renderer()->style()->lineBoxContain(); + return lineBoxContain & LineBoxContainInlineBox; +} + + +bool RootInlineBox::fitsToGlyphs() const +{ + // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage. + LineBoxContain lineBoxContain = renderer()->style()->lineBoxContain(); + return isHorizontal() && (lineBoxContain & LineBoxContainGlyphs); +} + +bool RootInlineBox::includesRootLineBoxFontOrLeading() const +{ + LineBoxContain lineBoxContain = renderer()->style()->lineBoxContain(); + return (lineBoxContain & LineBoxContainBlock) || (lineBoxContain & LineBoxContainInline) || (lineBoxContain & LineBoxContainFont); +} + +Node* RootInlineBox::getLogicalStartBoxWithNode(InlineBox*& startBox) +{ + Vector<InlineBox*> leafBoxesInLogicalOrder; + collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder); + for (size_t i = 0; i < leafBoxesInLogicalOrder.size(); ++i) { + if (leafBoxesInLogicalOrder[i]->renderer()->node()) { + startBox = leafBoxesInLogicalOrder[i]; + return startBox->renderer()->node(); + } + } + startBox = 0; + return 0; +} + +Node* RootInlineBox::getLogicalEndBoxWithNode(InlineBox*& endBox) +{ + Vector<InlineBox*> leafBoxesInLogicalOrder; + collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder); + for (size_t i = leafBoxesInLogicalOrder.size(); i > 0; --i) { + if (leafBoxesInLogicalOrder[i - 1]->renderer()->node()) { + endBox = leafBoxesInLogicalOrder[i - 1]; + return endBox->renderer()->node(); + } + } + endBox = 0; + return 0; +} + } // namespace WebCore diff --git a/Source/WebCore/rendering/RootInlineBox.h b/Source/WebCore/rendering/RootInlineBox.h index 1a9f0a8..0ff5704 100644 --- a/Source/WebCore/rendering/RootInlineBox.h +++ b/Source/WebCore/rendering/RootInlineBox.h @@ -99,8 +99,8 @@ public: virtual void paint(PaintInfo&, int tx, int ty); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int); - bool hasSelectedChildren() const { return m_hasSelectedChildren; } - void setHasSelectedChildren(bool); + bool hasSelectedChildren() const { return m_hasSelectedChildrenOrCanHaveLeadingExpansion; } + void setHasSelectedChildren(bool hasSelectedChildren) { m_hasSelectedChildrenOrCanHaveLeadingExpansion = hasSelectedChildren; } virtual RenderObject::SelectionState selectionState(); InlineBox* firstSelectedBox(); @@ -133,6 +133,17 @@ public: IntRect paddedLayoutOverflowRect(int endPadding) const; + void ascentAndDescentForBox(InlineBox*, GlyphOverflowAndFallbackFontsMap&, int& ascent, int& descent, bool& affectsAscent, bool& affectsDescent) const; + int verticalPositionForBox(InlineBox*, VerticalPositionCache&); + bool includeLeadingForBox(InlineBox*) const; + bool includeFontForBox(InlineBox*) const; + bool includeGlyphsForBox(InlineBox*) const; + bool includeMarginForBox(InlineBox*) const; + bool fitsToGlyphs() const; + bool includesRootLineBoxFontOrLeading() const; + + Node* getLogicalStartBoxWithNode(InlineBox*&); + Node* getLogicalEndBoxWithNode(InlineBox*&); private: bool hasEllipsisBox() const { return m_hasEllipsisBoxOrHyphen; } void setHasEllipsisBox(bool hasEllipsisBox) { m_hasEllipsisBoxOrHyphen = hasEllipsisBox; } diff --git a/Source/WebCore/rendering/mathml/RenderMathMLRoot.cpp b/Source/WebCore/rendering/mathml/RenderMathMLRoot.cpp index 075f6ba..51046f8 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLRoot.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLRoot.cpp @@ -229,7 +229,10 @@ void RenderMathMLRoot::layout() } // Positioning of the index - RenderBoxModelObject* indexBox = toRenderBoxModelObject(firstChild()->firstChild()); + RenderObject* possibleIndex = firstChild()->firstChild(); + while (possibleIndex && !possibleIndex->isBoxModelObject()) + possibleIndex = possibleIndex->nextSibling(); + RenderBoxModelObject* indexBox = toRenderBoxModelObject(possibleIndex); if (!indexBox) return; diff --git a/Source/WebCore/rendering/style/BorderData.h b/Source/WebCore/rendering/style/BorderData.h index 0e50edb..26f089f 100644 --- a/Source/WebCore/rendering/style/BorderData.h +++ b/Source/WebCore/rendering/style/BorderData.h @@ -49,13 +49,13 @@ public: bool hasBorderRadius() const { - if (m_topLeft.width().rawValue() > 0) + if (!m_topLeft.width().isZero()) return true; - if (m_topRight.width().rawValue() > 0) + if (!m_topRight.width().isZero()) return true; - if (m_bottomLeft.width().rawValue() > 0) + if (!m_bottomLeft.width().isZero()) return true; - if (m_bottomRight.width().rawValue() > 0) + if (!m_bottomRight.width().isZero()) return true; return false; } diff --git a/Source/WebCore/rendering/style/FillLayer.cpp b/Source/WebCore/rendering/style/FillLayer.cpp index 59f3bb2..c957cfe 100644 --- a/Source/WebCore/rendering/style/FillLayer.cpp +++ b/Source/WebCore/rendering/style/FillLayer.cpp @@ -129,17 +129,6 @@ bool FillLayer::operator==(const FillLayer& o) const void FillLayer::fillUnsetProperties() { FillLayer* curr; - for (curr = this; curr && curr->isImageSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_image = pattern->m_image; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } - } - for (curr = this; curr && curr->isXPositionSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. @@ -246,12 +235,7 @@ void FillLayer::cullEmptyLayers() FillLayer* next; for (FillLayer* p = this; p; p = next) { next = p->m_next; - if (next && !next->isImageSet() && - !next->isXPositionSet() && !next->isYPositionSet() && - !next->isAttachmentSet() && !next->isClipSet() && - !next->isCompositeSet() && !next->isOriginSet() && - !next->isRepeatXSet() && !next->isRepeatYSet() - && !next->isSizeSet()) { + if (next && !next->isImageSet()) { delete next; p->m_next = 0; break; diff --git a/Source/WebCore/rendering/style/RenderStyle.cpp b/Source/WebCore/rendering/style/RenderStyle.cpp index 122b762..34e972d 100644 --- a/Source/WebCore/rendering/style/RenderStyle.cpp +++ b/Source/WebCore/rendering/style/RenderStyle.cpp @@ -56,6 +56,14 @@ PassRefPtr<RenderStyle> RenderStyle::createDefaultStyle() return adoptRef(new RenderStyle(true)); } +PassRefPtr<RenderStyle> RenderStyle::createAnonymousStyle(const RenderStyle* parentStyle) +{ + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(parentStyle); + newStyle->inheritUnicodeBidiFrom(parentStyle); + return newStyle; +} + PassRefPtr<RenderStyle> RenderStyle::clone(const RenderStyle* other) { return adoptRef(new RenderStyle(*other)); @@ -295,21 +303,6 @@ static bool positionedObjectMoved(const LengthBox& a, const LengthBox& b) return true; } -/* - compares two styles. The result gives an idea of the action that - needs to be taken when replacing the old style with a new one. - - CbLayout: The containing block of the object needs a relayout. - Layout: the RenderObject needs a relayout after the style change - Visible: The change is visible, but no relayout is needed - NonVisible: The object does need neither repaint nor relayout after - the change. - - ### TODO: - A lot can be optimised here based on the display type, lots of - optimisations are unimplemented, and currently result in the - worst case result causing a relayout of the containing block. -*/ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedContextSensitiveProperties) const { changedContextSensitiveProperties = ContextSensitivePropertyNone; @@ -411,7 +404,8 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon rareInheritedData->locale != other->rareInheritedData->locale || rareInheritedData->textEmphasisMark != other->rareInheritedData->textEmphasisMark || rareInheritedData->textEmphasisPosition != other->rareInheritedData->textEmphasisPosition || - rareInheritedData->textEmphasisCustomMark != other->rareInheritedData->textEmphasisCustomMark) + rareInheritedData->textEmphasisCustomMark != other->rareInheritedData->textEmphasisCustomMark || + rareInheritedData->m_lineBoxContain != other->rareInheritedData->m_lineBoxContain) return StyleDifferenceLayout; if (!rareInheritedData->shadowDataEquivalent(*other->rareInheritedData.get())) @@ -499,15 +493,19 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon rareNonInheritedData->m_counterReset != other->rareNonInheritedData->m_counterReset) return StyleDifferenceLayout; + if ((visibility() == COLLAPSE) != (other->visibility() == COLLAPSE)) + return StyleDifferenceLayout; + if ((rareNonInheritedData->opacity == 1 && other->rareNonInheritedData->opacity < 1) || (rareNonInheritedData->opacity < 1 && other->rareNonInheritedData->opacity == 1)) { - // FIXME: We should add an optimized form of layout that just recomputes visual overflow. + // FIXME: We would like to use SimplifiedLayout here, but we can't quite do that yet. + // We need to make sure SimplifiedLayout can operate correctly on RenderInlines (we will need + // to add a selfNeedsSimplifiedLayout bit in order to not get confused and taint every line). + // In addition we need to solve the floating object issue when layers come and go. Right now + // a full layout is necessary to keep floating object lists sane. return StyleDifferenceLayout; } - 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, @@ -525,13 +523,10 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon if (position() == AbsolutePosition && positionedObjectMoved(surround->offset, other->surround->offset)) return StyleDifferenceLayoutPositionedMovementOnly; - // FIXME: We will need to do a bit of work in RenderObject/Box::setStyle before we - // can stop doing a layout when relative positioned objects move. In particular, we'll need - // to update scrolling positions and figure out how to do a repaint properly of the updated layer. - //if (other->position() == RelativePosition) - // return RepaintLayer; - //else - return StyleDifferenceLayout; + // FIXME: We would like to use SimplifiedLayout for relative positioning, but we can't quite do that yet. + // We need to make sure SimplifiedLayout can operate correctly on RenderInlines (we will need + // to add a selfNeedsSimplifiedLayout bit in order to not get confused and taint every line). + return StyleDifferenceLayout; } else if (m_box->zIndex() != other->m_box->zIndex() || m_box->hasAutoZIndex() != other->m_box->hasAutoZIndex() || visual->clip != other->visual->clip || visual->hasClip != other->visual->hasClip) return StyleDifferenceRepaintLayer; diff --git a/Source/WebCore/rendering/style/RenderStyle.h b/Source/WebCore/rendering/style/RenderStyle.h index 79ed6e4..859348f 100644 --- a/Source/WebCore/rendering/style/RenderStyle.h +++ b/Source/WebCore/rendering/style/RenderStyle.h @@ -27,6 +27,7 @@ #include "AnimationList.h" #include "BorderValue.h" +#include "CSSLineBoxContainValue.h" #include "CSSPrimitiveValue.h" #include "CSSPropertyNames.h" #include "Color.h" @@ -58,6 +59,7 @@ #include "StyleTransformData.h" #include "StyleVisualData.h" #include "TextDirection.h" +#include "TextOrientation.h" #include "ThemeTypes.h" #include "TransformOperations.h" #include <wtf/Forward.h> @@ -106,6 +108,7 @@ class RenderStyle: public RefCounted<RenderStyle> { friend class AnimationBase; // Used by CSS animations. We can't allow them to animate based off visited colors. friend class ApplyStyleCommand; // Editing has to only reveal unvisited info. friend class EditingStyle; // Editing has to only reveal unvisited info. + friend class CSSStyleApplyProperty; // Sets members directly. friend class CSSStyleSelector; // Sets members directly. friend class CSSComputedStyleDeclaration; // Ignores visited styles, so needs to be able to see unvisited info. friend class PropertyWrapperMaybeInvalidColor; // Used by CSS animations. We can't allow them to animate based off visited colors. @@ -184,7 +187,7 @@ protected: unsigned _list_style_type : 7; // EListStyleType unsigned _list_style_position : 1; // EListStylePosition unsigned _visibility : 2; // EVisibility - unsigned _text_align : 3; // ETextAlign + unsigned _text_align : 4; // ETextAlign unsigned _text_transform : 2; // ETextTransform unsigned _text_decorations : 4; unsigned _cursor_style : 6; // ECursor @@ -310,6 +313,7 @@ private: public: static PassRefPtr<RenderStyle> create(); static PassRefPtr<RenderStyle> createDefaultStyle(); + static PassRefPtr<RenderStyle> createAnonymousStyle(const RenderStyle* parentStyle); static PassRefPtr<RenderStyle> clone(const RenderStyle*); ~RenderStyle(); @@ -770,6 +774,7 @@ public: bool isRunningAcceleratedAnimation() const { return rareNonInheritedData->m_runningAcceleratedAnimation; } #endif + LineBoxContain lineBoxContain() const { return rareInheritedData->m_lineBoxContain; } const LineClampValue& lineClamp() const { return rareNonInheritedData->lineClamp; } bool textSizeAdjust() const { return rareInheritedData->textSizeAdjust; } ETextSecurity textSecurity() const { return static_cast<ETextSecurity>(rareInheritedData->textSecurity); } @@ -1130,6 +1135,7 @@ public: void setIsRunningAcceleratedAnimation(bool b = true) { SET_VAR(rareNonInheritedData, m_runningAcceleratedAnimation, b); } #endif + void setLineBoxContain(LineBoxContain c) { SET_VAR(rareInheritedData, m_lineBoxContain, c); } void setLineClamp(LineClampValue c) { SET_VAR(rareNonInheritedData, lineClamp, c); } void setTextSizeAdjust(bool b) { SET_VAR(rareInheritedData, textSizeAdjust, b); } void setTextSecurity(ETextSecurity aTextSecurity) { SET_VAR(rareInheritedData, textSecurity, aTextSecurity); } @@ -1246,6 +1252,7 @@ public: static TextDirection initialDirection() { return LTR; } static WritingMode initialWritingMode() { return TopToBottomWritingMode; } static TextCombine initialTextCombine() { return TextCombineNone; } + static TextOrientation initialTextOrientation() { return TextOrientationVerticalRight; } static EDisplay initialDisplay() { return INLINE; } static EEmptyCell initialEmptyCells() { return SHOW; } static EFloat initialFloating() { return FNONE; } @@ -1339,6 +1346,7 @@ public: static TextEmphasisMark initialTextEmphasisMark() { return TextEmphasisMarkNone; } static const AtomicString& initialTextEmphasisCustomMark() { return nullAtom; } static TextEmphasisPosition initialTextEmphasisPosition() { return TextEmphasisPositionOver; } + static LineBoxContain initialLineBoxContain() { return LineBoxContainBlock | LineBoxContainInline | LineBoxContainReplaced; } // Keep these at the end. static LineClampValue initialLineClamp() { return LineClampValue(); } @@ -1365,6 +1373,7 @@ public: #endif private: + void inheritUnicodeBidiFrom(const RenderStyle* parent) { noninherited_flags._unicodeBidi = parent->noninherited_flags._unicodeBidi; } 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; diff --git a/Source/WebCore/rendering/style/RenderStyleConstants.h b/Source/WebCore/rendering/style/RenderStyleConstants.h index 3c62dbe..08f2c15 100644 --- a/Source/WebCore/rendering/style/RenderStyleConstants.h +++ b/Source/WebCore/rendering/style/RenderStyleConstants.h @@ -51,6 +51,7 @@ enum StyleDifference { StyleDifferenceRepaint, StyleDifferenceRepaintLayer, StyleDifferenceLayoutPositionedMovementOnly, + StyleDifferenceSimplifiedLayout, StyleDifferenceLayout }; @@ -120,7 +121,7 @@ enum ETableLayout { }; enum EUnicodeBidi { - UBNormal, Embed, Override + UBNormal, Embed, Override, Isolate }; // CSS Text Layout Module Level 3: Vertical writing support @@ -318,7 +319,7 @@ enum EWhiteSpace { }; enum ETextAlign { - TAAUTO, LEFT, RIGHT, CENTER, JUSTIFY, WEBKIT_LEFT, WEBKIT_RIGHT, WEBKIT_CENTER + TAAUTO, LEFT, RIGHT, CENTER, JUSTIFY, WEBKIT_LEFT, WEBKIT_RIGHT, WEBKIT_CENTER, TASTART, TAEND, }; enum ETextTransform { diff --git a/Source/WebCore/rendering/style/StyleRareInheritedData.cpp b/Source/WebCore/rendering/style/StyleRareInheritedData.cpp index 2798fd1..2253d1c 100644 --- a/Source/WebCore/rendering/style/StyleRareInheritedData.cpp +++ b/Source/WebCore/rendering/style/StyleRareInheritedData.cpp @@ -66,6 +66,7 @@ StyleRareInheritedData::StyleRareInheritedData() , textEmphasisFill(TextEmphasisFillFilled) , textEmphasisMark(TextEmphasisMarkNone) , textEmphasisPosition(TextEmphasisPositionOver) + , m_lineBoxContain(RenderStyle::initialLineBoxContain()) , hyphenationLimitBefore(-1) , hyphenationLimitAfter(-1) { @@ -113,6 +114,7 @@ StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o) , textEmphasisFill(o.textEmphasisFill) , textEmphasisMark(o.textEmphasisMark) , textEmphasisPosition(o.textEmphasisPosition) + , m_lineBoxContain(o.m_lineBoxContain) , hyphenationString(o.hyphenationString) , hyphenationLimitBefore(o.hyphenationLimitBefore) , hyphenationLimitAfter(o.hyphenationLimitAfter) @@ -179,6 +181,7 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const && textEmphasisFill == o.textEmphasisFill && textEmphasisMark == o.textEmphasisMark && textEmphasisPosition == o.textEmphasisPosition + && m_lineBoxContain == o.m_lineBoxContain && hyphenationString == o.hyphenationString && locale == o.locale && textEmphasisCustomMark == o.textEmphasisCustomMark diff --git a/Source/WebCore/rendering/style/StyleRareInheritedData.h b/Source/WebCore/rendering/style/StyleRareInheritedData.h index d4f233c..39cfe3c 100644 --- a/Source/WebCore/rendering/style/StyleRareInheritedData.h +++ b/Source/WebCore/rendering/style/StyleRareInheritedData.h @@ -99,6 +99,7 @@ public: unsigned textEmphasisFill : 1; // TextEmphasisFill unsigned textEmphasisMark : 3; // TextEmphasisMark unsigned textEmphasisPosition : 1; // TextEmphasisPosition + unsigned m_lineBoxContain: 7; // LineBoxContain AtomicString hyphenationString; short hyphenationLimitBefore; diff --git a/Source/WebCore/rendering/style/StyleRareNonInheritedData.h b/Source/WebCore/rendering/style/StyleRareNonInheritedData.h index 89437f6..268a123 100644 --- a/Source/WebCore/rendering/style/StyleRareNonInheritedData.h +++ b/Source/WebCore/rendering/style/StyleRareNonInheritedData.h @@ -107,7 +107,7 @@ public: short m_counterIncrement; short m_counterReset; - + #if USE(ACCELERATED_COMPOSITING) bool m_runningAcceleratedAnimation : 1; #endif diff --git a/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp b/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp index 835bce8..9f3d8a5 100644 --- a/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp @@ -34,6 +34,7 @@ #include "RenderSVGInline.cpp" #include "RenderSVGInlineText.cpp" #include "RenderSVGModelObject.cpp" +#include "RenderSVGPath.cpp" #include "RenderSVGResource.cpp" #include "RenderSVGResourceClipper.cpp" #include "RenderSVGResourceContainer.cpp" diff --git a/Source/WebCore/rendering/svg/RenderSVGText.cpp b/Source/WebCore/rendering/svg/RenderSVGText.cpp index 56d9306..8ca3d58 100644 --- a/Source/WebCore/rendering/svg/RenderSVGText.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGText.cpp @@ -51,6 +51,7 @@ namespace WebCore { RenderSVGText::RenderSVGText(SVGTextElement* node) : RenderSVGBlock(node) + , m_needsReordering(false) , m_needsPositioningValuesUpdate(true) , m_needsTransformUpdate(true) { @@ -127,6 +128,7 @@ void RenderSVGText::layout() // Perform SVG text layout phase one (see SVGTextLayoutAttributesBuilder for details). SVGTextLayoutAttributesBuilder layoutAttributesBuilder; layoutAttributesBuilder.buildLayoutAttributesForTextSubtree(this); + m_needsReordering = true; m_needsPositioningValuesUpdate = false; updateCachedBoundariesInParents = true; } @@ -134,7 +136,7 @@ void RenderSVGText::layout() // 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. ASSERT(!isInline()); - ASSERT(!layoutOnlyPositionedObjects()); + ASSERT(!simplifiedLayout()); ASSERT(!scrollsOverflow()); ASSERT(!hasControlClip()); ASSERT(!hasColumns()); @@ -150,6 +152,9 @@ void RenderSVGText::layout() ASSERT(childrenInline()); forceLayoutInlineChildren(); + if (m_needsReordering) + m_needsReordering = false; + if (!updateCachedBoundariesInParents) updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox(); diff --git a/Source/WebCore/rendering/svg/RenderSVGText.h b/Source/WebCore/rendering/svg/RenderSVGText.h index 188d5fc..93fc5f8 100644 --- a/Source/WebCore/rendering/svg/RenderSVGText.h +++ b/Source/WebCore/rendering/svg/RenderSVGText.h @@ -26,6 +26,7 @@ #include "AffineTransform.h" #include "RenderSVGBlock.h" +#include "SVGTextLayoutAttributes.h" namespace WebCore { @@ -33,7 +34,7 @@ class SVGTextElement; class RenderSVGText : public RenderSVGBlock { public: - RenderSVGText(SVGTextElement* node); + RenderSVGText(SVGTextElement*); void setNeedsPositioningValuesUpdate() { m_needsPositioningValuesUpdate = true; } virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } @@ -42,6 +43,9 @@ public: static RenderSVGText* locateRenderSVGTextAncestor(RenderObject*); static const RenderSVGText* locateRenderSVGTextAncestor(const RenderObject*); + Vector<SVGTextLayoutAttributes>& layoutAttributes() { return m_layoutAttributes; } + bool needsReordering() const { return m_needsReordering; } + private: virtual const char* renderName() const { return "RenderSVGText"; } virtual bool isSVGText() const { return true; } @@ -71,9 +75,11 @@ private: virtual RenderBlock* firstLineBlock() const; virtual void updateFirstLetter(); + bool m_needsReordering : 1; bool m_needsPositioningValuesUpdate : 1; bool m_needsTransformUpdate : 1; AffineTransform m_localTransform; + Vector<SVGTextLayoutAttributes> m_layoutAttributes; }; inline RenderSVGText* toRenderSVGText(RenderObject* object) diff --git a/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp b/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp index 05e1357..17bbfaa 100644 --- a/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp +++ b/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp @@ -73,7 +73,7 @@ int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragmen if (!fragment.transform.isIdentity()) textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragment.transform.xScale())); - return fragment.positionListOffset - start() + textRenderer->scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs); + return fragment.characterOffset - start() + textRenderer->scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs); } float SVGInlineTextBox::positionForOffset(int) const @@ -402,7 +402,7 @@ TextRun SVGInlineTextBox::constructTextRun(RenderStyle* style, const SVGTextFrag RenderText* text = textRenderer(); ASSERT(text); - TextRun run(text->characters() + fragment.positionListOffset + TextRun run(text->characters() + fragment.characterOffset , fragment.length , false /* allowTabs */ , 0 /* xPos, only relevant with allowTabs=true */ @@ -425,7 +425,7 @@ bool SVGInlineTextBox::mapStartEndPositionsIntoFragmentCoordinates(const SVGText if (startPosition >= endPosition) return false; - int offset = static_cast<int>(fragment.positionListOffset) - start(); + int offset = static_cast<int>(fragment.characterOffset) - start(); int length = static_cast<int>(fragment.length); if (startPosition >= offset + length || endPosition <= offset) diff --git a/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp b/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp index 0f94fdd..a099f87 100644 --- a/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp +++ b/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp @@ -464,8 +464,8 @@ static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textB SVGTextFragment& fragment = fragments.at(i); writeIndent(ts, indent + 1); - unsigned startOffset = fragment.positionListOffset; - unsigned endOffset = fragment.positionListOffset + fragment.length; + unsigned startOffset = fragment.characterOffset; + unsigned endOffset = fragment.characterOffset + fragment.length; // 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 "; @@ -500,7 +500,7 @@ static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textB ts << " override"; } - ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.positionListOffset, fragment.length)) << "\n"; + ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.characterOffset, fragment.length)) << "\n"; } } diff --git a/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp b/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp index c2289d6..ddbd3ea 100644 --- a/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp +++ b/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp @@ -25,8 +25,8 @@ #if ENABLE(SVG) #include "GraphicsContext.h" -#include "RenderBlock.h" #include "RenderSVGInlineText.h" +#include "RenderSVGText.h" #include "SVGInlineFlowBox.h" #include "SVGInlineTextBox.h" #include "SVGNames.h" @@ -73,8 +73,15 @@ void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int) void SVGRootInlineBox::computePerCharacterLayoutInformation() { + RenderSVGText* parentBlock = toRenderSVGText(block()); + ASSERT(parentBlock); + + Vector<SVGTextLayoutAttributes>& attributes = parentBlock->layoutAttributes(); + if (parentBlock->needsReordering()) + reorderValueLists(attributes); + // Perform SVG text layout phase two (see SVGTextLayoutEngine for details). - SVGTextLayoutEngine characterLayout; + SVGTextLayoutEngine characterLayout(attributes); layoutCharactersInTextBoxes(this, characterLayout); // Perform SVG text layout phase three (see SVGTextChunkBuilder for details). @@ -108,8 +115,12 @@ void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGText 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; + RenderSVGText* parentBlock = toRenderSVGText(block()); + ASSERT(parentBlock); + + SVGTextLayoutEngine lineLayout(parentBlock->layoutAttributes()); layoutCharactersInTextBoxes(flowBox, lineLayout); + characterLayout.beginTextPathLayout(child->renderer(), lineLayout); } @@ -215,6 +226,91 @@ InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const IntPoint& point) return closestLeaf ? closestLeaf : lastLeaf; } + +static inline void swapItemsInVector(Vector<float>& firstVector, Vector<float>& lastVector, unsigned first, unsigned last) +{ + float temp = firstVector.at(first); + firstVector.at(first) = lastVector.at(last); + lastVector.at(last) = temp; +} + +static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes& firstAttributes, SVGTextLayoutAttributes& lastAttributes, unsigned firstPosition, unsigned lastPosition) +{ + swapItemsInVector(firstAttributes.xValues(), lastAttributes.xValues(), firstPosition, lastPosition); + swapItemsInVector(firstAttributes.yValues(), lastAttributes.yValues(), firstPosition, lastPosition); + swapItemsInVector(firstAttributes.dxValues(), lastAttributes.dxValues(), firstPosition, lastPosition); + swapItemsInVector(firstAttributes.dyValues(), lastAttributes.dyValues(), firstPosition, lastPosition); + swapItemsInVector(firstAttributes.rotateValues(), lastAttributes.rotateValues(), firstPosition, lastPosition); +} + +static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes>& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext, + SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last) +{ + first = 0; + last = 0; + + unsigned attributesSize = attributes.size(); + for (unsigned i = 0; i < attributesSize; ++i) { + SVGTextLayoutAttributes& current = attributes.at(i); + if (!first && firstContext == current.context()) + first = ¤t; + if (!last && lastContext == current.context()) + last = ¤t; + if (first && last) + break; + } + + ASSERT(first); + ASSERT(last); +} + +static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last) +{ + ASSERT(userData); + Vector<SVGTextLayoutAttributes>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes>*>(userData); + + // This is a copy of std::reverse(first, last). It additionally assure that the value lists within the InlineBoxes are reordered as well. + while (true) { + if (first == last || first == --last) + return; + + ASSERT((*first)->isSVGInlineTextBox()); + ASSERT((*last)->isSVGInlineTextBox()); + + SVGInlineTextBox* firstTextBox = static_cast<SVGInlineTextBox*>(*first); + SVGInlineTextBox* lastTextBox = static_cast<SVGInlineTextBox*>(*last); + + // Reordering is only necessary for BiDi text that is _absolutely_ positioned. + if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) { + RenderSVGInlineText* firstContext = toRenderSVGInlineText(firstTextBox->textRenderer()); + RenderSVGInlineText* lastContext = toRenderSVGInlineText(lastTextBox->textRenderer()); + + SVGTextLayoutAttributes* firstAttributes = 0; + SVGTextLayoutAttributes* lastAttributes = 0; + findFirstAndLastAttributesInVector(attributes, firstContext, lastContext, firstAttributes, lastAttributes); + + unsigned firstBoxPosition = firstTextBox->start(); + unsigned firstBoxEnd = firstTextBox->end(); + + unsigned lastBoxPosition = lastTextBox->start(); + unsigned lastBoxEnd = lastTextBox->end(); + for (; firstBoxPosition <= firstBoxEnd && lastBoxPosition <= lastBoxEnd; ++lastBoxPosition, ++firstBoxPosition) + swapItemsInLayoutAttributes(*firstAttributes, *lastAttributes, firstBoxPosition, lastBoxPosition); + } + + InlineBox* temp = *first; + *first = *last; + *last = temp; + + ++first; + } +} + +void SVGRootInlineBox::reorderValueLists(Vector<SVGTextLayoutAttributes>& attributes) +{ + Vector<InlineBox*> leafBoxesInLogicalOrder; + collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes); +} } // namespace WebCore diff --git a/Source/WebCore/rendering/svg/SVGRootInlineBox.h b/Source/WebCore/rendering/svg/SVGRootInlineBox.h index 2e073c9..39612e7 100644 --- a/Source/WebCore/rendering/svg/SVGRootInlineBox.h +++ b/Source/WebCore/rendering/svg/SVGRootInlineBox.h @@ -55,6 +55,7 @@ public: InlineBox* closestLeafChildForPosition(const IntPoint&); private: + void reorderValueLists(Vector<SVGTextLayoutAttributes>&); void layoutCharactersInTextBoxes(InlineFlowBox*, SVGTextLayoutEngine&); void layoutChildBoxes(InlineFlowBox*); void layoutRootBox(); diff --git a/Source/WebCore/rendering/svg/SVGShadowTreeElements.cpp b/Source/WebCore/rendering/svg/SVGShadowTreeElements.cpp index 2d84c48..9ccdef0 100644 --- a/Source/WebCore/rendering/svg/SVGShadowTreeElements.cpp +++ b/Source/WebCore/rendering/svg/SVGShadowTreeElements.cpp @@ -47,6 +47,10 @@ FloatSize SVGShadowTreeContainerElement::containerTranslation() const return FloatSize(m_xOffset.value(this), m_yOffset.value(this)); } +PassRefPtr<Element> SVGShadowTreeContainerElement::cloneElementWithoutAttributesAndChildren() const +{ + return adoptRef(new SVGShadowTreeContainerElement(document())); +} // SVGShadowTreeRootElement inline SVGShadowTreeRootElement::SVGShadowTreeRootElement(Document* document, SVGUseElement* shadowParent) diff --git a/Source/WebCore/rendering/svg/SVGShadowTreeElements.h b/Source/WebCore/rendering/svg/SVGShadowTreeElements.h index 3406f95..2952e35 100644 --- a/Source/WebCore/rendering/svg/SVGShadowTreeElements.h +++ b/Source/WebCore/rendering/svg/SVGShadowTreeElements.h @@ -44,6 +44,7 @@ protected: SVGShadowTreeContainerElement(Document*); private: + virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const; virtual bool isShadowTreeContainerElement() const { return true; } SVGLength m_xOffset; diff --git a/Source/WebCore/rendering/svg/SVGTextChunk.cpp b/Source/WebCore/rendering/svg/SVGTextChunk.cpp index 5dea6ad..49407e5 100644 --- a/Source/WebCore/rendering/svg/SVGTextChunk.cpp +++ b/Source/WebCore/rendering/svg/SVGTextChunk.cpp @@ -27,10 +27,8 @@ namespace WebCore { -SVGTextChunk::SVGTextChunk(bool isVerticalText, ETextAnchor textAnchor, SVGTextContentElement::SVGLengthAdjustType lengthAdjust, float desiredTextLength) - : m_isVerticalText(isVerticalText) - , m_textAnchor(textAnchor) - , m_lengthAdjust(lengthAdjust) +SVGTextChunk::SVGTextChunk(unsigned chunkStyle, float desiredTextLength) + : m_chunkStyle(chunkStyle) , m_desiredTextLength(desiredTextLength) { } @@ -52,7 +50,7 @@ void SVGTextChunk::calculateLength(float& length, unsigned& characters) const SVGTextFragment& fragment = fragments.at(i); characters += fragment.length; - if (m_isVerticalText) + if (m_chunkStyle & VerticalText) length += fragment.height; else length += fragment.width; @@ -63,7 +61,7 @@ void SVGTextChunk::calculateLength(float& length, unsigned& characters) const } // Resepect gap between chunks. - if (m_isVerticalText) + if (m_chunkStyle & VerticalText) length += fragment.y - (lastFragment->y + lastFragment->height); else length += fragment.x - (lastFragment->x + lastFragment->width); @@ -75,17 +73,11 @@ void SVGTextChunk::calculateLength(float& length, unsigned& characters) const float SVGTextChunk::calculateTextAnchorShift(float length) const { - switch (m_textAnchor) { - case TA_START: - return 0; - case TA_MIDDLE: + if (m_chunkStyle & MiddleAnchor) return -length / 2; - case TA_END: - return -length; - }; - - ASSERT_NOT_REACHED(); - return 0; + if (m_chunkStyle & EndAnchor) + return m_chunkStyle & RightToLeftText ? 0 : -length; + return m_chunkStyle & RightToLeftText ? -length : 0; } } // namespace WebCore diff --git a/Source/WebCore/rendering/svg/SVGTextChunk.h b/Source/WebCore/rendering/svg/SVGTextChunk.h index ebe6d81..9618d9f 100644 --- a/Source/WebCore/rendering/svg/SVGTextChunk.h +++ b/Source/WebCore/rendering/svg/SVGTextChunk.h @@ -31,34 +31,37 @@ 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); + enum ChunkStyle { + DefaultStyle = 1 << 0, + MiddleAnchor = 1 << 1, + EndAnchor = 1 << 2, + RightToLeftText = 1 << 3, + VerticalText = 1 << 4, + LengthAdjustSpacing = 1 << 5, + LengthAdjustSpacingAndGlyphs = 1 << 6 + }; + + SVGTextChunk(unsigned chunkStyle, 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; } + bool isVerticalText() const { return m_chunkStyle & VerticalText; } 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; } + bool hasDesiredTextLength() const { return m_desiredTextLength > 0 && ((m_chunkStyle & LengthAdjustSpacing) || (m_chunkStyle & LengthAdjustSpacingAndGlyphs)); } + bool hasTextAnchor() const { return m_chunkStyle & RightToLeftText ? !(m_chunkStyle & EndAnchor) : (m_chunkStyle & MiddleAnchor) || (m_chunkStyle & EndAnchor); } + bool hasLengthAdjustSpacing() const { return m_chunkStyle & LengthAdjustSpacing; } + bool hasLengthAdjustSpacingAndGlyphs() const { return m_chunkStyle & LengthAdjustSpacingAndGlyphs; } 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; + unsigned m_chunkStyle; float m_desiredTextLength; }; diff --git a/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp b/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp index bbbae6c..47311bd 100644 --- a/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp +++ b/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp @@ -101,15 +101,47 @@ void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxe const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); - SVGTextContentElement::SVGLengthAdjustType lengthAdjust = SVGTextContentElement::LENGTHADJUST_UNKNOWN; + // Build chunk style flags. + unsigned chunkStyle = SVGTextChunk::DefaultStyle; + + // Handle 'direction' property. + if (!style->isLeftToRightDirection()) + chunkStyle |= SVGTextChunk::RightToLeftText; + + // Handle 'writing-mode' property. + if (svgStyle->isVerticalWritingMode()) + chunkStyle |= SVGTextChunk::VerticalText; + + // Handle 'text-anchor' property. + switch (svgStyle->textAnchor()) { + case TA_START: + break; + case TA_MIDDLE: + chunkStyle |= SVGTextChunk::MiddleAnchor; + break; + case TA_END: + chunkStyle |= SVGTextChunk::EndAnchor; + break; + }; + + // Handle 'lengthAdjust' property. float desiredTextLength = 0; - if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textRenderer->parent())) { - lengthAdjust = static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust()); desiredTextLength = textContentElement->textLength().value(textContentElement); + + switch (static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust())) { + case SVGTextContentElement::LENGTHADJUST_UNKNOWN: + break; + case SVGTextContentElement::LENGTHADJUST_SPACING: + chunkStyle |= SVGTextChunk::LengthAdjustSpacing; + break; + case SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS: + chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs; + break; + }; } - SVGTextChunk chunk(svgStyle->isVerticalWritingMode(), svgStyle->textAnchor(), lengthAdjust, desiredTextLength); + SVGTextChunk chunk(chunkStyle, desiredTextLength); Vector<SVGInlineTextBox*>& boxes = chunk.boxes(); for (unsigned i = boxStart; i < boxStart + boxCount; ++i) @@ -137,7 +169,7 @@ void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk) bool isVerticalText = chunk.isVerticalText(); if (processTextLength) { - if (chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACING) { + if (chunk.hasLengthAdjustSpacing()) { float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters; unsigned atCharacter = 0; for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { @@ -147,7 +179,7 @@ void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk) processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter); } } else { - ASSERT(chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS); + ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs()); float scale = chunk.desiredTextLength() / chunkLength; AffineTransform spacingAndGlyphsTransform; @@ -172,7 +204,7 @@ void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk) 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) { + if (processTextLength && chunk.hasLengthAdjustSpacing()) { chunkLength = 0; chunkCharacters = 0; chunk.calculateLength(chunkLength, chunkCharacters); diff --git a/Source/WebCore/rendering/svg/SVGTextFragment.h b/Source/WebCore/rendering/svg/SVGTextFragment.h index 2e520da..b5b3c57 100644 --- a/Source/WebCore/rendering/svg/SVGTextFragment.h +++ b/Source/WebCore/rendering/svg/SVGTextFragment.h @@ -28,7 +28,8 @@ namespace WebCore { // A SVGTextFragment describes a text fragment of a RenderSVGInlineText which can be rendered at once. struct SVGTextFragment { SVGTextFragment() - : positionListOffset(0) + : characterOffset(0) + , metricsListOffset(0) , length(0) , x(0) , y(0) @@ -37,8 +38,9 @@ struct SVGTextFragment { { } - // The first rendered character starts at RenderSVGInlineText::characters() + positionListOffset. - unsigned positionListOffset; + // The first rendered character starts at RenderSVGInlineText::characters() + characterOffset. + unsigned characterOffset; + unsigned metricsListOffset; unsigned length; float x; diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp index 3037b77..4c227b4 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp +++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp @@ -27,7 +27,8 @@ namespace WebCore { -SVGTextLayoutAttributes::SVGTextLayoutAttributes() +SVGTextLayoutAttributes::SVGTextLayoutAttributes(RenderSVGInlineText* context) + : m_context(context) { } @@ -65,6 +66,8 @@ static inline void dumpLayoutVector(const Vector<float>& values) void SVGTextLayoutAttributes::dump() const { + fprintf(stderr, "context: %p\n", m_context); + fprintf(stderr, "x values: "); dumpLayoutVector(m_xValues); fprintf(stderr, "\n"); diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h index d08d5b7..fc6bd10 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h +++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h @@ -27,15 +27,19 @@ namespace WebCore { +class RenderSVGInlineText; + class SVGTextLayoutAttributes { public: - SVGTextLayoutAttributes(); + SVGTextLayoutAttributes(RenderSVGInlineText* context = 0); void reserveCapacity(unsigned length); void dump() const; static float emptyValue(); + RenderSVGInlineText* context() const { return m_context; } + Vector<float>& xValues() { return m_xValues; } const Vector<float>& xValues() const { return m_xValues; } @@ -55,6 +59,7 @@ public: const Vector<SVGTextMetrics>& textMetricsValues() const { return m_textMetricsValues; } private: + RenderSVGInlineText* m_context; Vector<float> m_xValues; Vector<float> m_yValues; Vector<float> m_dxValues; diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp index 3122912..86be424 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp +++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp @@ -52,9 +52,11 @@ void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextSubtree(RenderS buildOutermostLayoutScope(textRoot, atCharacter); // Propagate layout attributes to each RenderSVGInlineText object. + Vector<SVGTextLayoutAttributes>& allAttributes = textRoot->layoutAttributes(); + allAttributes.clear(); atCharacter = 0; lastCharacter = '\0'; - propagateLayoutAttributes(textRoot, atCharacter, lastCharacter); + propagateLayoutAttributes(textRoot, allAttributes, atCharacter, lastCharacter); } static inline void extractFloatValuesFromSVGLengthList(SVGElement* lengthContext, const SVGLengthList& list, Vector<float>& floatValues, unsigned textContentLength) @@ -186,7 +188,7 @@ void SVGTextLayoutAttributesBuilder::buildOutermostLayoutScope(RenderSVGText* te m_scopes.prepend(scope); } -void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter) const +void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const { for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { if (child->isSVGInlineText()) { @@ -195,16 +197,29 @@ void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* sta unsigned textLength = text->textLength(); bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style()); - SVGTextLayoutAttributes attributes; + SVGTextLayoutAttributes attributes(text); attributes.reserveCapacity(textLength); unsigned valueListPosition = atCharacter; unsigned metricsLength = 1; + SVGTextMetrics lastMetrics = SVGTextMetrics::emptyMetrics(); + for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) { const UChar& currentCharacter = characters[textPosition]; - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1); - metricsLength = metrics.length(); + SVGTextMetrics startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 1); + SVGTextMetrics currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1); + + // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken + // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping. + // So whenever runWidthAdvance != currentMetrics.width(), we are processing a text run whose length is + // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated. + float runWidthAdvance = startToCurrentMetrics.width() - lastMetrics.width(); + if (runWidthAdvance != currentMetrics.width()) + currentMetrics.setWidth(runWidthAdvance); + + lastMetrics = startToCurrentMetrics; + metricsLength = currentMetrics.length(); if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) { assignEmptyLayoutAttributesForCharacter(attributes); @@ -212,7 +227,7 @@ void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* sta continue; } - assignLayoutAttributesForCharacter(attributes, metrics, valueListPosition); + assignLayoutAttributesForCharacter(attributes, currentMetrics, valueListPosition); if (metricsLength > 1) { for (unsigned i = 0; i < metricsLength - 1; ++i) @@ -225,10 +240,12 @@ void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* sta #if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0 fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter); + fprintf(stderr, "BiDi properties: unicode-bidi=%i, block direction=%i\n", text->style()->unicodeBidi(), text->style()->direction()); attributes.dump(); #endif text->storeLayoutAttributes(attributes); + allAttributes.append(attributes); atCharacter = valueListPosition; continue; } @@ -236,7 +253,7 @@ void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* sta if (!child->isSVGInline()) continue; - propagateLayoutAttributes(child, atCharacter, lastCharacter); + propagateLayoutAttributes(child, allAttributes, atCharacter, lastCharacter); } } diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h index c68185b..b368c51 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h +++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h @@ -60,7 +60,7 @@ private: 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; + void propagateLayoutAttributes(RenderObject*, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const; enum LayoutValueType { XValueAttribute, diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp index c25ed79..71db2ea 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp +++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp @@ -34,8 +34,13 @@ namespace WebCore { -SVGTextLayoutEngine::SVGTextLayoutEngine() - : m_x(0) +SVGTextLayoutEngine::SVGTextLayoutEngine(Vector<SVGTextLayoutAttributes>& layoutAttributes) + : m_layoutAttributes(layoutAttributes) + , m_logicalCharacterOffset(0) + , m_logicalMetricsListOffset(0) + , m_visualCharacterOffset(0) + , m_visualMetricsListOffset(0) + , m_x(0) , m_y(0) , m_dx(0) , m_dy(0) @@ -46,6 +51,7 @@ SVGTextLayoutEngine::SVGTextLayoutEngine() , m_textPathSpacing(0) , m_textPathScaling(1) { + ASSERT(!m_layoutAttributes.isEmpty()); } void SVGTextLayoutEngine::updateCharacerPositionIfNeeded(float& x, float& y) @@ -77,7 +83,7 @@ void SVGTextLayoutEngine::updateCurrentTextPosition(float x, float y, float glyp } } -void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues, unsigned positionListOffset) +void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues) { // Update relative positioning information. if (dxValues.isEmpty() && dyValues.isEmpty()) @@ -85,14 +91,14 @@ void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float float dx = 0; if (!dxValues.isEmpty()) { - float& dxCurrent = dxValues.at(positionListOffset); + float& dxCurrent = dxValues.at(m_logicalCharacterOffset); if (dxCurrent != SVGTextLayoutAttributes::emptyValue()) dx = dxCurrent; } float dy = 0; if (!dyValues.isEmpty()) { - float& dyCurrent = dyValues.at(positionListOffset); + float& dyCurrent = dyValues.at(m_logicalCharacterOffset); if (dyCurrent != SVGTextLayoutAttributes::emptyValue()) dy = dyCurrent; } @@ -113,23 +119,31 @@ void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float m_dy = dy; } -void SVGTextLayoutEngine::recordTextFragment(SVGInlineTextBox* textBox, RenderSVGInlineText* text, unsigned positionListOffset, const SVGTextMetrics& lastCharacterMetrics) +void SVGTextLayoutEngine::recordTextFragment(SVGInlineTextBox* textBox, Vector<SVGTextMetrics>& textMetricsValues) { ASSERT(!m_currentTextFragment.length); + ASSERT(m_visualMetricsListOffset > 0); // Figure out length of fragment. - m_currentTextFragment.length = positionListOffset - m_currentTextFragment.positionListOffset; + m_currentTextFragment.length = m_visualCharacterOffset - m_currentTextFragment.characterOffset; // 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(); + SVGTextMetrics& lastCharacterMetrics = textMetricsValues.at(m_visualMetricsListOffset - 1); + m_currentTextFragment.width = lastCharacterMetrics.width(); + m_currentTextFragment.height = lastCharacterMetrics.height(); + + if (m_currentTextFragment.length > 1) { + // SVGTextLayoutAttributesBuilder assures that the length of the range is equal to the sum of the individual lengths of the glyphs. + float length = 0; + if (m_isVerticalText) { + for (unsigned i = m_currentTextFragment.metricsListOffset; i < m_visualMetricsListOffset; ++i) + length += textMetricsValues.at(i).height(); + m_currentTextFragment.height = length; + } else { + for (unsigned i = m_currentTextFragment.metricsListOffset; i < m_visualMetricsListOffset; ++i) + length += textMetricsValues.at(i).width(); + m_currentTextFragment.width = length; + } } textBox->textFragments().append(m_currentTextFragment); @@ -281,7 +295,7 @@ void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& b 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, " textBox properties, start=%i, len=%i, box direction=%i\n", textBox->start(), textBox->len(), textBox->direction()); fprintf(stderr, " textRenderer properties, textLength=%i\n", textBox->textRenderer()->textLength()); const UChar* characters = textBox->textRenderer()->characters(); @@ -289,9 +303,9 @@ void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& b 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()); + String fragmentString(characters + fragment.characterOffset, fragment.length); + fprintf(stderr, " -> Fragment %i, x=%lf, y=%lf, width=%lf, height=%lf, characterOffset=%i, length=%i, characters='%s'\n" + , i, fragment.x, fragment.y, fragment.width, fragment.height, fragment.characterOffset, fragment.length, fragmentString.utf8().data()); } } #endif @@ -328,6 +342,97 @@ void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& b boxes.clear(); } +bool SVGTextLayoutEngine::currentLogicalCharacterAttributes(SVGTextLayoutAttributes& logicalAttributes) +{ + logicalAttributes = m_layoutAttributes.first(); + if (m_logicalCharacterOffset != logicalAttributes.xValues().size()) + return true; + + m_layoutAttributes.remove(0); + if (m_layoutAttributes.isEmpty()) + return false; + + logicalAttributes = m_layoutAttributes.first(); + m_logicalMetricsListOffset = 0; + m_logicalCharacterOffset = 0; + return true; +} + +bool SVGTextLayoutEngine::currentLogicalCharacterMetrics(SVGTextLayoutAttributes& logicalAttributes, SVGTextMetrics& logicalMetrics) +{ + logicalMetrics = SVGTextMetrics::emptyMetrics(); + + Vector<SVGTextMetrics>& textMetricsValues = logicalAttributes.textMetricsValues(); + unsigned textMetricsSize = textMetricsValues.size(); + while (true) { + if (m_logicalMetricsListOffset == textMetricsSize) { + if (!currentLogicalCharacterAttributes(logicalAttributes)) + return false; + + textMetricsValues = logicalAttributes.textMetricsValues(); + textMetricsSize = textMetricsValues.size(); + continue; + } + + logicalMetrics = textMetricsValues.at(m_logicalMetricsListOffset); + if (logicalMetrics == SVGTextMetrics::emptyMetrics() || (!logicalMetrics.width() && !logicalMetrics.height())) { + advanceToNextLogicalCharacter(logicalMetrics); + continue; + } + + // Stop if we found the next valid logical text metrics object. + return true; + } + + ASSERT_NOT_REACHED(); + return true; +} + +bool SVGTextLayoutEngine::currentVisualCharacterMetrics(SVGInlineTextBox* textBox, RenderSVGInlineText* text, SVGTextMetrics& metrics) +{ + SVGTextLayoutAttributes& attributes = text->layoutAttributes(); + Vector<SVGTextMetrics>& textMetricsValues = attributes.textMetricsValues(); + ASSERT(!textMetricsValues.isEmpty()); + + unsigned textMetricsSize = textMetricsValues.size(); + unsigned boxStart = textBox->start(); + unsigned boxLength = textBox->len(); + + if (m_visualMetricsListOffset == textMetricsSize) + return false; + + while (m_visualMetricsListOffset < textMetricsSize) { + SVGTextMetrics& visualMetrics = textMetricsValues.at(m_visualMetricsListOffset); + + // Advance to text box start location. + if (m_visualCharacterOffset < boxStart) { + advanceToNextVisualCharacter(visualMetrics); + continue; + } + + // Stop if we've finished processing this text box. + if (m_visualCharacterOffset >= boxStart + boxLength) + return false; + + metrics = visualMetrics; + return true; + } + + return false; +} + +void SVGTextLayoutEngine::advanceToNextLogicalCharacter(const SVGTextMetrics& logicalMetrics) +{ + ++m_logicalMetricsListOffset; + m_logicalCharacterOffset += logicalMetrics.length(); +} + +void SVGTextLayoutEngine::advanceToNextVisualCharacter(const SVGTextMetrics& visualMetrics) +{ + ++m_visualMetricsListOffset; + m_visualCharacterOffset += visualMetrics.length(); +} + void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, RenderSVGInlineText* text, const RenderStyle* style) { SVGElement* lengthContext = static_cast<SVGElement*>(text->parent()->node()); @@ -338,26 +443,10 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend 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()); + m_visualMetricsListOffset = 0; + m_visualCharacterOffset = 0; - if (boxLength > textMetricsSize) - textMetricsSize = boxLength; - - unsigned positionListOffset = 0; - unsigned metricsListOffset = 0; + Vector<SVGTextMetrics>& textMetricsValues = text->layoutAttributes().textMetricsValues(); const UChar* characters = text->characters(); const Font& font = style->font(); @@ -372,56 +461,63 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend 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(); + while (true) { + // Find the start of the current text box in this list, respecting ligatures. + SVGTextMetrics visualMetrics = SVGTextMetrics::emptyMetrics(); + if (!currentVisualCharacterMetrics(textBox, text, visualMetrics)) + break; + + if (visualMetrics == SVGTextMetrics::emptyMetrics()) { + advanceToNextVisualCharacter(visualMetrics); continue; } - // Stop if we've finished processing this text box. - if (positionListOffset >= boxStart + boxLength) + SVGTextLayoutAttributes logicalAttributes; + if (!currentLogicalCharacterAttributes(logicalAttributes)) 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)); + SVGTextMetrics logicalMetrics = SVGTextMetrics::emptyMetrics(); + if (!currentLogicalCharacterMetrics(logicalAttributes, logicalMetrics)) + break; - if (metrics == SVGTextMetrics::emptyMetrics()) { - positionListOffset += metrics.length(); - continue; - } + Vector<float>& xValues = logicalAttributes.xValues(); + Vector<float>& yValues = logicalAttributes.yValues(); + Vector<float>& dxValues = logicalAttributes.dxValues(); + Vector<float>& dyValues = logicalAttributes.dyValues(); + Vector<float>& rotateValues = logicalAttributes.rotateValues(); + + float x = xValues.at(m_logicalCharacterOffset); + float y = yValues.at(m_logicalCharacterOffset); + + // When we've advanced to the box start offset, determine using the original x/y values, + // whether this character starts a new text chunk, before doing any further processing. + if (m_visualCharacterOffset == textBox->start()) + textBox->setStartsNewTextChunk(logicalAttributes.context()->characterStartsNewTextChunk(m_logicalCharacterOffset)); - const UChar* currentCharacter = characters + positionListOffset; float angle = 0; if (!rotateValues.isEmpty()) { - float newAngle = rotateValues.at(positionListOffset); + float newAngle = rotateValues.at(m_logicalCharacterOffset); if (newAngle != SVGTextLayoutAttributes::emptyValue()) angle = newAngle; } // Calculate glyph orientation angle. + const UChar* currentCharacter = characters + m_visualCharacterOffset; 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); + float glyphAdvance = baselineLayout.calculateGlyphAdvanceAndOrientation(m_isVerticalText, visualMetrics, 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); + updateRelativePositionAdjustmentsIfNeeded(dxValues, dyValues); // Calculate SVG Fonts kerning, if needed. - float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, metrics.glyph()); + float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, visualMetrics.glyph()); // Calculate CSS 'kerning', 'letter-spacing' and 'word-spacing' for next character, if needed. float spacing = spacingLayout.calculateCSSKerningAndSpacing(svgStyle, lengthContext, currentCharacter); @@ -461,7 +557,8 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend // Skip character, if we're before the path. if (textPathOffset < 0) { - positionListOffset += metrics.length(); + advanceToNextLogicalCharacter(logicalMetrics); + advanceToNextVisualCharacter(visualMetrics); continue; } @@ -513,16 +610,17 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend // If we already started a fragment, close it now. if (didStartTextFragment && shouldStartNewFragment) { applySpacingToNextCharacter = false; - recordTextFragment(textBox, text, positionListOffset, textMetricsValues.at(metricsListOffset - 1)); + recordTextFragment(textBox, textMetricsValues); } // Eventually start a new fragment, if not yet done. if (!didStartTextFragment || shouldStartNewFragment) { - ASSERT(!m_currentTextFragment.positionListOffset); + ASSERT(!m_currentTextFragment.characterOffset); ASSERT(!m_currentTextFragment.length); didStartTextFragment = true; - m_currentTextFragment.positionListOffset = positionListOffset; + m_currentTextFragment.characterOffset = m_visualCharacterOffset; + m_currentTextFragment.metricsListOffset = m_visualMetricsListOffset; m_currentTextFragment.x = x; m_currentTextFragment.y = y; @@ -563,7 +661,8 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend updateCurrentTextPosition(xNew, yNew, glyphAdvance + spacing); } - positionListOffset += metrics.length(); + advanceToNextLogicalCharacter(logicalMetrics); + advanceToNextVisualCharacter(visualMetrics); lastAngle = angle; } @@ -571,7 +670,7 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend return; // Close last open fragment, if needed. - recordTextFragment(textBox, text, positionListOffset, textMetricsValues.at(metricsListOffset - 1)); + recordTextFragment(textBox, textMetricsValues); } } diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h index 631e4cd..22dd59b 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h +++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h @@ -24,6 +24,7 @@ #include "Path.h" #include "SVGTextChunkBuilder.h" #include "SVGTextFragment.h" +#include "SVGTextLayoutAttributes.h" #include "SVGTextMetrics.h" #include <wtf/Vector.h> @@ -47,7 +48,7 @@ class SVGRenderStyle; class SVGTextLayoutEngine { WTF_MAKE_NONCOPYABLE(SVGTextLayoutEngine); public: - SVGTextLayoutEngine(); + SVGTextLayoutEngine(Vector<SVGTextLayoutAttributes>&); SVGTextChunkBuilder& chunkLayoutBuilder() { return m_chunkLayoutBuilder; } void beginTextPathLayout(RenderObject*, SVGTextLayoutEngine& lineLayout); @@ -59,20 +60,32 @@ public: 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 updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues); - void recordTextFragment(SVGInlineTextBox*, RenderSVGInlineText*, unsigned positionListOffset, const SVGTextMetrics& lastCharacterMetrics); + void recordTextFragment(SVGInlineTextBox*, Vector<SVGTextMetrics>& textMetricValues); bool parentDefinesTextLength(RenderObject*) const; void layoutTextOnLineOrPath(SVGInlineTextBox*, RenderSVGInlineText*, const RenderStyle*); void finalizeTransformMatrices(Vector<SVGInlineTextBox*>&); + bool currentLogicalCharacterAttributes(SVGTextLayoutAttributes&); + bool currentLogicalCharacterMetrics(SVGTextLayoutAttributes&, SVGTextMetrics&); + bool currentVisualCharacterMetrics(SVGInlineTextBox*, RenderSVGInlineText*, SVGTextMetrics&); + + void advanceToNextLogicalCharacter(const SVGTextMetrics&); + void advanceToNextVisualCharacter(const SVGTextMetrics&); + private: + Vector<SVGTextLayoutAttributes> m_layoutAttributes; Vector<SVGInlineTextBox*> m_lineLayoutBoxes; Vector<SVGInlineTextBox*> m_pathLayoutBoxes; SVGTextChunkBuilder m_chunkLayoutBuilder; SVGTextFragment m_currentTextFragment; + unsigned m_logicalCharacterOffset; + unsigned m_logicalMetricsListOffset; + unsigned m_visualCharacterOffset; + unsigned m_visualMetricsListOffset; float m_x; float m_y; float m_dx; diff --git a/Source/WebCore/rendering/svg/SVGTextMetrics.cpp b/Source/WebCore/rendering/svg/SVGTextMetrics.cpp index d75bdb3..042bc22 100644 --- a/Source/WebCore/rendering/svg/SVGTextMetrics.cpp +++ b/Source/WebCore/rendering/svg/SVGTextMetrics.cpp @@ -74,7 +74,17 @@ SVGTextMetrics SVGTextMetrics::emptyMetrics() static TextRun constructTextRun(RenderSVGInlineText* text, const UChar* characters, unsigned position, unsigned length) { - TextRun run(characters + position, length); + RenderStyle* style = text->style(); + ASSERT(style); + + TextRun run(characters + position + , length + , false /* allowTabs */ + , 0 /* xPos, only relevant with allowTabs=true */ + , 0 /* padding, only relevant for justified text, not relevant for SVG */ + , TextRun::AllowTrailingExpansion + , !style->isLeftToRightDirection() + , style->unicodeBidi() == Override /* directionalOverride */); #if ENABLE(SVG_FONTS) run.setReferencingRenderObject(text); @@ -92,22 +102,6 @@ SVGTextMetrics SVGTextMetrics::measureCharacterRange(RenderSVGInlineText* text, return SVGTextMetrics(text, run, position, text->textLength()); } -void SVGTextMetrics::measureAllCharactersIndividually(RenderSVGInlineText* text, Vector<SVGTextMetrics>& allMetrics) -{ - ASSERT(text); - 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(text, run, position, text->textLength()); - allMetrics.append(metrics); - position += metrics.length(); - } -} - } #endif // ENABLE(SVG) diff --git a/Source/WebCore/rendering/svg/SVGTextMetrics.h b/Source/WebCore/rendering/svg/SVGTextMetrics.h index 7ef0f7d..cb016b6 100644 --- a/Source/WebCore/rendering/svg/SVGTextMetrics.h +++ b/Source/WebCore/rendering/svg/SVGTextMetrics.h @@ -32,7 +32,6 @@ class SVGTextMetrics { public: static SVGTextMetrics emptyMetrics(); static SVGTextMetrics measureCharacterRange(RenderSVGInlineText*, unsigned position, unsigned length); - static void measureAllCharactersIndividually(RenderSVGInlineText*, Vector<SVGTextMetrics>&); bool operator==(const SVGTextMetrics&); @@ -62,6 +61,10 @@ public: const Glyph& glyph() const { return m_glyph; } private: + friend class SVGTextLayoutAttributesBuilder; + void setWidth(float width) { m_width = width; } + +private: SVGTextMetrics(); SVGTextMetrics(RenderSVGInlineText*, const TextRun&, unsigned position, unsigned textLength); diff --git a/Source/WebCore/rendering/svg/SVGTextQuery.cpp b/Source/WebCore/rendering/svg/SVGTextQuery.cpp index 1a4cdab..5f3523e 100644 --- a/Source/WebCore/rendering/svg/SVGTextQuery.cpp +++ b/Source/WebCore/rendering/svg/SVGTextQuery.cpp @@ -313,7 +313,7 @@ bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGTextFragmen if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) return false; - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset + startPosition, endPosition - startPosition); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, endPosition - startPosition); float fragmentLength = queryData->isVerticalText ? metrics.height() : metrics.width(); data->subStringLength += mapLengthThroughFragmentTransformation(fragment, queryData->isVerticalText, fragmentLength); @@ -353,7 +353,7 @@ bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGTe data->startPosition = FloatPoint(fragment.x, fragment.y); if (startPosition) { - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition); if (queryData->isVerticalText) data->startPosition.move(0, metrics.height()); else @@ -399,7 +399,7 @@ bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGText data->endPosition = FloatPoint(fragment.x, fragment.y); - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition + 1); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition + 1); if (queryData->isVerticalText) data->endPosition.move(0, metrics.height()); else @@ -478,14 +478,14 @@ static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const extent.setLocation(FloatPoint(fragment.x, fragment.y - queryData->textRenderer->scaledFont().fontMetrics().floatAscent() / scalingFactor)); if (startPosition) { - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, 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); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, 1); extent.setSize(FloatSize(metrics.width(), metrics.height())); if (fragment.transform.isIdentity()) |