diff options
author | Steve Block <steveblock@google.com> | 2010-02-02 14:57:50 +0000 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-02-04 15:06:55 +0000 |
commit | d0825bca7fe65beaee391d30da42e937db621564 (patch) | |
tree | 7461c49eb5844ffd1f35d1ba2c8b7584c1620823 /WebCore/rendering | |
parent | 3db770bd97c5a59b6c7574ca80a39e5a51c1defd (diff) | |
download | external_webkit-d0825bca7fe65beaee391d30da42e937db621564.zip external_webkit-d0825bca7fe65beaee391d30da42e937db621564.tar.gz external_webkit-d0825bca7fe65beaee391d30da42e937db621564.tar.bz2 |
Merge webkit.org at r54127 : Initial merge by git
Change-Id: Ib661abb595522f50ea406f72d3a0ce17f7193c82
Diffstat (limited to 'WebCore/rendering')
124 files changed, 4316 insertions, 1709 deletions
diff --git a/WebCore/rendering/AutoTableLayout.cpp b/WebCore/rendering/AutoTableLayout.cpp index 0e001c7..2a4958a 100644 --- a/WebCore/rendering/AutoTableLayout.cpp +++ b/WebCore/rendering/AutoTableLayout.cpp @@ -382,7 +382,7 @@ int AutoTableLayout::calcEffectiveWidth() float spanMax = max(maxWidth, cMaxWidth); tMaxWidth = max(tMaxWidth, spanMax * 100 * percentScaleFactor / w.rawValue()); - // all non percent columns in the span get percent vlaues to sum up correctly. + // all non percent columns in the span get percent values to sum up correctly. int percentMissing = w.rawValue() - totalPercent; float totalWidth = 0; for (unsigned int pos = col; pos < lastCol; pos++) { @@ -668,8 +668,8 @@ void AutoTableLayout::layout() } } - // if we have overallocated, reduce every cell according to the difference between desired width and minwidth - // this seems to produce to the pixel exaxt results with IE. Wonder is some of this also holds for width distributing. + // If we have overallocated, reduce every cell according to the difference between desired width and minwidth + // this seems to produce to the pixel exact results with IE. Wonder is some of this also holds for width distributing. if (available < 0) { // Need to reduce cells with the following prioritization: // (1) Auto diff --git a/WebCore/rendering/CounterNode.cpp b/WebCore/rendering/CounterNode.cpp index 95a3748..c164c81 100644 --- a/WebCore/rendering/CounterNode.cpp +++ b/WebCore/rendering/CounterNode.cpp @@ -22,20 +22,14 @@ #include "config.h" #include "CounterNode.h" +#include "RenderCounter.h" #include "RenderObject.h" #include <stdio.h> -// FIXME: There's currently no strategy for getting the counter tree updated when new -// elements with counter-reset and counter-increment styles are added to the render tree. -// Also, the code can't handle changes where an existing node needs to change into a -// "reset" node, or from a "reset" node back to not a "reset" node. As of this writing, -// at least some of these problems manifest as failures in the t1204-increment and -// t1204-reset tests in the CSS 2.1 test suite. - namespace WebCore { -CounterNode::CounterNode(RenderObject* o, bool isReset, int value) - : m_isReset(isReset) +CounterNode::CounterNode(RenderObject* o, bool hasResetType, int value) + : m_hasResetType(hasResetType) , m_value(value) , m_countInParent(0) , m_renderer(o) @@ -52,18 +46,14 @@ CounterNode* CounterNode::nextInPreOrderAfterChildren(const CounterNode* stayWit if (this == stayWithin) return 0; - CounterNode* next = m_nextSibling; - if (next) - return next; - next = m_parent; - while (next && !next->m_nextSibling) { - if (next == stayWithin) + const CounterNode* current = this; + CounterNode* next; + while (!(next = current->m_nextSibling)) { + current = current->m_parent; + if (!current || current == stayWithin) return 0; - next = next->m_parent; } - if (next) - return next->m_nextSibling; - return 0; + return next; } CounterNode* CounterNode::nextInPreOrder(const CounterNode* stayWithin) const @@ -100,14 +90,13 @@ CounterNode* CounterNode::previousInPreOrder() const int CounterNode::computeCountInParent() const { - int increment = m_isReset ? 0 : m_value; + int increment = actsAsReset() ? 0 : m_value; if (m_previousSibling) return m_previousSibling->m_countInParent + increment; ASSERT(m_parent->m_firstChild == this); return m_parent->m_value + increment; } - void CounterNode::resetRenderer(const AtomicString& identifier) const { if (!m_renderer || m_renderer->documentBeingDestroyed()) @@ -145,6 +134,11 @@ void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild, cons ASSERT(!newChild->m_nextSibling); ASSERT(!refChild || refChild->m_parent == this); + if (newChild->m_hasResetType) { + while (m_lastChild != refChild) + RenderCounter::destroyCounterNode(m_lastChild->renderer(), identifier); + } + CounterNode* next; if (refChild) { @@ -155,21 +149,57 @@ void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild, cons m_firstChild = newChild; } - if (next) { - ASSERT(next->m_previousSibling == refChild); - next->m_previousSibling = newChild; - } else { - ASSERT(m_lastChild == refChild); - m_lastChild = newChild; - } - newChild->m_parent = this; newChild->m_previousSibling = refChild; - newChild->m_nextSibling = next; - newChild->m_countInParent = newChild->computeCountInParent(); + if (!newChild->m_firstChild || newChild->m_hasResetType) { + newChild->m_nextSibling = next; + if (next) { + ASSERT(next->m_previousSibling == refChild); + next->m_previousSibling = newChild; + } else { + ASSERT(m_lastChild == refChild); + m_lastChild = newChild; + } + + newChild->m_countInParent = newChild->computeCountInParent(); + newChild->resetRenderers(identifier); + if (next) + next->recount(identifier); + return; + } + + // The code below handles the case when a formerly root increment counter is loosing its root position + // and therefore its children become next siblings. + CounterNode* last = newChild->m_lastChild; + CounterNode* first = newChild->m_firstChild; + + newChild->m_nextSibling = first; + first->m_previousSibling = newChild; + // The case when the original next sibling of the inserted node becomes a child of + // one of the former children of the inserted node is not handled as it is believed + // to be impossible since: + // 1. if the increment counter node lost it's root position as a result of another + // counter node being created, it will be inserted as the last child so next is null. + // 2. if the increment counter node lost it's root position as a result of a renderer being + // inserted into the document's render tree, all its former children counters are attached + // to children of the inserted renderer and hence cannot be in scope for counter nodes + // attached to renderers that were already in the document's render tree. + last->m_nextSibling = next; if (next) - next->recount(identifier); + next->m_previousSibling = last; + else + m_lastChild = last; + for (next = first; ; next = next->m_nextSibling) { + next->m_parent = this; + if (last == next) + break; + } + newChild->m_firstChild = 0; + newChild->m_lastChild = 0; + newChild->m_countInParent = newChild->computeCountInParent(); + newChild->resetRenderer(identifier); + first->recount(identifier); } void CounterNode::removeChild(CounterNode* oldChild, const AtomicString& identifier) @@ -216,7 +246,7 @@ static void showTreeAndMark(const CounterNode* node) for (const CounterNode* parent = current; parent && parent != root; parent = parent->parent()) fwrite(" ", 1, 2, stderr); fprintf(stderr, "%p %s: %d %d P:%p PS:%p NS:%p R:%p\n", - current, current->isReset() ? "reset____" : "increment", current->value(), + current, current->actsAsReset() ? "reset____" : "increment", current->value(), current->countInParent(), current->parent(), current->previousSibling(), current->nextSibling(), current->renderer()); } diff --git a/WebCore/rendering/CounterNode.h b/WebCore/rendering/CounterNode.h index 8081dc6..15f2eb8 100644 --- a/WebCore/rendering/CounterNode.h +++ b/WebCore/rendering/CounterNode.h @@ -42,7 +42,8 @@ class CounterNode : public Noncopyable { public: CounterNode(RenderObject*, bool isReset, int value); - bool isReset() const { return m_isReset; } + 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; } @@ -58,15 +59,24 @@ public: CounterNode* nextInPreOrderAfterChildren(const CounterNode* stayWithin = 0) const; void insertAfter(CounterNode* newChild, CounterNode* beforeChild, const AtomicString& identifier); + + // identifier must match the identifier of this counter. void removeChild(CounterNode*, const AtomicString& identifier); private: 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; - bool m_isReset; + bool m_hasResetType; int m_value; int m_countInParent; RenderObject* m_renderer; diff --git a/WebCore/rendering/EllipsisBox.cpp b/WebCore/rendering/EllipsisBox.cpp index bea9d73..6ec3195 100644 --- a/WebCore/rendering/EllipsisBox.cpp +++ b/WebCore/rendering/EllipsisBox.cpp @@ -23,6 +23,7 @@ #include "Document.h" #include "GraphicsContext.h" #include "HitTestResult.h" +#include "RootInlineBox.h" namespace WebCore { @@ -40,9 +41,22 @@ void EllipsisBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) setShadow = true; } + if (selectionState() != RenderObject::SelectionNone) { + paintSelection(context, tx, ty, style, style->font()); + + // Select the correct color for painting the text. + Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor(); + if (foreground.isValid() && foreground != textColor) + context->setFillColor(foreground, style->colorSpace()); + } + const String& str = m_str; context->drawText(style->font(), TextRun(str.characters(), str.length(), false, 0, 0, false, style->visuallyOrdered()), IntPoint(m_x + tx, m_y + ty + style->font().ascent())); + // Restore the regular fill color. + if (textColor != context->fillColor()) + context->setFillColor(textColor, style->colorSpace()); + if (setShadow) context->clearShadow(); @@ -54,6 +68,35 @@ void EllipsisBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) } } +IntRect EllipsisBox::selectionRect(int tx, int ty) +{ + RenderStyle* style = m_renderer->style(m_firstLine); + const Font& f = style->font(); + return enclosingIntRect(f.selectionRectForText(TextRun(m_str.characters(), m_str.length(), false, 0, 0, false, style->visuallyOrdered()), + IntPoint(m_x + tx, m_y + ty + root()->selectionTop()), root()->selectionHeight())); +} + +void EllipsisBox::paintSelection(GraphicsContext* context, int tx, int ty, RenderStyle* style, const Font& font) +{ + Color textColor = style->color(); + Color c = m_renderer->selectionBackgroundColor(); + if (!c.isValid() || !c.alpha()) + return; + + // If the text color ends up being the same as the selection background, invert the selection + // background. + if (textColor == c) + c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); + + context->save(); + int y = root()->selectionTop(); + int h = root()->selectionHeight(); + context->clip(IntRect(m_x + tx, y + ty, m_width, h)); + context->drawHighlightForText(font, TextRun(m_str.characters(), m_str.length(), false, 0, 0, false, style->visuallyOrdered()), + IntPoint(m_x + tx, m_y + ty + y), h, c, style->colorSpace()); + context->restore(); +} + bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) { tx += m_x; diff --git a/WebCore/rendering/EllipsisBox.h b/WebCore/rendering/EllipsisBox.h index a228d7a..087fc72 100644 --- a/WebCore/rendering/EllipsisBox.h +++ b/WebCore/rendering/EllipsisBox.h @@ -35,18 +35,24 @@ public: , m_height(height) , m_str(ellipsisStr) , m_markupBox(markupBox) + , m_selectionState(RenderObject::SelectionNone) { } virtual void paint(RenderObject::PaintInfo&, int tx, int ty); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + void setSelectionState(RenderObject::SelectionState s) { m_selectionState = s; } + IntRect selectionRect(int tx, int ty); private: virtual int height() const { return m_height; } + virtual RenderObject::SelectionState selectionState() { return m_selectionState; } + void paintSelection(GraphicsContext*, int tx, int ty, RenderStyle*, const Font&); int m_height; AtomicString m_str; InlineBox* m_markupBox; + RenderObject::SelectionState m_selectionState; }; } // namespace WebCore diff --git a/WebCore/rendering/InlineFlowBox.cpp b/WebCore/rendering/InlineFlowBox.cpp index 2bd1683..34eec30 100644 --- a/WebCore/rendering/InlineFlowBox.cpp +++ b/WebCore/rendering/InlineFlowBox.cpp @@ -429,7 +429,7 @@ void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositi } lineHeight = baseline + baselineToBottom; } else if (parentLineHeight.isPercent()) { - lineHeight = parentLineHeight.calcMinValue(curr->renderer()->style()->fontSize(), true); + lineHeight = parentLineHeight.calcMinValue(curr->renderer()->style()->fontSize()); baseline = 0; for (size_t i = 0; i < usedFonts.size(); ++i) { int halfLeading = (lineHeight - usedFonts[i]->ascent() - usedFonts[i]->descent()) / 2; @@ -532,9 +532,6 @@ void InlineFlowBox::computeVerticalOverflow(int lineTop, int lineBottom, bool st // Any spillage outside of the line top and bottom is not considered overflow. We just ignore this, since it only happens // from the "your ascent/descent don't affect the line" quirk. - // FIXME: Technically this means there can be repaint errors in the case where a line box has a shadow or background that spills - // outside of the block. We should consider making any line box that has anything to render just stop respecting the quirk or making - // boxes that render something set visual overflow. int topOverflow = max(y(), lineTop); int bottomOverflow = min(y() + boxHeight, lineBottom); @@ -735,13 +732,24 @@ void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) return; - // Move x/y to our coordinates. - tx += m_x; - ty += m_y; - + int x = m_x; + int y = m_y; int w = width(); int h = height(); + // Constrain our background/border painting to the line top and bottom if necessary. + bool strictMode = renderer()->document()->inStrictMode(); + if (!hasTextChildren() && !strictMode) { + RootInlineBox* rootBox = root(); + int bottom = min(rootBox->lineBottom(), y + h); + y = max(rootBox->lineTop(), y); + h = bottom - y; + } + + // Move x/y to our coordinates. + tx += x; + ty += y; + GraphicsContext* context = paintInfo.context; // You can use p::first-line to specify a background. If so, the root line boxes for @@ -800,13 +808,24 @@ void InlineFlowBox::paintMask(RenderObject::PaintInfo& paintInfo, int tx, int ty if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; - // Move x/y to our coordinates. - tx += m_x; - ty += m_y; - + int x = m_x; + int y = m_y; int w = width(); int h = height(); + // Constrain our background/border painting to the line top and bottom if necessary. + bool strictMode = renderer()->document()->inStrictMode(); + if (!hasTextChildren() && !strictMode) { + RootInlineBox* rootBox = root(); + int bottom = min(rootBox->lineBottom(), y + h); + y = max(rootBox->lineTop(), y); + h = bottom - y; + } + + // Move x/y to our coordinates. + tx += x; + ty += y; + const NinePieceImage& maskNinePieceImage = renderer()->style()->maskBoxImage(); StyleImage* maskBoxImage = renderer()->style()->maskBoxImage().image(); diff --git a/WebCore/rendering/InlineTextBox.cpp b/WebCore/rendering/InlineTextBox.cpp index 31e6967..b7e6de2 100644 --- a/WebCore/rendering/InlineTextBox.cpp +++ b/WebCore/rendering/InlineTextBox.cpp @@ -23,9 +23,11 @@ #include "config.h" #include "InlineTextBox.h" +#include "Chrome.h" #include "ChromeClient.h" #include "Document.h" #include "Editor.h" +#include "EllipsisBox.h" #include "Frame.h" #include "GraphicsContext.h" #include "HitTestResult.h" @@ -81,6 +83,23 @@ RenderObject::SelectionState InlineTextBox::selectionState() else if (state == RenderObject::SelectionBoth) state = RenderObject::SelectionNone; } + + // If there are ellipsis following, make sure their selection is updated. + if (m_truncation != cNoTruncation && root()->ellipsisBox()) { + EllipsisBox* ellipsis = root()->ellipsisBox(); + if (state != RenderObject::SelectionNone) { + int start, end; + selectionStartEnd(start, end); + // The ellipsis should be considered to be selected if the end of + // the selection is past the beginning of the truncation and the + // beginning of the selection is before or at the beginning of the + // truncation. + ellipsis->setSelectionState(end >= m_truncation && start <= m_truncation ? + RenderObject::SelectionInside : RenderObject::SelectionNone); + } else + ellipsis->setSelectionState(RenderObject::SelectionNone); + } + return state; } @@ -89,7 +108,7 @@ IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos) int sPos = max(startPos - m_start, 0); int ePos = min(endPos - m_start, (int)m_len); - if (sPos >= ePos) + if (sPos > ePos) return IntRect(); RenderText* textObj = textRenderer(); @@ -254,7 +273,7 @@ bool InlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, in return false; } -static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, int startOffset, int endOffset, const IntPoint& textOrigin, int x, int y, int w, int h, ShadowData* shadow, bool stroked) +static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, int startOffset, int endOffset, int truncationPoint, const IntPoint& textOrigin, int x, int y, int w, int h, ShadowData* shadow, bool stroked) { Color fillColor = context->fillColor(); ColorSpace fillColorSpace = context->fillColorSpace(); @@ -289,8 +308,8 @@ static void paintTextWithShadows(GraphicsContext* context, const Font& font, con else { if (endOffset > 0) context->drawText(font, textRun, textOrigin + extraOffset, 0, endOffset); - if (startOffset < textRun.length()) - context->drawText(font, textRun, textOrigin + extraOffset, startOffset); + if (startOffset < truncationPoint) + context->drawText(font, textRun, textOrigin + extraOffset, startOffset, truncationPoint); } if (!shadow) @@ -460,6 +479,13 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) if (paintSelectedTextOnly || paintSelectedTextSeparately) selectionStartEnd(sPos, ePos); + int length = m_len; + if (m_truncation != cNoTruncation) { + sPos = min<int>(sPos, m_truncation); + ePos = min<int>(ePos, m_truncation); + length = m_truncation; + } + if (!paintSelectedTextOnly) { // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side // effect, so only when we know we're stroking, do a save/restore. @@ -469,9 +495,9 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); if (!paintSelectedTextSeparately || ePos <= sPos) { // FIXME: Truncate right-to-left text correctly. - paintTextWithShadows(context, font, textRun, 0, m_truncation == cNoTruncation ? m_len : m_truncation, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); + paintTextWithShadows(context, font, textRun, 0, length, length, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); } else - paintTextWithShadows(context, font, textRun, ePos, sPos, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); + paintTextWithShadows(context, font, textRun, ePos, sPos, length, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); if (textStrokeWidth > 0) context->restore(); @@ -483,7 +509,7 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) context->save(); updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth, styleToUse->colorSpace()); - paintTextWithShadows(context, font, textRun, sPos, ePos, textOrigin, m_x + tx, m_y + ty, width(), height(), selectionShadow, selectionStrokeWidth > 0); + paintTextWithShadows(context, font, textRun, sPos, ePos, length, textOrigin, m_x + tx, m_y + ty, width(), height(), selectionShadow, selectionStrokeWidth > 0); if (selectionStrokeWidth > 0) context->restore(); @@ -565,8 +591,11 @@ void InlineTextBox::paintSelection(GraphicsContext* context, int tx, int ty, Ren updateGraphicsContext(context, c, c, 0, style->colorSpace()); // Don't draw text at all! int y = selectionTop(); int h = selectionHeight(); + // If the text is truncated, let the thing being painted in the truncation + // draw its own highlight. + int length = m_truncation != cNoTruncation ? m_truncation : m_len; context->clip(IntRect(m_x + tx, y + ty, m_width, h)); - context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, + context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, length, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), IntPoint(m_x + tx, y + ty), h, c, style->colorSpace(), sPos, ePos); context->restore(); diff --git a/WebCore/rendering/InlineTextBox.h b/WebCore/rendering/InlineTextBox.h index 80af2e3..0a83ddd 100644 --- a/WebCore/rendering/InlineTextBox.h +++ b/WebCore/rendering/InlineTextBox.h @@ -62,6 +62,8 @@ public: void setFallbackFonts(const HashSet<const SimpleFontData*>&); void takeFallbackFonts(Vector<const SimpleFontData*>&); + unsigned short truncation() { return m_truncation; } + private: virtual int selectionTop(); virtual int selectionHeight(); diff --git a/WebCore/rendering/RenderArena.cpp b/WebCore/rendering/RenderArena.cpp index 3ab4dff..41f33de 100644 --- a/WebCore/rendering/RenderArena.cpp +++ b/WebCore/rendering/RenderArena.cpp @@ -114,7 +114,7 @@ void RenderArena::free(size_t size, void* ptr) // Use standard free so that memory debugging tools work. RenderArenaDebugHeader* header = static_cast<RenderArenaDebugHeader*>(ptr) - 1; ASSERT(header->signature == signature); - ASSERT(header->size == size); + ASSERT_UNUSED(size, header->size == size); ASSERT(header->arena == this); header->signature = signatureDead; ::free(header); diff --git a/WebCore/rendering/RenderBR.cpp b/WebCore/rendering/RenderBR.cpp index 012a433..340d6b7 100644 --- a/WebCore/rendering/RenderBR.cpp +++ b/WebCore/rendering/RenderBR.cpp @@ -62,7 +62,7 @@ int RenderBR::lineHeight(bool firstLine, bool /*isRootLineBox*/) const return s->font().lineSpacing(); } if (lh.isPercent()) - return lh.calcMinValue(s->fontSize(), true); + return lh.calcMinValue(s->fontSize()); return lh.value(); } diff --git a/WebCore/rendering/RenderBlock.cpp b/WebCore/rendering/RenderBlock.cpp index 4d60569..bffade7 100644 --- a/WebCore/rendering/RenderBlock.cpp +++ b/WebCore/rendering/RenderBlock.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -415,6 +415,31 @@ void RenderBlock::moveChildTo(RenderObject* to, RenderObjectChildList* toChildLi toChildList->insertChildNode(to, children()->removeChildNode(this, child, false), beforeChild, false); } +void RenderBlock::moveAllChildrenTo(RenderObject* to, RenderObjectChildList* toChildList) +{ + RenderObject* nextChild = children()->firstChild(); + while (nextChild) { + RenderObject* child = nextChild; + nextChild = child->nextSibling(); + toChildList->appendChildNode(to, children()->removeChildNode(this, child, false), false); + } +} + +void RenderBlock::moveAllChildrenTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* beforeChild) +{ + ASSERT(!beforeChild || to == beforeChild->parent()); + if (!beforeChild) { + moveAllChildrenTo(to, toChildList); + return; + } + RenderObject* nextChild = children()->firstChild(); + while (nextChild) { + RenderObject* child = nextChild; + nextChild = child->nextSibling(); + toChildList->insertChildNode(to, children()->removeChildNode(this, child, false), beforeChild, false); + } +} + void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) { // makeChildrenNonInline takes a block whose children are *all* inline and it @@ -521,20 +546,12 @@ void RenderBlock::removeChild(RenderObject* oldChild) // Take all the children out of the |next| block and put them in // the |prev| block. prev->setNeedsLayoutAndPrefWidthsRecalc(); - RenderObject* o = next->firstChild(); - RenderBlock* nextBlock = toRenderBlock(next); RenderBlock* prevBlock = toRenderBlock(prev); - while (o) { - RenderObject* no = o; - o = no->nextSibling(); - nextBlock->moveChildTo(prevBlock, prevBlock->children(), no); - } - + nextBlock->moveAllChildrenTo(prevBlock, prevBlock->children()); + // Delete the now-empty block's lines and nuke it. nextBlock->deleteLineBoxTree(); - - // Nuke the now-empty block. - next->destroy(); + nextBlock->destroy(); } RenderBox::removeChild(oldChild); @@ -547,17 +564,15 @@ void RenderBlock::removeChild(RenderObject* oldChild) setNeedsLayoutAndPrefWidthsRecalc(); RenderBlock* anonBlock = toRenderBlock(children()->removeChildNode(this, child, false)); setChildrenInline(true); - RenderObject* o = anonBlock->firstChild(); - while (o) { - RenderObject* no = o; - o = no->nextSibling(); - anonBlock->moveChildTo(this, children(), no); - } - + anonBlock->moveAllChildrenTo(this, children()); // Delete the now-empty block's lines and nuke it. anonBlock->deleteLineBoxTree(); anonBlock->destroy(); } + + // If this was our last child be sure to clear out our line boxes. + if (childrenInline() && !firstChild()) + lineBoxes()->deleteLineBoxes(renderArena()); } bool RenderBlock::isSelfCollapsingBlock() const @@ -1698,7 +1713,7 @@ void RenderBlock::paintCaret(PaintInfo& paintInfo, int tx, int ty, CaretType typ offsetForContents(tx, ty); if (type == CursorCaret) - document()->frame()->paintCaret(paintInfo.context, tx, ty, paintInfo.rect); + document()->frame()->selection()->paintCaret(paintInfo.context, tx, ty, paintInfo.rect); else document()->frame()->paintDragCaret(paintInfo.context, tx, ty, paintInfo.rect); } @@ -1965,8 +1980,11 @@ void RenderBlock::paintSelection(PaintInfo& paintInfo, int tx, int ty) IntRect gapRectsBounds = fillSelectionGaps(this, tx, ty, tx, ty, lastTop, lastLeft, lastRight, &paintInfo); if (!gapRectsBounds.isEmpty()) { if (RenderLayer* layer = enclosingLayer()) { - IntSize offset = hasLayer() ? IntSize() : offsetFromAncestorContainer(layer->renderer()); - gapRectsBounds.move(offset - IntSize(tx, ty)); + gapRectsBounds.move(IntSize(-tx, -ty)); + if (!hasLayer()) { + FloatRect localBounds(gapRectsBounds); + gapRectsBounds = localToContainerQuad(localBounds, layer->renderer()).enclosingBoundingBox(); + } layer->addBlockSelectionGapsBounds(gapRectsBounds); } } @@ -2655,6 +2673,9 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) return bottom; + + if (!firstChild() && (!width() || !height())) + return bottom; if (!hasColumns()) { // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. @@ -2747,6 +2768,9 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) return right; + if (!firstChild() && (!width() || !height())) + return right; + if (!hasColumns()) { // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. // For now, we have to descend into all the children, since we may have a huge abs div inside @@ -2841,6 +2865,9 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) return left; + if (!firstChild() && (!width() || !height())) + return left; + if (!hasColumns()) { // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. // For now, we have to descend into all the children, since we may have a huge abs div inside @@ -2938,7 +2965,7 @@ RenderBlock::rightBottom() return bottom; } -void RenderBlock::markLinesDirtyInVerticalRange(int top, int bottom) +void RenderBlock::markLinesDirtyInVerticalRange(int top, int bottom, RootInlineBox* highest) { if (top >= bottom) return; @@ -2950,7 +2977,7 @@ void RenderBlock::markLinesDirtyInVerticalRange(int top, int bottom) lowestDirtyLine = lowestDirtyLine->prevRootBox(); } - while (afterLowest && afterLowest->blockHeight() >= top) { + while (afterLowest && afterLowest != highest && afterLowest->blockHeight() >= top) { afterLowest->markDirty(); afterLowest = afterLowest->prevRootBox(); } @@ -3214,18 +3241,35 @@ int RenderBlock::getClearDelta(RenderBox* child, int yPos) } // We also clear floats if we are too big to sit on the same line as a float (and wish to avoid floats by default). - // FIXME: Note that the remaining space checks aren't quite accurate, since you should be able to clear only some floats (the minimum # needed - // to fit) and not all (we should be using nextFloatBottomBelow and looping). int result = clearSet ? max(0, bottom - yPos) : 0; if (!result && child->avoidsFloats()) { - int oldYPos = child->y(); - int oldWidth = child->width(); - child->setY(yPos); - child->calcWidth(); - if (child->width() > lineWidth(yPos, false) && child->minPrefWidth() <= availableWidth()) - result = max(0, floatBottom() - yPos); - child->setY(oldYPos); - child->setWidth(oldWidth); + int availableWidth = this->availableWidth(); + if (child->minPrefWidth() > availableWidth) + return 0; + + int y = yPos; + while (true) { + int widthAtY = lineWidth(y, false); + if (widthAtY == availableWidth) + return y - yPos; + + int oldChildY = child->y(); + int oldChildWidth = child->width(); + child->setY(y); + child->calcWidth(); + int childWidthAtY = child->width(); + child->setY(oldChildY); + child->setWidth(oldChildWidth); + + if (childWidthAtY <= widthAtY) + return y - yPos; + + y = nextFloatBottomBelow(y); + ASSERT(y >= yPos); + if (y < yPos) + break; + } + ASSERT_NOT_REACHED(); } return result; } @@ -5018,7 +5062,7 @@ IntRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, int* return IntRect(x, y, caretWidth, height); } -void RenderBlock::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) { // For blocks inside inlines, we go ahead and include margins so that we run right up to the // inline boxes above and below us (thus getting merged with them to form a single irregular @@ -5030,16 +5074,19 @@ void RenderBlock::addFocusRingRects(GraphicsContext* graphicsContext, int tx, in bool prevInlineHasLineBox = toRenderInline(inlineContinuation()->node()->renderer())->firstLineBox(); int topMargin = prevInlineHasLineBox ? collapsedMarginTop() : 0; int bottomMargin = nextInlineHasLineBox ? collapsedMarginBottom() : 0; - graphicsContext->addFocusRingRect(IntRect(tx, ty - topMargin, - width(), height() + topMargin + bottomMargin)); - } else - graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); + IntRect rect(tx, ty - topMargin, width(), height() + topMargin + bottomMargin); + if (!rect.isEmpty()) + rects.append(rect); + } else if (width() && height()) + rects.append(IntRect(tx, ty, width(), height())); if (!hasOverflowClip() && !hasControlClip()) { for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { int top = max(curr->lineTop(), curr->y()); int bottom = min(curr->lineBottom(), curr->y() + curr->height()); - graphicsContext->addFocusRingRect(IntRect(tx + curr->x(), ty + top, curr->width(), bottom - top)); + IntRect rect(tx + curr->x(), ty + top, curr->width(), bottom - top); + if (!rect.isEmpty()) + rects.append(rect); } for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { @@ -5051,13 +5098,13 @@ void RenderBlock::addFocusRingRects(GraphicsContext* graphicsContext, int tx, in pos = curr->localToAbsolute(); else pos = FloatPoint(tx + box->x(), ty + box->y()); - box->addFocusRingRects(graphicsContext, pos.x(), pos.y()); + box->addFocusRingRects(rects, pos.x(), pos.y()); } } } if (inlineContinuation()) - inlineContinuation()->addFocusRingRects(graphicsContext, + inlineContinuation()->addFocusRingRects(rects, tx - x() + inlineContinuation()->containingBlock()->x(), ty - y() + inlineContinuation()->containingBlock()->y()); } diff --git a/WebCore/rendering/RenderBlock.h b/WebCore/rendering/RenderBlock.h index 985074d..884695a 100644 --- a/WebCore/rendering/RenderBlock.h +++ b/WebCore/rendering/RenderBlock.h @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -34,7 +34,6 @@ namespace WebCore { class InlineIterator; class RenderInline; -class RootInlineBox; struct BidiRun; @@ -142,6 +141,8 @@ public: protected: void moveChildTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* child); void moveChildTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* beforeChild, RenderObject* child); + void moveAllChildrenTo(RenderObject* to, RenderObjectChildList* toChildList); + void moveAllChildrenTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* beforeChild); int maxTopPosMargin() const { return m_maxMargin ? m_maxMargin->m_topPos : MaxMargin::topPosDefault(this); } int maxTopNegMargin() const { return m_maxMargin ? m_maxMargin->m_topNeg : MaxMargin::topNegDefault(this); } @@ -363,12 +364,12 @@ private: virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); void adjustPointToColumnContents(IntPoint&) const; void adjustForBorderFit(int x, int& left, int& right) const; // Helper function for borderFitAdjust - void markLinesDirtyInVerticalRange(int top, int bottom); + void markLinesDirtyInVerticalRange(int top, int bottom, RootInlineBox* highest = 0); void newLine(EClear); @@ -528,6 +529,10 @@ private: RenderLineBoxList m_lineBoxes; // All of the root line boxes created for this block flow. For example, <div>Hello<br>world.</div> will have two total lines for the <div>. mutable int m_lineHeight; + + // RenderRubyBase objects need to be able to split and merge, moving their children around + // (calling moveChildTo, moveAllChildrenTo, and makeChildrenNonInline). + friend class RenderRubyBase; }; inline RenderBlock* toRenderBlock(RenderObject* object) diff --git a/WebCore/rendering/RenderBlockLineLayout.cpp b/WebCore/rendering/RenderBlockLineLayout.cpp index e0d712c..c76d963 100644 --- a/WebCore/rendering/RenderBlockLineLayout.cpp +++ b/WebCore/rendering/RenderBlockLineLayout.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2000 Lars Knoll (knoll@kde.org) - * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009 Apple Inc. All right reserved. + * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. All right reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -29,6 +30,7 @@ #include "RenderInline.h" #include "RenderListMarker.h" #include "RenderView.h" +#include "TrailingFloatsRootInlineBox.h" #include "break_lines.h" #include <wtf/AlwaysInline.h> #include <wtf/RefCountedLeakCounter.h> @@ -830,7 +832,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // Figure out if we should clear out our line boxes. // FIXME: Handle resize eventually! - bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren; + bool fullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren; if (fullLayout) lineBoxes()->deleteLineBoxes(renderArena()); @@ -882,6 +884,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i bool endOfInline = false; RenderObject* o = bidiFirst(this, 0, false); Vector<FloatWithRect> floats; + bool hasInlineChild = false; while (o) { if (o->isReplaced() || o->isFloating() || o->isPositioned()) { RenderBox* box = toRenderBox(o); @@ -909,6 +912,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i o->layoutIfNeeded(); } } else if (o->isText() || (o->isRenderInline() && !endOfInline)) { + hasInlineChild = true; if (fullLayout || o->selfNeedsLayout()) dirtyLineBoxesForRenderer(o, fullLayout); o->setNeedsLayout(false); @@ -988,7 +992,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i bool previousLineBrokeCleanly = true; RootInlineBox* startLine = determineStartPosition(firstLine, fullLayout, previousLineBrokeCleanly, resolver, floats, floatIndex); - if (fullLayout && !selfNeedsLayout()) { + if (fullLayout && hasInlineChild && !selfNeedsLayout()) { setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like // we're supposed to. RenderView* v = view(); @@ -1229,6 +1233,16 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // In case we have a float on the last line, it might not be positioned up to now. // This has to be done before adding in the bottom border/padding, or the float will // include the padding incorrectly. -dwh + if (checkForFloatsFromLastLine) { + int bottomVisualOverflow = lastRootBox()->bottomVisualOverflow(); + int bottomLayoutOverflow = lastRootBox()->bottomLayoutOverflow(); + TrailingFloatsRootInlineBox* trailingFloatsLineBox = new (renderArena()) TrailingFloatsRootInlineBox(this); + m_lineBoxes.appendLineBox(trailingFloatsLineBox); + trailingFloatsLineBox->setConstructed(); + trailingFloatsLineBox->verticallyAlignBoxes(height()); + trailingFloatsLineBox->setVerticalOverflowPositions(height(), bottomLayoutOverflow, height(), bottomVisualOverflow, 0); + trailingFloatsLineBox->setBlockHeight(height()); + } if (lastFloat) { for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) { } @@ -1291,7 +1305,7 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa if (floats[floatIndex].rect.size() != newSize) { int floatTop = floats[floatIndex].rect.y(); curr->markDirty(); - markLinesDirtyInVerticalRange(curr->blockHeight(), floatTop + max(floats[floatIndex].rect.height(), newSize.height())); + markLinesDirtyInVerticalRange(curr->blockHeight(), floatTop + max(floats[floatIndex].rect.height(), newSize.height()), curr); floats[floatIndex].rect.setSize(newSize); dirtiedByFloat = true; } @@ -1908,6 +1922,10 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool int wordSpacing = o->style()->wordSpacing(); int lastSpaceWordSpacing = 0; + // Non-zero only when kerning is enabled, in which case we measure words with their trailing + // space, then subtract its width. + int wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.spaceWidth() + wordSpacing : 0; + int wrapW = tmpW + inlineWidth(o, !appliedStartWidth, true); int charWidth = 0; bool breakNBSP = autoWrap && o->style()->nbspMode() == SPACE; @@ -2002,7 +2020,11 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } } - int additionalTmpW = textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + int additionalTmpW; + if (wordTrailingSpaceWidth && currentCharacterIsSpace) + additionalTmpW = textWidth(t, lastSpace, pos + 1 - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) - wordTrailingSpaceWidth + lastSpaceWordSpacing; + else + additionalTmpW = textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; tmpW += additionalTmpW; if (!appliedStartWidth) { tmpW += inlineWidth(o, true, false); @@ -2034,7 +2056,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } } if (lineWasTooWide || w + tmpW > width) { - if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && !toRenderText(lBreak.obj)->isWordBreak() && toRenderText(lBreak.obj)->characters()[lBreak.pos] == '\n') { + 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 (!stoppedIgnoringSpaces && pos > 0) { // We need to stop right before the newline and then start up again. addMidpoint(lineMidpointState, InlineIterator(0, o, pos - 1)); // Stop diff --git a/WebCore/rendering/RenderBox.cpp b/WebCore/rendering/RenderBox.cpp index a7c2e63..60230d4 100644 --- a/WebCore/rendering/RenderBox.cpp +++ b/WebCore/rendering/RenderBox.cpp @@ -26,6 +26,7 @@ #include "RenderBox.h" #include "CachedImage.h" +#include "Chrome.h" #include "ChromeClient.h" #include "Document.h" #include "FrameView.h" @@ -335,12 +336,12 @@ IntRect RenderBox::outlineBoundsForRepaint(RenderBoxModelObject* repaintContaine return box; } -void RenderBox::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +void RenderBox::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) { - graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); + if (width() && height()) + rects.append(IntRect(tx, ty, width(), height())); } - IntRect RenderBox::reflectionBox() const { IntRect result; @@ -568,13 +569,14 @@ void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty) { const FillLayer* bgLayer = style()->backgroundLayers(); Color bgColor = style()->backgroundColor(); + RenderObject* bodyObject = 0; if (!style()->hasBackground() && node() && node()->hasTagName(HTMLNames::htmlTag)) { // Locate the <body> element using the DOM. This is easier than trying // to crawl around a render tree with potential :before/:after content and // anonymous blocks created by inline <body> tags etc. We can locate the <body> // render object very easily via the DOM. HTMLElement* body = document()->body(); - RenderObject* bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; + bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; if (bodyObject) { bgLayer = bodyObject->style()->backgroundLayers(); bgColor = bodyObject->style()->backgroundColor(); @@ -602,7 +604,7 @@ void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty) int bw = max(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw); int bh = max(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh); - paintFillLayers(paintInfo, bgColor, bgLayer, bx, by, bw, bh); + paintFillLayers(paintInfo, bgColor, bgLayer, bx, by, bw, bh, CompositeSourceOver, bodyObject); if (style()->hasBorder() && style()->display() != INLINE) paintBorder(paintInfo.context, tx, ty, w, h, style()); @@ -670,9 +672,25 @@ void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int bool compositedMask = hasLayer() && layer()->hasCompositedMask(); CompositeOperator compositeOp = CompositeSourceOver; + bool allMaskImagesLoaded = true; + if (!compositedMask) { StyleImage* maskBoxImage = style()->maskBoxImage().image(); - if (maskBoxImage && style()->maskLayers()->hasImage()) { + const FillLayer* maskLayers = style()->maskLayers(); + + // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content. + if (maskBoxImage) + allMaskImagesLoaded &= maskBoxImage->isLoaded(); + + if (maskLayers) + allMaskImagesLoaded &= maskLayers->imagesAreLoaded(); + + // Before all images have loaded, just use an empty transparency layer as the mask. + if (!allMaskImagesLoaded) + pushTransparencyLayer = true; + + if (maskBoxImage && maskLayers->hasImage()) { + // We have a mask-box-image and mask-image, so need to composite them together before using the result as a mask. pushTransparencyLayer = true; } else { // We have to use an extra image buffer to hold the mask. Multiple mask images need @@ -682,7 +700,7 @@ void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int // and composite that buffer as the mask. // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering // before pushing the transparency layer. - for (const FillLayer* fillLayer = style()->maskLayers()->next(); fillLayer; fillLayer = fillLayer->next()) { + for (const FillLayer* fillLayer = maskLayers->next(); fillLayer; fillLayer = fillLayer->next()) { if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) { pushTransparencyLayer = true; // We found one image that can be used in rendering, exit the loop @@ -699,8 +717,10 @@ void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int } } - paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp); - paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp); + if (allMaskImagesLoaded) { + paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp); + paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp); + } if (pushTransparencyLayer) paintInfo.context->endTransparencyLayer(); @@ -725,18 +745,18 @@ IntRect RenderBox::maskClipRect() return result; } -void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op) +void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) { if (!fillLayer) return; - paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op); - paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op); + paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op, backgroundObject); + paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op, backgroundObject); } -void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op) +void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) { - paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, op); + paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, op, backgroundObject); } void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*) @@ -946,18 +966,20 @@ void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool } } - if (style()->position() == FixedPosition) - fixed = true; - bool containerSkipped; RenderObject* o = container(repaintContainer, &containerSkipped); if (!o) return; + bool isFixedPos = style()->position() == FixedPosition; bool hasTransform = hasLayer() && layer()->transform(); - if (hasTransform) - fixed = false; // Elements with transforms act as a containing block for fixed position descendants - + if (hasTransform) { + // If this box has a transform, it acts as a fixed position container for fixed descendants, + // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. + fixed &= isFixedPos; + } else + fixed |= isFixedPos; + IntSize containerOffset = offsetFromContainer(o); bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); @@ -984,12 +1006,14 @@ void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, Transfor // We don't expect absoluteToLocal() to be called during layout (yet) ASSERT(!view() || !view()->layoutStateEnabled()); - if (style()->position() == FixedPosition) - fixed = true; - + bool isFixedPos = style()->position() == FixedPosition; bool hasTransform = hasLayer() && layer()->transform(); - if (hasTransform) - fixed = false; // Elements with transforms act as a containing block for fixed position descendants + if (hasTransform) { + // If this box has a transform, it acts as a fixed position container for fixed descendants, + // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. + fixed &= isFixedPos; + } else + fixed |= isFixedPos; RenderObject* o = container(); if (!o) @@ -1968,7 +1992,7 @@ void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBoxModelO int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos) { // 'left' and 'right' cannot both be 'auto' because one would of been - // converted to the static postion already + // converted to the static position already ASSERT(!(left.isAuto() && right.isAuto())); int leftValue = 0; @@ -2002,7 +2026,7 @@ void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBoxModelO if (marginLeft.isAuto() && marginRight.isAuto()) { // Both margins auto, solve for equality if (availableSpace >= 0) { - marginLeftValue = availableSpace / 2; // split the diference + marginLeftValue = availableSpace / 2; // split the difference marginRightValue = availableSpace - marginLeftValue; // account for odd valued differences } else { // see FIXME 1 @@ -2287,7 +2311,7 @@ void RenderBox::calcAbsoluteVerticalValues(Length h, const RenderBoxModelObject* if (marginTop.isAuto() && marginBottom.isAuto()) { // Both margins auto, solve for equality // NOTE: This may result in negative values. - marginTopValue = availableSpace / 2; // split the diference + marginTopValue = availableSpace / 2; // split the difference marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences } else if (marginTop.isAuto()) { // Solve for top margin @@ -2365,7 +2389,7 @@ void RenderBox::calcAbsoluteVerticalValues(Length h, const RenderBoxModelObject* void RenderBox::calcAbsoluteHorizontalReplaced() { // The following is based off of the W3C Working Draft from April 11, 2006 of - // CSS 2.1: Section 10.3.8 "Absolutly positioned, replaced elements" + // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> // (block-style-comments in this function correspond to text from the spec and // the numbers correspond to numbers in spec) @@ -2456,7 +2480,7 @@ void RenderBox::calcAbsoluteHorizontalReplaced() int difference = availableSpace - (leftValue + rightValue); if (difference > 0) { - m_marginLeft = difference / 2; // split the diference + m_marginLeft = difference / 2; // split the difference m_marginRight = difference - m_marginLeft; // account for odd valued differences } else { // see FIXME 1 @@ -2543,7 +2567,7 @@ void RenderBox::calcAbsoluteHorizontalReplaced() void RenderBox::calcAbsoluteVerticalReplaced() { // The following is based off of the W3C Working Draft from April 11, 2006 of - // CSS 2.1: Section 10.6.5 "Absolutly positioned, replaced elements" + // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> // (block-style-comments in this function correspond to text from the spec and // the numbers correspond to numbers in spec) @@ -2607,7 +2631,7 @@ void RenderBox::calcAbsoluteVerticalReplaced() int bottomValue = 0; if (marginTop.isAuto() && marginBottom.isAuto()) { - // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combinded. + // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. ASSERT(!(top.isAuto() || bottom.isAuto())); topValue = top.calcValue(containerHeight); diff --git a/WebCore/rendering/RenderBox.h b/WebCore/rendering/RenderBox.h index c579123..11c65e8 100644 --- a/WebCore/rendering/RenderBox.h +++ b/WebCore/rendering/RenderBox.h @@ -74,7 +74,7 @@ public: // Bounds of the outline box in absolute coords. Respects transforms virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/) const; - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); // Use this with caution! No type checking is done! RenderBox* previousSiblingBox() const; @@ -305,8 +305,8 @@ protected: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); virtual void updateBoxModelInfoFromStyle(); - void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver); - void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver); + void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject); + void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver, RenderObject* backgroundObject = 0); void paintMaskImages(const PaintInfo&, int tx, int ty, int width, int height); diff --git a/WebCore/rendering/RenderBoxModelObject.cpp b/WebCore/rendering/RenderBoxModelObject.cpp index 98960e0..f2cd9bd 100644 --- a/WebCore/rendering/RenderBoxModelObject.cpp +++ b/WebCore/rendering/RenderBoxModelObject.cpp @@ -33,6 +33,7 @@ #include "RenderInline.h" #include "RenderLayer.h" #include "RenderView.h" +#include <wtf/CurrentTime.h> using namespace std; @@ -44,6 +45,140 @@ bool RenderBoxModelObject::s_wasFloating = false; bool RenderBoxModelObject::s_hadLayer = false; bool RenderBoxModelObject::s_layerWasSelfPainting = false; +static const double cInterpolationCutoff = 800. * 800.; +static const double cLowQualityTimeThreshold = 0.500; // 500 ms + +class RenderBoxModelScaleData : public Noncopyable { +public: + RenderBoxModelScaleData(RenderBoxModelObject* object, const IntSize& size, const TransformationMatrix& transform, double time, bool lowQualityScale) + : m_size(size) + , m_transform(transform) + , m_lastPaintTime(time) + , m_lowQualityScale(lowQualityScale) + , m_highQualityRepaintTimer(object, &RenderBoxModelObject::highQualityRepaintTimerFired) + { + } + + ~RenderBoxModelScaleData() + { + m_highQualityRepaintTimer.stop(); + } + + Timer<RenderBoxModelObject>& hiqhQualityRepaintTimer() { return m_highQualityRepaintTimer; } + + const IntSize& size() const { return m_size; } + void setSize(const IntSize& s) { m_size = s; } + double lastPaintTime() const { return m_lastPaintTime; } + void setLastPaintTime(double t) { m_lastPaintTime = t; } + bool useLowQualityScale() const { return m_lowQualityScale; } + const TransformationMatrix& transform() const { return m_transform; } + void setTransform(const TransformationMatrix& transform) { m_transform = transform; } + void setUseLowQualityScale(bool b) + { + m_highQualityRepaintTimer.stop(); + m_lowQualityScale = b; + if (b) + m_highQualityRepaintTimer.startOneShot(cLowQualityTimeThreshold); + } + +private: + IntSize m_size; + TransformationMatrix m_transform; + double m_lastPaintTime; + bool m_lowQualityScale; + Timer<RenderBoxModelObject> m_highQualityRepaintTimer; +}; + +class RenderBoxModelScaleObserver { +public: + static bool shouldPaintBackgroundAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const IntSize&); + + static void boxModelObjectDestroyed(RenderBoxModelObject* object) + { + if (gBoxModelObjects) { + RenderBoxModelScaleData* data = gBoxModelObjects->take(object); + delete data; + if (!gBoxModelObjects->size()) { + delete gBoxModelObjects; + gBoxModelObjects = 0; + } + } + } + + static void highQualityRepaintTimerFired(RenderBoxModelObject* object) + { + RenderBoxModelScaleObserver::boxModelObjectDestroyed(object); + object->repaint(); + } + + static HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>* gBoxModelObjects; +}; + +bool RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const IntSize& size) +{ + // If the image is not a bitmap image, then none of this is relevant and we just paint at high + // quality. + if (!image->isBitmapImage()) + return false; + + // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image + // is actually being scaled. + IntSize imageSize(image->width(), image->height()); + + // Look ourselves up in the hashtable. + RenderBoxModelScaleData* data = 0; + if (gBoxModelObjects) + data = gBoxModelObjects->get(object); + + const TransformationMatrix& currentTransform = context->getCTM(); + bool contextIsScaled = !currentTransform.isIdentityOrTranslation(); + if (!contextIsScaled && imageSize == size) { + // There is no scale in effect. If we had a scale in effect before, we can just delete this data. + if (data) { + gBoxModelObjects->remove(object); + delete data; + } + return false; + } + + // There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case. + if (object->document()->page()->inLowQualityImageInterpolationMode()) { + double totalPixels = static_cast<double>(image->width()) * static_cast<double>(image->height()); + if (totalPixels > cInterpolationCutoff) + return true; + } + + // If there is no data yet, we will paint the first scale at high quality and record the paint time in case a second scale happens + // very soon. + if (!data) { + data = new RenderBoxModelScaleData(object, size, currentTransform, currentTime(), false); + if (!gBoxModelObjects) + gBoxModelObjects = new HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>; + gBoxModelObjects->set(object, data); + return false; + } + + // We are scaled, but we painted already at this size, so just keep using whatever mode we last painted with. + if ((!contextIsScaled || data->transform() == currentTransform) && data->size() == size) + return data->useLowQualityScale(); + + // We have data and our size just changed. If this change happened quickly, go into low quality mode and then set a repaint + // timer to paint in high quality mode. Otherwise it is ok to just paint in high quality mode. + double newTime = currentTime(); + data->setUseLowQualityScale(newTime - data->lastPaintTime() < cLowQualityTimeThreshold); + data->setLastPaintTime(newTime); + data->setTransform(currentTransform); + data->setSize(size); + return data->useLowQualityScale(); +} + +HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>* RenderBoxModelScaleObserver::gBoxModelObjects = 0; + +void RenderBoxModelObject::highQualityRepaintTimerFired(Timer<RenderBoxModelObject>*) +{ + RenderBoxModelScaleObserver::highQualityRepaintTimerFired(this); +} + RenderBoxModelObject::RenderBoxModelObject(Node* node) : RenderObject(node) , m_layer(0) @@ -55,6 +190,7 @@ RenderBoxModelObject::~RenderBoxModelObject() // Our layer should have been destroyed and cleared by now ASSERT(!hasLayer()); ASSERT(!m_layer); + RenderBoxModelScaleObserver::boxModelObjectDestroyed(this); } void RenderBoxModelObject::destroyLayer() @@ -304,9 +440,12 @@ int RenderBoxModelObject::paddingRight(bool) const } -void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op) +void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op, RenderObject* backgroundObject) { GraphicsContext* context = paintInfo.context; + if (context->paintingDisabled()) + return; + bool includeLeftEdge = box ? box->includeLeftEdge() : true; bool includeRightEdge = box ? box->includeRightEdge() : true; int bLeft = includeLeftEdge ? borderLeft() : 0; @@ -463,21 +602,10 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co if (!destRect.isEmpty()) { phase += destRect.location() - destOrigin; CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; - RenderObject* clientForBackgroundImage = this; - // Check if this is the root element painting a background layer propagated from <body>, - // and pass the body's renderer as the client in that case. - if (isRoot && !style()->hasBackground()) { - ASSERT(node()->hasTagName(htmlTag)); - HTMLElement* body = document()->body(); - ASSERT(body); - ASSERT(body->hasLocalName(bodyTag)); - ASSERT(body->renderer()); - if (body) { - if (RenderObject* bodyRenderer = body->renderer()) - clientForBackgroundImage = bodyRenderer; - } - } - context->drawTiledImage(bg->image(clientForBackgroundImage, tileSize), style()->colorSpace(), destRect, phase, tileSize, compositeOp); + RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this; + Image* image = bg->image(clientForBackgroundImage, tileSize); + bool useLowQualityScaling = RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality(context, this, image, destRect.size()); + context->drawTiledImage(image, style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling); } } @@ -557,6 +685,17 @@ void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fil // Determine the background positioning area and set destRect to the background painting area. // destRect will be adjusted later if the background is non-repeating. bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment; + +#if ENABLE(FAST_MOBILE_SCROLLING) + if (view()->frameView() && view()->frameView()->canBlitOnScroll()) { + // As a side effect of an optimization to blit on scroll, we do not honor the CSS + // property "background-attachment: fixed" because it may result in rendering + // artifacts. Note, these artifacts only appear if we are blitting on scroll of + // a page that has fixed background images. + fixedAttachment = false; + } +#endif + if (!fixedAttachment) { destRect = IntRect(tx, ty, w, h); diff --git a/WebCore/rendering/RenderBoxModelObject.h b/WebCore/rendering/RenderBoxModelObject.h index c9a4a0a..db7538d 100644 --- a/WebCore/rendering/RenderBoxModelObject.h +++ b/WebCore/rendering/RenderBoxModelObject.h @@ -90,7 +90,7 @@ public: void paintBorder(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, bool begin = true, bool end = true); bool paintNinePieceImage(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, const NinePieceImage&, CompositeOperator = CompositeSourceOver); void paintBoxShadow(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, ShadowStyle, bool begin = true, bool end = true); - void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver); + void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver, RenderObject* backgroundObject = 0); // The difference between this inline's baseline position and the line's baseline position. int verticalPosition(bool firstLine) const; @@ -98,6 +98,8 @@ public: // Called by RenderObject::destroy() (and RenderWidget::destroy()) and is the only way layers should ever be destroyed void destroyLayer(); + void highQualityRepaintTimerFired(Timer<RenderBoxModelObject>*); + protected: void calculateBackgroundImageGeometry(const FillLayer*, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize); diff --git a/WebCore/rendering/RenderCounter.cpp b/WebCore/rendering/RenderCounter.cpp index b0aefc5..46bf9f4 100644 --- a/WebCore/rendering/RenderCounter.cpp +++ b/WebCore/rendering/RenderCounter.cpp @@ -38,7 +38,7 @@ using namespace HTMLNames; typedef HashMap<RefPtr<AtomicStringImpl>, CounterNode*> CounterMap; typedef HashMap<const RenderObject*, CounterMap*> CounterMaps; -static CounterNode* makeCounterNode(RenderObject*, const AtomicString& counterName, bool alwaysCreateCounter); +static CounterNode* makeCounterNode(RenderObject*, const AtomicString& identifier, bool alwaysCreateCounter); static CounterMaps& counterMaps() { @@ -53,7 +53,7 @@ static inline RenderObject* previousSiblingOrParent(RenderObject* object) return object->parent(); } -static bool planCounter(RenderObject* object, const AtomicString& counterName, bool& isReset, int& value) +static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value) { ASSERT(object); @@ -66,7 +66,7 @@ static bool planCounter(RenderObject* object, const AtomicString& counterName, b ASSERT(style); if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) { - CounterDirectives directives = directivesMap->get(counterName.impl()); + CounterDirectives directives = directivesMap->get(identifier.impl()); if (directives.m_reset) { value = directives.m_resetValue; if (directives.m_increment) @@ -81,7 +81,7 @@ static bool planCounter(RenderObject* object, const AtomicString& counterName, b } } - if (counterName == "list-item") { + if (identifier == "list-item") { if (object->isListItem()) { if (toRenderListItem(object)->hasExplicitValue()) { value = toRenderListItem(object)->explicitValue(); @@ -142,7 +142,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& if (currentCounter) { // We have a suitable counter on the EndSearchRenderer. if (previousSibling) { // But we already found another counter that we come after. - if (currentCounter->isReset()) { + if (currentCounter->actsAsReset()) { // We found a reset counter that is on a renderer that is a sibling of ours or a parent. if (isReset && currentRenderer->parent() == counterOwner->parent()) { // We are also a reset counter and the previous reset was on a sibling renderer @@ -171,7 +171,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& // In this case we follow pretty much the same logic as above but no ASSERTs about // previousSibling, and when we are a sibling of the end counter we must set previousSibling // to currentCounter. - if (currentCounter->isReset()) { + if (currentCounter->actsAsReset()) { if (isReset && currentRenderer->parent() == counterOwner->parent()) { parent = currentCounter->parent(); previousSibling = currentCounter; @@ -201,7 +201,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& if (previousSibling) { // Since we had a suitable previous counter before, we should only consider this one as our // previousSibling if it is a reset counter and hence the current previousSibling is its child. - if (currentCounter->isReset()) { + if (currentCounter->actsAsReset()) { previousSibling = currentCounter; // We are no longer interested in previous siblings of the currentRenderer or their children // as counters they may have attached cannot be the previous sibling of the counter we are placing. @@ -226,31 +226,25 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& return false; } -static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& counterName, bool alwaysCreateCounter) +static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& identifier, bool alwaysCreateCounter) { ASSERT(object); if (object->m_hasCounterNodeMap) if (CounterMap* nodeMap = counterMaps().get(object)) - if (CounterNode* node = nodeMap->get(counterName.impl())) + if (CounterNode* node = nodeMap->get(identifier.impl())) return node; bool isReset = false; int value = 0; - if (!planCounter(object, counterName, isReset, value) && !alwaysCreateCounter) + if (!planCounter(object, identifier, isReset, value) && !alwaysCreateCounter) return 0; CounterNode* newParent = 0; CounterNode* newPreviousSibling = 0; - CounterNode* newNode; - if (findPlaceForCounter(object, counterName, isReset, newParent, newPreviousSibling)) { - newNode = new CounterNode(object, isReset, value); - newParent->insertAfter(newNode, newPreviousSibling, counterName); - } else { - // Make a reset node for counters that aren't inside an existing reset node. - newNode = new CounterNode(object, true, value); - } - + CounterNode* newNode = new CounterNode(object, isReset, value); + if (findPlaceForCounter(object, identifier, isReset, newParent, newPreviousSibling)) + newParent->insertAfter(newNode, newPreviousSibling, identifier); CounterMap* nodeMap; if (object->m_hasCounterNodeMap) nodeMap = counterMaps().get(object); @@ -259,8 +253,30 @@ static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& co counterMaps().set(object, nodeMap); object->m_hasCounterNodeMap = true; } - nodeMap->set(counterName.impl(), newNode); - + nodeMap->set(identifier.impl(), newNode); + if (newNode->parent() || !object->nextInPreOrder(object->parent())) + return newNode; + // Checking if some nodes that were previously counter tree root nodes + // should become children of this node now. + CounterMaps& maps = counterMaps(); + RenderObject* stayWithin = object->parent(); + for (RenderObject* currentRenderer = object->nextInPreOrder(stayWithin); currentRenderer; currentRenderer = currentRenderer->nextInPreOrder(stayWithin)) { + if (!currentRenderer->m_hasCounterNodeMap) + continue; + CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()); + if (!currentCounter) + continue; + if (currentCounter->parent()) { + ASSERT(newNode->firstChild()); + if (currentRenderer->lastChild()) + currentRenderer = currentRenderer->lastChild(); + continue; + } + if (stayWithin != currentRenderer->parent() || !currentCounter->hasResetType()) + newNode->insertAfter(currentCounter, newNode->lastChild(), identifier); + if (currentRenderer->lastChild()) + currentRenderer = currentRenderer->lastChild(); + } return newNode; } @@ -290,12 +306,12 @@ PassRefPtr<StringImpl> RenderCounter::originalText() const m_counterNode = makeCounterNode(parent(), m_counter.identifier(), true); CounterNode* child = m_counterNode; - int value = child->isReset() ? child->value() : child->countInParent(); + int value = child->actsAsReset() ? child->value() : child->countInParent(); String text = listMarkerText(m_counter.listStyle(), value); if (!m_counter.separator().isNull()) { - if (!child->isReset()) + if (!child->actsAsReset()) child = child->parent(); while (CounterNode* parent = child->parent()) { text = listMarkerText(m_counter.listStyle(), child->countInParent()) @@ -321,7 +337,7 @@ void RenderCounter::invalidate(const AtomicString& identifier) setNeedsLayoutAndPrefWidthsRecalc(); } -static void destroyCounterNodeChildren(const AtomicString& identifier, CounterNode* node) +static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node) { CounterNode* previous; for (CounterNode* child = node->lastDescendant(); child && child != node; child = previous) { @@ -336,27 +352,132 @@ static void destroyCounterNodeChildren(const AtomicString& identifier, CounterNo } delete child; } + RenderObject* renderer = node->renderer(); + if (!renderer->documentBeingDestroyed()) { + if (RenderObjectChildList* children = renderer->virtualChildren()) + children->invalidateCounters(renderer, identifier); + } + if (CounterNode* parent = node->parent()) + parent->removeChild(node, identifier); + delete node; } -void RenderCounter::destroyCounterNodes(RenderObject* object) +void RenderCounter::destroyCounterNodes(RenderObject* renderer) { CounterMaps& maps = counterMaps(); - CounterMap* map = maps.get(object); - if (!map) + CounterMaps::iterator mapsIterator = maps.find(renderer); + if (mapsIterator == maps.end()) return; - maps.remove(object); - + CounterMap* map = mapsIterator->second; CounterMap::const_iterator end = map->end(); for (CounterMap::const_iterator it = map->begin(); it != end; ++it) { - CounterNode* node = it->second; AtomicString identifier(it->first.get()); - destroyCounterNodeChildren(identifier, node); - if (CounterNode* parent = node->parent()) - parent->removeChild(node, identifier); - delete node; + destroyCounterNodeWithoutMapRemoval(identifier, it->second); } - + maps.remove(mapsIterator); delete map; + renderer->m_hasCounterNodeMap = false; +} + +void RenderCounter::destroyCounterNode(RenderObject* renderer, const AtomicString& identifier) +{ + CounterMap* map = counterMaps().get(renderer); + if (!map) + return; + CounterMap::iterator mapIterator = map->find(identifier.impl()); + if (mapIterator == map->end()) + return; + destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second); + map->remove(mapIterator); + // We do not delete "map" here even if empty because we expect to reuse + // it soon. In order for a renderer to lose all its counters permanently, + // a style change for the renderer involving removal of all counter + // directives must occur, in which case, RenderCounter::destroyCounterNodes() + // must be called. + // The destruction of the Renderer (possibly caused by the removal of its + // associated DOM node) is the other case that leads to the permanent + // destruction of all counters attached to a Renderer. In this case + // RenderCounter::destroyCounterNodes() must be and is now called, too. + // RenderCounter::destroyCounterNodes() handles destruction of the counter + // map associated with a renderer, so there is no risk in leaking the map. +} + +static void updateCounters(RenderObject* renderer) +{ + ASSERT(renderer->style()); + const CounterDirectiveMap* directiveMap = renderer->style()->counterDirectives(); + if (!directiveMap) + return; + CounterDirectiveMap::const_iterator end = directiveMap->end(); + if (!renderer->m_hasCounterNodeMap) { + for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) + makeCounterNode(renderer, AtomicString(it->first.get()), false); + return; + } + CounterMap* counterMap = counterMaps().get(renderer); + ASSERT(counterMap); + for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) { + CounterNode* node = counterMap->get(it->first.get()); + if (!node) { + makeCounterNode(renderer, AtomicString(it->first.get()), false); + continue; + } + CounterNode* newParent = 0; + CounterNode* newPreviousSibling; + findPlaceForCounter(renderer, AtomicString(it->first.get()), node->hasResetType(), newParent, newPreviousSibling); + CounterNode* parent = node->parent(); + if (newParent == parent && newPreviousSibling == node->previousSibling()) + continue; + if (parent) + parent->removeChild(node, it->first.get()); + newParent->insertAfter(node, newPreviousSibling, it->first.get()); + } +} + +void RenderCounter::rendererSubtreeAttached(RenderObject* renderer) +{ + for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer)) + updateCounters(descendant); +} + +void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle) +{ + const CounterDirectiveMap* newCounterDirectives; + const CounterDirectiveMap* oldCounterDirectives; + if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) { + if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { + CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); + CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end(); + for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { + CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->first); + if (oldMapIt != oldMapEnd) { + if (oldMapIt->second == it->second) + continue; + RenderCounter::destroyCounterNode(renderer, it->first.get()); + } + // We must create this node here, because the changed node may be a node with no display such as + // as those created by the increment or reset directives and the re-layout that will happen will + // not catch the change if the node had no children. + makeCounterNode(renderer, it->first.get(), false); + } + // Destroying old counters that do not exist in the new counterDirective map. + for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) { + if (!newCounterDirectives->contains(it->first)) + RenderCounter::destroyCounterNode(renderer, it->first.get()); + } + } else { + if (renderer->m_hasCounterNodeMap) + RenderCounter::destroyCounterNodes(renderer); + } + } else if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { + CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); + for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { + // We must create this node here, because the added node may be a node with no display such as + // as those created by the increment or reset directives and the re-layout that will happen will + // not catch the change if the node had no children. + makeCounterNode(renderer, it->first.get(), false); + } + } } } // namespace WebCore diff --git a/WebCore/rendering/RenderCounter.h b/WebCore/rendering/RenderCounter.h index 356f1bd..10ba1dc 100644 --- a/WebCore/rendering/RenderCounter.h +++ b/WebCore/rendering/RenderCounter.h @@ -40,6 +40,9 @@ public: void invalidate(const AtomicString& identifier); static void destroyCounterNodes(RenderObject*); + static void destroyCounterNode(RenderObject*, const AtomicString& identifier); + static void rendererSubtreeAttached(RenderObject*); + static void rendererStyleChanged(RenderObject*, const RenderStyle* oldStyle, const RenderStyle* newStyle); private: virtual const char* renderName() const; diff --git a/WebCore/rendering/RenderEmbeddedObject.cpp b/WebCore/rendering/RenderEmbeddedObject.cpp new file mode 100644 index 0000000..db32808 --- /dev/null +++ b/WebCore/rendering/RenderEmbeddedObject.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderEmbeddedObject.h" + +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "HTMLEmbedElement.h" +#include "HTMLIFrameElement.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" +#include "HTMLParamElement.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "PluginWidget.h" +#include "RenderView.h" +#include "RenderWidgetProtector.h" +#include "Text.h" + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLVideoElement.h" +#endif + +#if USE(ACCELERATED_COMPOSITING) +#include "PluginWidget.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +RenderEmbeddedObject::RenderEmbeddedObject(Element* element) + : RenderPartObject(element) +{ + view()->frameView()->setIsVisuallyNonEmpty(); +} + +RenderEmbeddedObject::~RenderEmbeddedObject() +{ + if (frameView()) + frameView()->removeWidgetToUpdate(this); +} + +#if USE(ACCELERATED_COMPOSITING) +bool RenderEmbeddedObject::requiresLayer() const +{ + if (RenderPartObject::requiresLayer()) + return true; + + return allowsAcceleratedCompositing(); +} + +bool RenderEmbeddedObject::allowsAcceleratedCompositing() const +{ + return widget() && widget()->isPluginWidget() && static_cast<PluginWidget*>(widget())->platformLayer(); +} +#endif + +static bool isURLAllowed(Document* doc, const String& url) +{ + if (doc->frame()->page()->frameCount() >= 200) + return false; + + // We allow one level of self-reference because some sites depend on that. + // But we don't allow more than one. + KURL completeURL = doc->completeURL(url); + bool foundSelfReference = false; + for (Frame* frame = doc->frame(); frame; frame = frame->tree()->parent()) { + if (equalIgnoringFragmentIdentifier(frame->loader()->url(), completeURL)) { + if (foundSelfReference) + return false; + foundSelfReference = true; + } + } + return true; +} + +typedef HashMap<String, String, CaseFoldingHash> ClassIdToTypeMap; + +static ClassIdToTypeMap* createClassIdToTypeMap() +{ + ClassIdToTypeMap* map = new ClassIdToTypeMap; + map->add("clsid:D27CDB6E-AE6D-11CF-96B8-444553540000", "application/x-shockwave-flash"); + map->add("clsid:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA", "audio/x-pn-realaudio-plugin"); + map->add("clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B", "video/quicktime"); + map->add("clsid:166B1BCA-3F9C-11CF-8075-444553540000", "application/x-director"); + map->add("clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6", "application/x-mplayer2"); + map->add("clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95", "application/x-mplayer2"); + return map; +} + +static String serviceTypeForClassId(const String& classId) +{ + // Return early if classId is empty (since we won't do anything below). + // Furthermore, if classId is null, calling get() below will crash. + if (classId.isEmpty()) + return String(); + + static ClassIdToTypeMap* map = createClassIdToTypeMap(); + return map->get(classId); +} + +static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues) +{ + // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP + // require "src" attribute). + int srcIndex = -1, dataIndex = -1; + for (unsigned int i = 0; i < paramNames->size(); ++i) { + if (equalIgnoringCase((*paramNames)[i], "src")) + srcIndex = i; + else if (equalIgnoringCase((*paramNames)[i], "data")) + dataIndex = i; + } + + if (srcIndex == -1 && dataIndex != -1) { + paramNames->append("src"); + paramValues->append((*paramValues)[dataIndex]); + } +} + +void RenderEmbeddedObject::updateWidget(bool onlyCreateNonNetscapePlugins) +{ + String url; + String serviceType; + Vector<String> paramNames; + Vector<String> paramValues; + Frame* frame = frameView()->frame(); + + // The calls to FrameLoader::requestObject within this function can result in a plug-in being initialized. + // This can run cause arbitrary JavaScript to run and may result in this RenderObject being detached from + // the render tree and destroyed, causing a crash like <rdar://problem/6954546>. By extending our lifetime + // artifically to ensure that we remain alive for the duration of plug-in initialization. + RenderWidgetProtector protector(this); + + if (node()->hasTagName(objectTag)) { + HTMLObjectElement* objectElement = static_cast<HTMLObjectElement*>(node()); + + objectElement->setNeedWidgetUpdate(false); + if (!objectElement->isFinishedParsingChildren()) + return; + + // Check for a child EMBED tag. + HTMLEmbedElement* embed = 0; + for (Node* child = objectElement->firstChild(); child; ) { + if (child->hasTagName(embedTag)) { + embed = static_cast<HTMLEmbedElement*>(child); + break; + } + + if (child->hasTagName(objectTag)) + child = child->nextSibling(); // Don't descend into nested OBJECT tags + else + child = child->traverseNextNode(objectElement); // Otherwise descend (EMBEDs may be inside COMMENT tags) + } + + // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT. + HTMLElement* embedOrObject; + if (embed) { + embedOrObject = embed; + url = embed->url(); + serviceType = embed->serviceType(); + } else + embedOrObject = objectElement; + + // If there was no URL or type defined in EMBED, try the OBJECT tag. + if (url.isEmpty()) + url = objectElement->url(); + if (serviceType.isEmpty()) + serviceType = objectElement->serviceType(); + + HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; + + // Scan the PARAM children. + // Get the URL and type from the params if we don't already have them. + // Get the attributes from the params if there is no EMBED tag. + Node* child = objectElement->firstChild(); + while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) { + if (child->hasTagName(paramTag)) { + HTMLParamElement* p = static_cast<HTMLParamElement*>(child); + String name = p->name(); + if (url.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) + url = p->value(); + if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { + serviceType = p->value(); + int pos = serviceType.find(";"); + if (pos != -1) + serviceType = serviceType.left(pos); + } + if (!embed && !name.isEmpty()) { + uniqueParamNames.add(name.impl()); + paramNames.append(p->name()); + paramValues.append(p->value()); + } + } + child = child->nextSibling(); + } + + // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag + // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is + // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means + // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, + // else our Java plugin will misinterpret it. [4004531] + String codebase; + if (!embed && MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { + codebase = "codebase"; + uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already + } + + // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values. + NamedNodeMap* attributes = embedOrObject->attributes(); + if (attributes) { + for (unsigned i = 0; i < attributes->length(); ++i) { + Attribute* it = attributes->attributeItem(i); + const AtomicString& name = it->name().localName(); + if (embed || !uniqueParamNames.contains(name.impl())) { + paramNames.append(name.string()); + paramValues.append(it->value().string()); + } + } + } + + mapDataParamToSrc(¶mNames, ¶mValues); + + // If we still don't have a type, try to map from a specific CLASSID to a type. + if (serviceType.isEmpty()) + serviceType = serviceTypeForClassId(objectElement->classId()); + + if (!isURLAllowed(document(), url)) + return; + + // Find out if we support fallback content. + m_hasFallbackContent = false; + for (Node* child = objectElement->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) { + if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) // Discount <embed> and <param> + || (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace())) + m_hasFallbackContent = true; + } + + if (onlyCreateNonNetscapePlugins) { + KURL completedURL; + if (!url.isEmpty()) + completedURL = frame->loader()->completeURL(url); + + if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) + return; + } + + bool success = objectElement->dispatchBeforeLoadEvent(url) && frame->loader()->requestObject(this, url, objectElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); + if (!success && m_hasFallbackContent) + objectElement->renderFallbackContent(); + + } else if (node()->hasTagName(embedTag)) { + HTMLEmbedElement* embedElement = static_cast<HTMLEmbedElement*>(node()); + embedElement->setNeedWidgetUpdate(false); + url = embedElement->url(); + serviceType = embedElement->serviceType(); + + if (url.isEmpty() && serviceType.isEmpty()) + return; + if (!isURLAllowed(document(), url)) + return; + + // add all attributes set on the embed object + NamedNodeMap* attributes = embedElement->attributes(); + if (attributes) { + for (unsigned i = 0; i < attributes->length(); ++i) { + Attribute* it = attributes->attributeItem(i); + paramNames.append(it->name().localName().string()); + paramValues.append(it->value().string()); + } + } + + if (onlyCreateNonNetscapePlugins) { + KURL completedURL; + if (!url.isEmpty()) + completedURL = frame->loader()->completeURL(url); + + if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) + return; + } + + if (embedElement->dispatchBeforeLoadEvent(url)) + frame->loader()->requestObject(this, url, embedElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); + } +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + else if (node()->hasTagName(videoTag) || node()->hasTagName(audioTag)) { + HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node()); + + mediaElement->setNeedWidgetUpdate(false); + if (node()->hasTagName(videoTag)) { + HTMLVideoElement* vid = static_cast<HTMLVideoElement*>(node()); + String poster = vid->poster(); + if (!poster.isEmpty()) { + paramNames.append("_media_element_poster_"); + paramValues.append(poster); + } + } + + url = mediaElement->initialURL(); + if (!url.isEmpty()) { + paramNames.append("_media_element_src_"); + paramValues.append(url); + } + + serviceType = "application/x-media-element-proxy-plugin"; + + if (mediaElement->dispatchBeforeLoadEvent(url)) + frame->loader()->requestObject(this, url, nullAtom, serviceType, paramNames, paramValues); + } +#endif +} + +void RenderEmbeddedObject::layout() +{ + ASSERT(needsLayout()); + + calcWidth(); + calcHeight(); + + RenderPart::layout(); + + m_overflow.clear(); + addShadowOverflow(); + + if (!widget() && frameView()) + frameView()->addWidgetToUpdate(this); + + setNeedsLayout(false); +} + +} diff --git a/WebCore/rendering/RenderEmbeddedObject.h b/WebCore/rendering/RenderEmbeddedObject.h new file mode 100644 index 0000000..bdaea92 --- /dev/null +++ b/WebCore/rendering/RenderEmbeddedObject.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderEmbeddedObject_h +#define RenderEmbeddedObject_h + +#include "RenderPartObject.h" + +namespace WebCore { + +// Renderer for embeds and objects. +class RenderEmbeddedObject : public RenderPartObject { +public: + RenderEmbeddedObject(Element*); + virtual ~RenderEmbeddedObject(); + + void updateWidget(bool onlyCreateNonNetscapePlugins); + +#if USE(ACCELERATED_COMPOSITING) + virtual bool allowsAcceleratedCompositing() const; +#endif + +private: + virtual const char* renderName() const { return "RenderEmbeddedObject"; } + virtual bool isEmbeddedObject() const { return true; } + +#if USE(ACCELERATED_COMPOSITING) + virtual bool requiresLayer() const; +#endif + + virtual void layout(); +}; + +inline RenderEmbeddedObject* toRenderEmbeddedObject(RenderObject* object) +{ + ASSERT(!object || !strcmp(object->renderName(), "RenderEmbeddedObject")); + return static_cast<RenderEmbeddedObject*>(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderEmbeddedObject(const RenderEmbeddedObject*); + +} // namespace WebCore + +#endif // RenderEmbeddedObject_h diff --git a/WebCore/rendering/RenderFileUploadControl.cpp b/WebCore/rendering/RenderFileUploadControl.cpp index 37ee8fb..59cbacf 100644 --- a/WebCore/rendering/RenderFileUploadControl.cpp +++ b/WebCore/rendering/RenderFileUploadControl.cpp @@ -21,6 +21,7 @@ #include "config.h" #include "RenderFileUploadControl.h" +#include "Chrome.h" #include "FileList.h" #include "Frame.h" #include "FrameView.h" @@ -108,6 +109,11 @@ bool RenderFileUploadControl::allowsMultipleFiles() return !input->getAttribute(multipleAttr).isNull(); } +String RenderFileUploadControl::acceptTypes() +{ + return static_cast<HTMLInputElement*>(node())->accept(); +} + void RenderFileUploadControl::click() { Frame* frame = node()->document()->frame(); diff --git a/WebCore/rendering/RenderFileUploadControl.h b/WebCore/rendering/RenderFileUploadControl.h index 72ba308..dcdce4d 100644 --- a/WebCore/rendering/RenderFileUploadControl.h +++ b/WebCore/rendering/RenderFileUploadControl.h @@ -49,6 +49,7 @@ public: String fileTextValue() const; bool allowsMultipleFiles(); + String acceptTypes(); private: virtual const char* renderName() const { return "RenderFileUploadControl"; } diff --git a/WebCore/rendering/RenderForeignObject.cpp b/WebCore/rendering/RenderForeignObject.cpp index b15d55c..6597554 100644 --- a/WebCore/rendering/RenderForeignObject.cpp +++ b/WebCore/rendering/RenderForeignObject.cpp @@ -84,12 +84,14 @@ FloatRect RenderForeignObject::repaintRectInLocalCoordinates() const void RenderForeignObject::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) { rect = localToParentTransform().mapRect(rect); + style()->svgStyle()->inflateForShadow(rect); RenderBlock::computeRectForRepaint(repaintContainer, rect, fixed); } -TransformationMatrix RenderForeignObject::localToParentTransform() const +const TransformationMatrix& RenderForeignObject::localToParentTransform() const { - return localTransform() * translationForAttributes(); + m_localToParentTransform = localTransform() * translationForAttributes(); + return m_localToParentTransform; } void RenderForeignObject::layout() @@ -118,6 +120,11 @@ bool RenderForeignObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int return false; } +void RenderForeignObject::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const +{ + SVGRenderBase::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState); +} + } // namespace WebCore #endif // ENABLE(SVG) && ENABLE(SVG_FOREIGN_OBJECT) diff --git a/WebCore/rendering/RenderForeignObject.h b/WebCore/rendering/RenderForeignObject.h index e014f22..8cb9a55 100644 --- a/WebCore/rendering/RenderForeignObject.h +++ b/WebCore/rendering/RenderForeignObject.h @@ -38,25 +38,29 @@ public: virtual void paint(PaintInfo&, int parentX, int parentY); - virtual TransformationMatrix localToParentTransform() const; + virtual const TransformationMatrix& localToParentTransform() const; virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); virtual bool requiresLayer() const { return false; } virtual void layout(); virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const { return borderBoxRect(); } virtual FloatRect repaintRectInLocalCoordinates() const; virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); virtual bool isSVGForeignObject() const { return true; } + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const; + private: TransformationMatrix translationForAttributes() const; virtual TransformationMatrix localTransform() const { return m_localTransform; } TransformationMatrix m_localTransform; + mutable TransformationMatrix m_localToParentTransform; }; } // namespace WebCore diff --git a/WebCore/rendering/RenderImage.cpp b/WebCore/rendering/RenderImage.cpp index d06ca1f..881d0b4 100644 --- a/WebCore/rendering/RenderImage.cpp +++ b/WebCore/rendering/RenderImage.cpp @@ -4,7 +4,7 @@ * (C) 2000 Dirk Mueller (mueller@kde.org) * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,14 +26,19 @@ #include "config.h" #include "RenderImage.h" +#include "Frame.h" #include "GraphicsContext.h" +#include "HTMLAreaElement.h" +#include "HTMLCollection.h" #include "HTMLImageElement.h" #include "HTMLInputElement.h" #include "HTMLMapElement.h" #include "HTMLNames.h" #include "HitTestResult.h" #include "Page.h" +#include "RenderTheme.h" #include "RenderView.h" +#include "SelectionController.h" #include <wtf/CurrentTime.h> #include <wtf/UnusedParam.h> @@ -430,20 +435,77 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty) #endif IntSize contentSize(cWidth, cHeight); - bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, contentSize); IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), contentSize); - HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(node()) : 0; - CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; - context->drawImage(image(cWidth, cHeight), style()->colorSpace(), rect, compositeOperator, useLowQualityScaling); + paintIntoRect(context, rect); } } +void RenderImage::paint(PaintInfo& paintInfo, int tx, int ty) +{ + RenderReplaced::paint(paintInfo, tx, ty); + + if (paintInfo.phase == PaintPhaseOutline) + paintFocusRings(paintInfo, style()); +} + +void RenderImage::paintFocusRings(PaintInfo& paintInfo, const RenderStyle* style) +{ + // Don't draw focus rings if printing. + if (document()->printing() || !document()->frame()->selection()->isFocusedAndActive()) + return; + + if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints()) + return; + + HTMLMapElement* mapElement = imageMap(); + if (!mapElement) + return; + + Document* document = mapElement->document(); + if (!document) + return; + + Node* focusedNode = document->focusedNode(); + if (!focusedNode) + return; + + RefPtr<HTMLCollection> areas = mapElement->areas(); + unsigned numAreas = areas->length(); + + // FIXME: Clip the paths to the image bounding box. + for (unsigned k = 0; k < numAreas; ++k) { + HTMLAreaElement* areaElement = static_cast<HTMLAreaElement*>(areas->item(k)); + if (focusedNode != areaElement) + continue; + + Vector<Path> focusRingPaths; + focusRingPaths.append(areaElement->getPath(this)); + paintInfo.context->drawFocusRing(focusRingPaths, style->outlineWidth(), style->outlineOffset(), style->outlineColor()); + break; + } +} + +void RenderImage::paintIntoRect(GraphicsContext* context, const IntRect& rect) +{ + if (!hasImage() || errorOccurred() || rect.width() <= 0 || rect.height() <= 0) + return; + + Image* img = image(rect.width(), rect.height()); + if (!img || img->isNull()) + return; + + HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(node()) : 0; + CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; + bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, rect.size()); + context->drawImage(image(rect.width(), rect.height()), style()->colorSpace(), rect, compositeOperator, useLowQualityScaling); +} + int RenderImage::minimumReplacedHeight() const { return errorOccurred() ? intrinsicSize().height() : 0; } -HTMLMapElement* RenderImage::imageMap() +HTMLMapElement* RenderImage::imageMap() const { HTMLImageElement* i = node() && node()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(node()) : 0; return i ? i->document()->getImageMap(i->getAttribute(usemapAttr)) : 0; diff --git a/WebCore/rendering/RenderImage.h b/WebCore/rendering/RenderImage.h index 2224412..bc5e2d8 100644 --- a/WebCore/rendering/RenderImage.h +++ b/WebCore/rendering/RenderImage.h @@ -3,7 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2005, 2006, 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -43,7 +43,7 @@ public: void setCachedImage(CachedImage*); CachedImage* cachedImage() const { return m_cachedImage.get(); } - HTMLMapElement* imageMap(); + HTMLMapElement* imageMap() const; void resetAnimation(); @@ -57,6 +57,13 @@ protected: virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + virtual void paintIntoRect(GraphicsContext*, const IntRect&); + void paintFocusRings(PaintInfo&, const RenderStyle*); + virtual void paint(PaintInfo&, int tx, int ty); + + bool isWidthSpecified() const; + bool isHeightSpecified() const; + private: virtual const char* renderName() const { return "RenderImage"; } @@ -87,9 +94,6 @@ private: int calcAspectRatioWidth() const; int calcAspectRatioHeight() const; - bool isWidthSpecified() const; - bool isHeightSpecified() const; - protected: // The image we are rendering. CachedResourceHandle<CachedImage> m_cachedImage; diff --git a/WebCore/rendering/RenderInline.cpp b/WebCore/rendering/RenderInline.cpp index 2f9a247..9571751 100644 --- a/WebCore/rendering/RenderInline.cpp +++ b/WebCore/rendering/RenderInline.cpp @@ -23,6 +23,7 @@ #include "config.h" #include "RenderInline.h" +#include "Chrome.h" #include "FloatQuad.h" #include "GraphicsContext.h" #include "HitTestResult.h" @@ -931,13 +932,15 @@ void RenderInline::imageChanged(WrappedImagePtr, const IntRect*) repaint(); } -void RenderInline::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +void RenderInline::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) { for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { RootInlineBox* root = curr->root(); int top = max(root->lineTop(), curr->y()); int bottom = min(root->lineBottom(), curr->y() + curr->height()); - graphicsContext->addFocusRingRect(IntRect(tx + curr->x(), ty + top, curr->width(), bottom - top)); + IntRect rect(tx + curr->x(), ty + top, curr->width(), bottom - top); + if (!rect.isEmpty()) + rects.append(rect); } for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { @@ -948,17 +951,17 @@ void RenderInline::addFocusRingRects(GraphicsContext* graphicsContext, int tx, i pos = curr->localToAbsolute(); else if (curr->isBox()) pos.move(toRenderBox(curr)->x(), toRenderBox(curr)->y()); - curr->addFocusRingRects(graphicsContext, pos.x(), pos.y()); + curr->addFocusRingRects(rects, pos.x(), pos.y()); } } if (continuation()) { if (continuation()->isInline()) - continuation()->addFocusRingRects(graphicsContext, + continuation()->addFocusRingRects(rects, tx - containingBlock()->x() + continuation()->containingBlock()->x(), ty - containingBlock()->y() + continuation()->containingBlock()->y()); else - continuation()->addFocusRingRects(graphicsContext, + continuation()->addFocusRingRects(rects, tx - containingBlock()->x() + toRenderBox(continuation())->x(), ty - containingBlock()->y() + toRenderBox(continuation())->y()); } @@ -975,13 +978,12 @@ void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty if (!oc.isValid()) oc = style()->color(); - graphicsContext->initFocusRing(ow, style()->outlineOffset()); - addFocusRingRects(graphicsContext, tx, ty); + Vector<IntRect> focusRingRects; + addFocusRingRects(focusRingRects, tx, ty); if (style()->outlineStyleIsAuto()) - graphicsContext->drawFocusRing(oc); + graphicsContext->drawFocusRing(focusRingRects, ow, style()->outlineOffset(), oc); else - addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); - graphicsContext->clearFocusRing(); + addPDFURLRect(graphicsContext, unionRect(focusRingRects)); } if (style()->outlineStyleIsAuto() || style()->outlineStyle() == BNONE) diff --git a/WebCore/rendering/RenderInline.h b/WebCore/rendering/RenderInline.h index 8e9715c..d35aa85 100644 --- a/WebCore/rendering/RenderInline.h +++ b/WebCore/rendering/RenderInline.h @@ -65,7 +65,7 @@ public: IntSize relativePositionedInlineOffset(const RenderBox* child) const; - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); void paintOutline(GraphicsContext*, int tx, int ty); int verticalPositionFromCache(bool firstLine) const; diff --git a/WebCore/rendering/RenderLayer.cpp b/WebCore/rendering/RenderLayer.cpp index 7e0fee9..38e5f44 100644 --- a/WebCore/rendering/RenderLayer.cpp +++ b/WebCore/rendering/RenderLayer.cpp @@ -73,11 +73,13 @@ #include "RenderScrollbar.h" #include "RenderScrollbarPart.h" #include "RenderTheme.h" +#include "RenderTreeAsText.h" #include "RenderView.h" #include "ScaleTransformOperation.h" #include "Scrollbar.h" #include "ScrollbarTheme.h" #include "SelectionController.h" +#include "TextStream.h" #include "TransformationMatrix.h" #include "TransformState.h" #include "TranslateTransformOperation.h" @@ -657,6 +659,12 @@ static inline bool isPositionedContainer(RenderLayer* layer) return o->isRenderView() || o->isPositioned() || o->isRelPositioned() || layer->hasTransform(); } +static inline bool isFixedPositionedContainer(RenderLayer* layer) +{ + RenderObject* o = layer->renderer(); + return o->isRenderView() || layer->hasTransform(); +} + RenderLayer* RenderLayer::enclosingPositionedAncestor() const { RenderLayer* curr = parent(); @@ -826,7 +834,7 @@ void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const RenderLayer* p->clip(clipRect); p->beginTransparencyLayer(renderer()->opacity()); #ifdef REVEAL_TRANSPARENCY_LAYERS - p->setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f)); + p->setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f), DeviceColorSpace); p->fillRect(clipRect); #endif } @@ -986,9 +994,10 @@ RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& xPos, i { if (ancestorLayer == this) return; - - if (renderer()->style()->position() == FixedPosition) { - // Add in the offset of the view. We can obtain this by calling + + EPosition position = renderer()->style()->position(); + if (position == FixedPosition && (!ancestorLayer || ancestorLayer == renderer()->view()->layer())) { + // If the fixed layer's container is the root, just add in the offset of the view. We can obtain this by calling // localToAbsolute() on the RenderView. FloatPoint absPos = renderer()->localToAbsolute(FloatPoint(), true); xPos += absPos.x(); @@ -996,9 +1005,43 @@ RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& xPos, i return; } + if (position == FixedPosition) { + // For a fixed layers, we need to walk up to the root to see if there's a fixed position container + // (e.g. a transformed layer). It's an error to call convertToLayerCoords() across a layer with a transform, + // so we should always find the ancestor at or before we find the fixed position container. + RenderLayer* fixedPositionContainerLayer = 0; + bool foundAncestor = false; + for (RenderLayer* currLayer = parent(); currLayer; currLayer = currLayer->parent()) { + if (currLayer == ancestorLayer) + foundAncestor = true; + + if (isFixedPositionedContainer(currLayer)) { + fixedPositionContainerLayer = currLayer; + ASSERT(foundAncestor); + break; + } + } + + ASSERT(fixedPositionContainerLayer); // We should have hit the RenderView's layer at least. + + if (fixedPositionContainerLayer != ancestorLayer) { + int fixedContainerX = 0; + int fixedContainerY = 0; + convertToLayerCoords(fixedPositionContainerLayer, fixedContainerX, fixedContainerY); + + int ancestorX = 0; + int ancestorY = 0; + ancestorLayer->convertToLayerCoords(fixedPositionContainerLayer, ancestorX, ancestorY); + + xPos += (fixedContainerX - ancestorX); + yPos += (fixedContainerY - ancestorY); + return; + } + } + RenderLayer* parentLayer; - if (renderer()->style()->position() == AbsolutePosition) { - // Do what enclosingPositionedAncestor() does, but check for ancestorLayer along the way + if (position == AbsolutePosition || position == FixedPosition) { + // Do what enclosingPositionedAncestor() does, but check for ancestorLayer along the way. parentLayer = parent(); bool foundAncestorFirst = false; while (parentLayer) { @@ -1114,7 +1157,7 @@ void RenderLayer::scrollByRecursively(int xDelta, int yDelta) frame->eventHandler()->updateAutoscrollRenderer(); } } else if (renderer()->view()->frameView()) { - // If we are here, we were called on a renderer that can be programatically scrolled, but doesn't + // If we are here, we were called on a renderer that can be programmatically scrolled, but doesn't // have an overflow clip. Which means that it is a document node that can be scrolled. renderer()->view()->frameView()->scrollBy(IntSize(xDelta, yDelta)); // FIXME: If we didn't scroll the whole way, do we want to try looking at the frames ownerElement? @@ -1198,7 +1241,7 @@ void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repai // The caret rect needs to be invalidated after scrolling Frame* frame = renderer()->document()->frame(); if (frame) - frame->invalidateSelection(); + frame->selection()->setNeedsLayout(); // Just schedule a full repaint of our object. if (repaint) @@ -1701,6 +1744,11 @@ IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& absolutePoint) const return localPoint - bottomRight; } +bool RenderLayer::hasOverflowControls() const +{ + return m_hBar || m_vBar || m_scrollCorner || renderer()->style()->resize() != RESIZE_NONE; +} + void RenderLayer::positionOverflowControls(int tx, int ty) { if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) @@ -1886,16 +1934,22 @@ RenderLayer::updateScrollInfoAfterLayout() // Set up the range (and page step/line step). if (m_hBar) { int clientWidth = box->clientWidth(); - int pageStep = (clientWidth - cAmountToKeepWhenPaging); - if (pageStep < 0) pageStep = clientWidth; + int pageStep = max(clientWidth * cFractionToStepWhenPaging, 1.f); m_hBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); m_hBar->setProportion(clientWidth, m_scrollWidth); + // Explicitly set the horizontal scroll value. This ensures that when a + // right-to-left scrollable area's width (or content width) changes, the + // top right corner of the content doesn't shift with respect to the top + // right corner of the area. Conceptually, right-to-left areas have + // their origin at the top-right, but RenderLayer is top-left oriented, + // so this is needed to keep everything working (see how scrollXOffset() + // differs from scrollYOffset() to get an idea of why the horizontal and + // vertical scrollbars need to be treated differently). m_hBar->setValue(scrollXOffset()); } if (m_vBar) { int clientHeight = box->clientHeight(); - int pageStep = (clientHeight - cAmountToKeepWhenPaging); - if (pageStep < 0) pageStep = clientHeight; + int pageStep = max(clientHeight * cFractionToStepWhenPaging, 1.f); m_vBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); m_vBar->setProportion(clientHeight, m_scrollHeight); } @@ -2228,7 +2282,7 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set). // Else, our renderer tree may or may not contain the painting root, so we pass that root along - // so it will be tested against as we decend through the renderers. + // so it will be tested against as we descend through the renderers. RenderObject* paintingRootForRenderer = 0; if (paintingRoot && !renderer()->isDescendantOf(paintingRoot)) paintingRootForRenderer = paintingRoot; @@ -2351,15 +2405,10 @@ bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) } } - // Now determine if the result is inside an anchor; make sure an image map wins if - // it already set URLElement and only use the innermost. + // Now determine if the result is inside an anchor - if the urlElement isn't already set. Node* node = result.innerNode(); - while (node) { - // for imagemaps, URLElement is the associated area element not the image itself - if (node->isLink() && !result.URLElement() && !node->hasTagName(imgTag)) - result.setURLElement(static_cast<Element*>(node)); - node = node->eventParentNode(); - } + if (node && !result.URLElement()) + result.setURLElement(static_cast<Element*>(node->enclosingLinkEventParentOrSelf())); // Next set up the correct :hover/:active state along the new chain. updateHoverActiveState(request, result); @@ -2603,7 +2652,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. if (fgRect.contains(hitTestPoint) && isSelfPaintingLayer()) { - // Hit test with a temporary HitTestResult, because we onlyl want to commit to 'result' if we know we're frontmost. + // Hit test with a temporary HitTestResult, because we only want to commit to 'result' if we know we're frontmost. HitTestResult tempResult(result.point()); if (hitTestContents(request, tempResult, layerBounds, hitTestPoint, HitTestDescendants) && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { @@ -3238,7 +3287,7 @@ void RenderLayer::updateCompositingAndLayerListsIfNeeded() #if USE(ACCELERATED_COMPOSITING) if (compositor()->inCompositingMode()) { if ((isStackingContext() && m_zOrderListsDirty) || m_normalFlowListDirty) - compositor()->updateCompositingLayers(this); + compositor()->updateCompositingLayers(CompositingUpdateOnPaitingOrHitTest, this); return; } #endif @@ -3299,7 +3348,7 @@ void RenderLayer::repaintIncludingNonCompositingDescendants(RenderBoxModelObject bool RenderLayer::shouldBeNormalFlowOnly() const { - return (renderer()->hasOverflowClip() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isVideo()) && + return (renderer()->hasOverflowClip() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isVideo() || renderer()->isEmbeddedObject()) && !renderer()->isPositioned() && !renderer()->isRelPositioned() && !renderer()->hasTransform() && @@ -3308,7 +3357,7 @@ bool RenderLayer::shouldBeNormalFlowOnly() const bool RenderLayer::isSelfPaintingLayer() const { - return !isNormalFlowOnly() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isTableRow() || renderer()->isVideo(); + return !isNormalFlowOnly() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isTableRow() || renderer()->isVideo() || renderer()->isEmbeddedObject(); } void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle*) @@ -3322,7 +3371,7 @@ void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle*) dirtyStackingContextZOrderLists(); } - if (renderer()->style()->overflowX() == OMARQUEE && renderer()->style()->marqueeBehavior() != MNONE) { + if (renderer()->style()->overflowX() == OMARQUEE && renderer()->style()->marqueeBehavior() != MNONE && renderer()->isBox()) { if (!m_marquee) m_marquee = new RenderMarquee(this); m_marquee->updateMarqueeStyle(); @@ -3456,3 +3505,16 @@ void RenderLayer::updateReflectionStyle() } } // namespace WebCore + +#ifndef NDEBUG +void showLayerTree(const WebCore::RenderLayer* layer) +{ + if (!layer) + return; + + if (WebCore::Frame* frame = layer->renderer()->document()->frame()) { + WebCore::String output = externalRepresentation(frame, WebCore::RenderAsTextShowAllLayers | WebCore::RenderAsTextShowLayerNesting | WebCore::RenderAsTextShowCompositedLayers); + fprintf(stderr, "%s\n", output.utf8().data()); + } +} +#endif diff --git a/WebCore/rendering/RenderLayer.h b/WebCore/rendering/RenderLayer.h index af64fc4..3cdad3a 100644 --- a/WebCore/rendering/RenderLayer.h +++ b/WebCore/rendering/RenderLayer.h @@ -205,6 +205,7 @@ public: void beginTransparencyLayers(GraphicsContext*, const RenderLayer* rootLayer, PaintBehavior); bool hasReflection() const { return renderer()->hasReflection(); } + bool isReflection() const { return renderer()->isReplica(); } RenderReplica* reflection() const { return m_reflection; } RenderLayer* reflectionLayer() const; @@ -262,6 +263,7 @@ public: int verticalScrollbarWidth() const; int horizontalScrollbarHeight() const; + bool hasOverflowControls() const; void positionOverflowControls(int tx, int ty); bool isPointInResizeControl(const IntPoint& absolutePoint) const; bool hitTestOverflowControls(HitTestResult&, const IntPoint& localPoint); @@ -662,4 +664,9 @@ private: } // namespace WebCore +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showLayerTree(const WebCore::RenderLayer* layer); +#endif + #endif // RenderLayer_h diff --git a/WebCore/rendering/RenderLayerBacking.cpp b/WebCore/rendering/RenderLayerBacking.cpp index 35aa7e1..3f60557 100644 --- a/WebCore/rendering/RenderLayerBacking.cpp +++ b/WebCore/rendering/RenderLayerBacking.cpp @@ -41,9 +41,11 @@ #include "HTMLNames.h" #include "InspectorTimelineAgent.h" #include "KeyframeList.h" +#include "PluginWidget.h" #include "RenderBox.h" #include "RenderImage.h" #include "RenderLayerCompositor.h" +#include "RenderEmbeddedObject.h" #include "RenderVideo.h" #include "RenderView.h" #include "Settings.h" @@ -57,12 +59,22 @@ namespace WebCore { using namespace HTMLNames; static bool hasBorderOutlineOrShadow(const RenderStyle*); -static bool hasBoxDecorations(const RenderStyle*); -static bool hasBoxDecorationsWithBackgroundImage(const RenderStyle*); +static bool hasBoxDecorationsOrBackground(const RenderStyle*); +static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle*); + +static inline bool is3DCanvas(RenderObject* renderer) +{ +#if ENABLE(3D_CANVAS) + if (renderer->isCanvas()) + return static_cast<HTMLCanvasElement*>(renderer->node())->is3D(); +#else + UNUSED_PARAM(renderer); +#endif + return false; +} RenderLayerBacking::RenderLayerBacking(RenderLayer* layer) : m_owningLayer(layer) - , m_hasDirectlyCompositedContent(false) , m_artificiallyInflatedBounds(false) { createGraphicsLayer(); @@ -86,11 +98,13 @@ void RenderLayerBacking::createGraphicsLayer() m_graphicsLayer->setName("Document Node"); else { if (renderer()->node()->isHTMLElement() && renderer()->node()->hasID()) - m_graphicsLayer->setName(renderer()->renderName() + String(" ") + static_cast<HTMLElement*>(renderer()->node())->getAttribute(idAttr)); + m_graphicsLayer->setName(renderer()->renderName() + String(" ") + static_cast<HTMLElement*>(renderer()->node())->getIDAttribute()); else m_graphicsLayer->setName(renderer()->renderName()); } - } else + } else if (m_owningLayer->isReflection()) + m_graphicsLayer->setName("Reflection"); + else m_graphicsLayer->setName("Anonymous Node"); #endif // NDEBUG @@ -186,30 +200,31 @@ bool RenderLayerBacking::updateGraphicsLayerConfiguration() if (updateMaskLayer(m_owningLayer->renderer()->hasMask())) m_graphicsLayer->setMaskLayer(m_maskLayer.get()); - m_hasDirectlyCompositedContent = false; - if (canUseDirectCompositing()) { - if (renderer()->isImage()) { - updateImageContents(); - m_hasDirectlyCompositedContent = true; - m_graphicsLayer->setDrawsContent(false); - } -#if ENABLE(3D_CANVAS) - else if (renderer()->isCanvas()) { - HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer()->node()); - if (canvas->is3D()) { - WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(canvas->renderingContext()); - if (context->graphicsContext3D()->platformGraphicsContext3D()) - m_graphicsLayer->setContentsToGraphicsContext3D(context->graphicsContext3D()); - } + if (m_owningLayer->hasReflection()) { + if (m_owningLayer->reflectionLayer()->backing()) { + GraphicsLayer* reflectionLayer = m_owningLayer->reflectionLayer()->backing()->graphicsLayer(); + m_graphicsLayer->setReplicatedByLayer(reflectionLayer); } -#endif + } else + m_graphicsLayer->setReplicatedByLayer(0); + + if (isDirectlyCompositedImage()) + updateImageContents(); - if (rendererHasBackground()) - m_graphicsLayer->setBackgroundColor(rendererBackgroundColor()); - else - m_graphicsLayer->clearBackgroundColor(); + if (renderer()->isEmbeddedObject() && toRenderEmbeddedObject(renderer())->allowsAcceleratedCompositing()) { + PluginWidget* pluginWidget = static_cast<PluginWidget*>(toRenderEmbeddedObject(renderer())->widget()); + m_graphicsLayer->setContentsToMedia(pluginWidget->platformLayer()); } +#if ENABLE(3D_CANVAS) + if (is3DCanvas(renderer())) { + HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer()->node()); + WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(canvas->renderingContext()); + if (context->graphicsContext3D()->platformGraphicsContext3D()) + m_graphicsLayer->setContentsToGraphicsContext3D(context->graphicsContext3D()); + } +#endif + return layerConfigChanged; } @@ -229,7 +244,7 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() updateLayerOpacity(renderer()->style()); RenderStyle* style = renderer()->style(); - m_graphicsLayer->setPreserves3D(style->transformStyle3D() == TransformStyle3DPreserve3D); + m_graphicsLayer->setPreserves3D(style->transformStyle3D() == TransformStyle3DPreserve3D && !renderer()->hasReflection()); m_graphicsLayer->setBackfaceVisibility(style->backfaceVisibility() == BackfaceVisibilityVisible); RenderLayer* compAncestor = m_owningLayer->ancestorCompositingLayer(); @@ -350,9 +365,19 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() m_foregroundLayer->setOffsetFromRenderer(foregroundOffset); } + if (m_owningLayer->reflectionLayer() && m_owningLayer->reflectionLayer()->isComposited()) { + RenderLayerBacking* reflectionBacking = m_owningLayer->reflectionLayer()->backing(); + reflectionBacking->updateGraphicsLayerGeometry(); + + // The reflection layer has the bounds of m_owningLayer->reflectionLayer(), + // but the reflected layer is the bounds of this layer, so we need to position it appropriately. + FloatRect layerBounds = compositedBounds(); + FloatRect reflectionLayerBounds = reflectionBacking->compositedBounds(); + reflectionBacking->graphicsLayer()->setReplicatedLayerPosition(FloatPoint() + (layerBounds.location() - reflectionLayerBounds.location())); + } + m_graphicsLayer->setContentsRect(contentsBox()); - if (!m_hasDirectlyCompositedContent) - m_graphicsLayer->setDrawsContent(!isSimpleContainerCompositingLayer() && !paintingGoesToWindow() && !m_artificiallyInflatedBounds); + m_graphicsLayer->setDrawsContent(containsPaintedContent()); } void RenderLayerBacking::updateInternalHierarchy() @@ -498,12 +523,12 @@ static bool hasBorderOutlineOrShadow(const RenderStyle* style) return style->hasBorder() || style->hasBorderRadius() || style->hasOutline() || style->hasAppearance() || style->boxShadow(); } -static bool hasBoxDecorations(const RenderStyle* style) +static bool hasBoxDecorationsOrBackground(const RenderStyle* style) { return hasBorderOutlineOrShadow(style) || style->hasBackground(); } -static bool hasBoxDecorationsWithBackgroundImage(const RenderStyle* style) +static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle* style) { return hasBorderOutlineOrShadow(style) || style->hasBackgroundImage(); } @@ -563,7 +588,7 @@ bool RenderLayerBacking::isSimpleContainerCompositingLayer() const // Reject anything that has a border, a border-radius or outline, // or any background (color or image). // FIXME: we could optimize layers for simple backgrounds. - if (hasBoxDecorations(style)) + if (hasBoxDecorationsOrBackground(style)) return false; // If we have got this far and the renderer has no children, then we're ok. @@ -580,7 +605,7 @@ bool RenderLayerBacking::isSimpleContainerCompositingLayer() const // Reject anything that has a border, a border-radius or outline, // or is not a simple background (no background, or solid color). - if (hasBoxDecorationsWithBackgroundImage(style)) + if (hasBoxDecorationsOrBackgroundImage(style)) return false; // Now look at the body's renderer. @@ -591,7 +616,7 @@ bool RenderLayerBacking::isSimpleContainerCompositingLayer() const style = bodyObject->style(); - if (hasBoxDecorationsWithBackgroundImage(style)) + if (hasBoxDecorationsOrBackgroundImage(style)) return false; // Ceck to see if all the body's children are compositing layers. @@ -608,9 +633,11 @@ bool RenderLayerBacking::isSimpleContainerCompositingLayer() const return true; } +// Conservative test for having no rendered children. bool RenderLayerBacking::hasNonCompositingContent() const { - // Conservative test for having no rendered children. + if (m_owningLayer->hasOverflowControls()) + return true; // Some HTML can cause whitespace text nodes to have renderers, like: // <div> @@ -627,7 +654,6 @@ bool RenderLayerBacking::hasNonCompositingContent() const } } - // FIXME: test for overflow controls. if (m_owningLayer->isStackingContext()) { // Use the m_hasCompositingDescendant bit to optimize? if (Vector<RenderLayer*>* negZOrderList = m_owningLayer->negZOrderList()) { @@ -661,51 +687,43 @@ bool RenderLayerBacking::hasNonCompositingContent() const return false; } -// A layer can use direct compositing if the render layer's object is a replaced object and has no children. -// This allows the GraphicsLayer to display the RenderLayer contents directly; it's used for images. -bool RenderLayerBacking::canUseDirectCompositing() const +bool RenderLayerBacking::containsPaintedContent() const { - RenderObject* renderObject = renderer(); - - // Canvas3D is always direct composited -#if ENABLE(3D_CANVAS) - if (renderer()->isCanvas()) { - HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer()->node()); - return canvas->is3D(); - } -#endif - - // Reject anything that isn't an image - if (!renderObject->isImage() && !renderObject->isVideo()) + if (isSimpleContainerCompositingLayer() || paintingGoesToWindow() || m_artificiallyInflatedBounds || m_owningLayer->isReflection()) return false; - - if (renderObject->hasMask() || renderObject->hasReflection()) + + if (isDirectlyCompositedImage()) return false; - // Video can use an inner layer even if it has box decorations; we draw those into another layer. - if (renderObject->isVideo()) - return true; - - // Reject anything that would require the image to be drawn via the GraphicsContext, - // like border, shadows etc. Solid background color is OK. - return !hasBoxDecorationsWithBackgroundImage(renderObject->style()); + // FIXME: we could optimize cases where the image, video or canvas is known to fill the border box entirely, + // and set background color on the layer in that case, instead of allocating backing store and painting. + if (renderer()->isVideo() || is3DCanvas(renderer())) + return hasBoxDecorationsOrBackground(renderer()->style()); + + return true; } - + +// An image can be directly compositing if it's the sole content of the layer, and has no box decorations +// that require painting. Direct compositing saves backing store. +bool RenderLayerBacking::isDirectlyCompositedImage() const +{ + RenderObject* renderObject = renderer(); + return renderObject->isImage() && !hasBoxDecorationsOrBackground(renderObject->style()); +} + void RenderLayerBacking::rendererContentChanged() { - if (canUseDirectCompositing()) { - if (renderer()->isImage()) - updateImageContents(); - else { + if (isDirectlyCompositedImage()) { + updateImageContents(); + return; + } + #if ENABLE(3D_CANVAS) - if (renderer()->isCanvas()) { - HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer()->node()); - if (canvas->is3D()) - m_graphicsLayer->setGraphicsContext3DNeedsDisplay(); - } -#endif - } + if (is3DCanvas(renderer())) { + m_graphicsLayer->setGraphicsContext3DNeedsDisplay(); + return; } +#endif } void RenderLayerBacking::updateImageContents() @@ -864,14 +882,6 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* m_owningLayer->updateLayerListsIfNeeded(); - // Paint the reflection first if we have one. - if (m_owningLayer->hasReflection()) { - // Mark that we are now inside replica painting. - m_owningLayer->setPaintingInsideReflection(true); - m_owningLayer->reflectionLayer()->paintLayer(rootLayer, context, paintDirtyRect, paintBehavior, paintingRoot, 0, RenderLayer::PaintLayerPaintingReflection); - m_owningLayer->setPaintingInsideReflection(false); - } - // Calculate the clip rects we should use. IntRect layerBounds, damageRect, clipRectToApply, outlineRect; m_owningLayer->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect); @@ -1062,7 +1072,7 @@ bool RenderLayerBacking::showRepaintCounter() const return compositor() ? compositor()->showRepaintCounter() : false; } -bool RenderLayerBacking::startAnimation(double beginTime, const Animation* anim, const KeyframeList& keyframes) +bool RenderLayerBacking::startAnimation(double timeOffset, const Animation* anim, const KeyframeList& keyframes) { bool hasOpacity = keyframes.containsProperty(CSSPropertyOpacity); bool hasTransform = keyframes.containsProperty(CSSPropertyWebkitTransform); @@ -1093,10 +1103,10 @@ bool RenderLayerBacking::startAnimation(double beginTime, const Animation* anim, bool didAnimateTransform = !hasTransform; bool didAnimateOpacity = !hasOpacity; - if (hasTransform && m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), anim, keyframes.animationName(), beginTime)) + if (hasTransform && m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), anim, keyframes.animationName(), timeOffset)) didAnimateTransform = true; - if (hasOpacity && m_graphicsLayer->addAnimation(opacityVector, IntSize(), anim, keyframes.animationName(), beginTime)) + if (hasOpacity && m_graphicsLayer->addAnimation(opacityVector, IntSize(), anim, keyframes.animationName(), timeOffset)) didAnimateOpacity = true; bool runningAcceleratedAnimation = didAnimateTransform && didAnimateOpacity; @@ -1106,7 +1116,7 @@ bool RenderLayerBacking::startAnimation(double beginTime, const Animation* anim, return runningAcceleratedAnimation; } -bool RenderLayerBacking::startTransition(double beginTime, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle) +bool RenderLayerBacking::startTransition(double timeOffset, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle) { bool didAnimate = false; ASSERT(property != cAnimateAll); @@ -1118,7 +1128,7 @@ bool RenderLayerBacking::startTransition(double beginTime, int property, const R opacityVector.insert(new FloatAnimationValue(0, compositingOpacity(fromStyle->opacity()))); opacityVector.insert(new FloatAnimationValue(1, compositingOpacity(toStyle->opacity()))); // The boxSize param is only used for transform animations (which can only run on RenderBoxes), so we pass an empty size here. - if (m_graphicsLayer->addAnimation(opacityVector, IntSize(), opacityAnim, String(), beginTime)) { + if (m_graphicsLayer->addAnimation(opacityVector, IntSize(), opacityAnim, String(), timeOffset)) { // To ensure that the correct opacity is visible when the animation ends, also set the final opacity. updateLayerOpacity(toStyle); didAnimate = true; @@ -1132,7 +1142,7 @@ bool RenderLayerBacking::startTransition(double beginTime, int property, const R KeyframeValueList transformVector(AnimatedPropertyWebkitTransform); transformVector.insert(new TransformAnimationValue(0, &fromStyle->transform())); transformVector.insert(new TransformAnimationValue(1, &toStyle->transform())); - if (m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), transformAnim, String(), beginTime)) { + if (m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), transformAnim, String(), timeOffset)) { // To ensure that the correct transform is visible when the animation ends, also set the final opacity. updateLayerTransform(toStyle); didAnimate = true; @@ -1162,9 +1172,9 @@ void RenderLayerBacking::animationFinished(const String& animationName) m_graphicsLayer->removeAnimationsForKeyframes(animationName); } -void RenderLayerBacking::animationPaused(const String& animationName) +void RenderLayerBacking::animationPaused(double timeOffset, const String& animationName) { - m_graphicsLayer->pauseAnimation(animationName); + m_graphicsLayer->pauseAnimation(animationName, timeOffset); } void RenderLayerBacking::transitionFinished(int property) diff --git a/WebCore/rendering/RenderLayerBacking.h b/WebCore/rendering/RenderLayerBacking.h index 2bbb37f..7aea926 100644 --- a/WebCore/rendering/RenderLayerBacking.h +++ b/WebCore/rendering/RenderLayerBacking.h @@ -97,10 +97,10 @@ public: void rendererContentChanged(); // Interface to start, finish, suspend and resume animations and transitions - bool startAnimation(double beginTime, const Animation* anim, const KeyframeList& keyframes); - bool startTransition(double beginTime, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle); + bool startAnimation(double timeOffset, const Animation* anim, const KeyframeList& keyframes); + bool startTransition(double timeOffset, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle); void animationFinished(const String& name); - void animationPaused(const String& name); + void animationPaused(double timeOffset, const String& name); void transitionFinished(int property); void suspendAnimations(double time = 0); @@ -149,12 +149,12 @@ private: // Return the opacity value that this layer should use for compositing. float compositingOpacity(float rendererOpacity) const; - // Returns true if this RenderLayer only has content that can be rendered directly - // by the compositing layer, without drawing (e.g. solid background color). + // Returns true if this compositing layer has no visible content. bool isSimpleContainerCompositingLayer() const; - // Returns true if we can optimize the RenderLayer to draw the replaced content - // directly into a compositing buffer - bool canUseDirectCompositing() const; + // Returns true if this layer has content that needs to be rendered by painting into the backing store. + bool containsPaintedContent() const; + // Returns true if the RenderLayer just contains an image that we can composite directly. + bool isDirectlyCompositedImage() const; void updateImageContents(); bool rendererHasBackground() const; @@ -179,7 +179,6 @@ private: IntRect m_compositedBounds; - bool m_hasDirectlyCompositedContent; bool m_artificiallyInflatedBounds; // bounds had to be made non-zero to make transform-origin work }; diff --git a/WebCore/rendering/RenderLayerCompositor.cpp b/WebCore/rendering/RenderLayerCompositor.cpp index 8ce59cb..2730114 100644 --- a/WebCore/rendering/RenderLayerCompositor.cpp +++ b/WebCore/rendering/RenderLayerCompositor.cpp @@ -29,6 +29,7 @@ #include "RenderLayerCompositor.h" #include "AnimationController.h" +#include "Chrome.h" #include "ChromeClient.h" #include "CSSPropertyNames.h" #include "Frame.h" @@ -37,7 +38,9 @@ #include "HitTestResult.h" #include "HTMLCanvasElement.h" #include "Page.h" +#include "RenderEmbeddedObject.h" #include "RenderLayerBacking.h" +#include "RenderReplica.h" #include "RenderVideo.h" #include "RenderView.h" #include "Settings.h" @@ -58,6 +61,8 @@ bool WebCoreHas3DRendering = true; namespace WebCore { +using namespace HTMLNames; + struct CompositingState { CompositingState(RenderLayer* compAncestor) : m_compositingAncestor(compAncestor) @@ -147,16 +152,30 @@ void RenderLayerCompositor::scheduleSync() page->chrome()->client()->scheduleCompositingLayerSync(); } -void RenderLayerCompositor::updateCompositingLayers(RenderLayer* updateRoot) +void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType updateType, RenderLayer* updateRoot) { - // When m_compositingConsultsOverlap is true, then layer positions affect compositing, - // so we can only bail here when we're not looking at overlap. - if (!m_compositingLayersNeedRebuild && !m_compositingConsultsOverlap) + bool checkForHierarchyUpdate = false; + bool needGeometryUpdate = false; + + switch (updateType) { + case CompositingUpdateAfterLayoutOrStyleChange: + case CompositingUpdateOnPaitingOrHitTest: + checkForHierarchyUpdate = true; + break; + case CompositingUpdateOnScroll: + if (m_compositingConsultsOverlap) + checkForHierarchyUpdate = true; // Overlap can change with scrolling, so need to check for hierarchy updates. + + needGeometryUpdate = true; + break; + } + + if (!checkForHierarchyUpdate && !needGeometryUpdate) return; ASSERT(inCompositingMode()); - bool needLayerRebuild = m_compositingLayersNeedRebuild; + bool needHierarchyUpdate = m_compositingLayersNeedRebuild; if (!updateRoot) { // Only clear the flag if we're updating the entire hierarchy. m_compositingLayersNeedRebuild = false; @@ -169,24 +188,22 @@ void RenderLayerCompositor::updateCompositingLayers(RenderLayer* updateRoot) double startTime = WTF::currentTime(); #endif - // Go through the layers in presentation order, so that we can compute which - // RLs need compositing layers. - // FIXME: we could maybe do this in one pass, but the parenting logic would be more - // complex. - { + if (checkForHierarchyUpdate) { + // Go through the layers in presentation order, so that we can compute which RenderLayers need compositing layers. + // FIXME: we could maybe do this and the hierarchy udpate in one pass, but the parenting logic would be more complex. CompositingState compState(updateRoot); - bool layersChanged; + bool layersChanged = false; if (m_compositingConsultsOverlap) { OverlapMap overlapTestRequestMap; computeCompositingRequirements(updateRoot, &overlapTestRequestMap, compState, layersChanged); } else computeCompositingRequirements(updateRoot, 0, compState, layersChanged); - needLayerRebuild |= layersChanged; + needHierarchyUpdate |= layersChanged; } - if (needLayerRebuild) { - // Now updated and parent the compositing layers. + if (needHierarchyUpdate) { + // Update the hierarchy of the compositing layers. CompositingState compState(updateRoot); Vector<GraphicsLayer*> childList; rebuildCompositingLayerTree(updateRoot, compState, childList); @@ -194,8 +211,9 @@ void RenderLayerCompositor::updateCompositingLayers(RenderLayer* updateRoot) // Host the document layer in the RenderView's root layer. if (updateRoot == rootRenderLayer() && !childList.isEmpty()) m_rootPlatformLayer->setChildren(childList); - } else { - // We just need to do a geometry update. + } else if (needGeometryUpdate) { + // We just need to do a geometry update. This is only used for position:fixed scrolling; + // most of the time, geometry is updated via RenderLayer::styleChanged(). updateLayerTreeGeometry(updateRoot); } @@ -233,6 +251,17 @@ bool RenderLayerCompositor::updateBacking(RenderLayer* layer, CompositingChangeR } } else { if (layer->backing()) { + // If we're removing backing on a reflection, clear the source GraphicsLayer's pointer to + // its replica GraphicsLayer. In practice this should never happen because reflectee and reflection + // are both either composited, or not composited. + if (layer->isReflection()) { + RenderLayer* sourceLayer = toRenderBoxModelObject(layer->renderer()->parent())->layer(); + if (RenderLayerBacking* backing = sourceLayer->backing()) { + ASSERT(backing->graphicsLayer()->replicaLayer() == layer->backing()->graphicsLayer()); + backing->graphicsLayer()->setReplicatedByLayer(0); + } + } + layer->clearBacking(); layerChanged = true; @@ -533,6 +562,9 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); } + if (layer->reflectionLayer()) + layer->reflectionLayer()->setMustOverlapCompositedLayers(needsToBeComposited(layer)); + // Subsequent layers in the parent stacking context also need to composite. if (childState.m_subtreeIsCompositing) compositingState.m_subtreeIsCompositing = true; @@ -549,6 +581,9 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O // Update backing now, so that we can use isComposited() reliably during tree traversal in rebuildCompositingLayerTree(). if (updateBacking(layer, CompositingChangeRepaintNow)) layersChanged = true; + + if (layer->reflectionLayer() && updateLayerCompositingState(layer->reflectionLayer(), CompositingChangeRepaintNow)) + layersChanged = true; } void RenderLayerCompositor::setCompositingParent(RenderLayer* childLayer, RenderLayer* parentLayer) @@ -595,9 +630,7 @@ void RenderLayerCompositor::parentInRootLayer(RenderLayer* layer) #if ENABLE(VIDEO) bool RenderLayerCompositor::canAccelerateVideoRendering(RenderVideo* o) const { - // FIXME: ideally we need to look at all ancestors for mask or video. But for now, - // just bail on the obvious cases. - if (o->hasReflection() || !m_hasAcceleratedCompositing) + if (!m_hasAcceleratedCompositing) return false; return o->supportsAcceleratedRendering(); @@ -615,6 +648,12 @@ void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, cons // The compositing state of all our children has been updated already, so now // we can compute and cache the composited bounds for this layer. layerBacking->updateCompositedBounds(); + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (reflection->backing()) + reflection->backing()->updateCompositedBounds(); + } + layerBacking->updateGraphicsLayerConfiguration(); layerBacking->updateGraphicsLayerGeometry(); @@ -688,8 +727,10 @@ void RenderLayerCompositor::updateLayerTreeGeometry(RenderLayer* layer) // we can compute and cache the composited bounds for this layer. layerBacking->updateCompositedBounds(); - if (layer->reflectionLayer()) - layer->reflectionLayer()->backing()->updateCompositedBounds(); + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (reflection->backing()) + reflection->backing()->updateCompositedBounds(); + } layerBacking->updateGraphicsLayerConfiguration(); layerBacking->updateGraphicsLayerGeometry(); @@ -730,12 +771,21 @@ void RenderLayerCompositor::updateCompositingDescendantGeometry(RenderLayer* com if (layer != compositingAncestor) { if (RenderLayerBacking* layerBacking = layer->backing()) { layerBacking->updateCompositedBounds(); + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (reflection->backing()) + reflection->backing()->updateCompositedBounds(); + } + layerBacking->updateGraphicsLayerGeometry(); if (updateDepth == RenderLayerBacking::CompositingChildren) return; } } + if (layer->reflectionLayer()) + updateCompositingDescendantGeometry(compositingAncestor, layer->reflectionLayer(), updateDepth); + if (!layer->hasCompositingDescendant()) return; @@ -884,9 +934,16 @@ bool RenderLayerCompositor::needsToBeComposited(const RenderLayer* layer) const // Use needsToBeComposited() to determine if a RL actually needs a compositing layer. // static bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer) const -{ +{ + RenderObject* renderer = layer->renderer(); + // The compositing state of a reflection should match that of its reflected layer. + if (layer->isReflection()) { + renderer = renderer->parent(); // The RenderReplica's parent is the object being reflected. + layer = toRenderBoxModelObject(renderer)->layer(); + } // The root layer always has a compositing layer, but it may not have backing. return (inCompositingMode() && layer->isRootLayer()) || +<<<<<<< HEAD requiresCompositingForTransform(layer->renderer()) || requiresCompositingForVideo(layer->renderer()) || requiresCompositingForCanvas(layer->renderer()) || @@ -895,8 +952,15 @@ bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer) c (layer->renderer()->isPositioned() && layer->renderer()->style()->position() == FixedPosition) || #endif +======= + requiresCompositingForTransform(renderer) || + requiresCompositingForVideo(renderer) || + requiresCompositingForCanvas(renderer) || + requiresCompositingForPlugin(renderer) || + renderer->style()->backfaceVisibility() == BackfaceVisibilityHidden || +>>>>>>> webkit.org at r54127 clipsCompositingDescendants(layer) || - requiresCompositingForAnimation(layer->renderer()); + requiresCompositingForAnimation(renderer); } // Return true if the given layer has some ancestor in the RenderLayer hierarchy that clips, @@ -959,6 +1023,8 @@ bool RenderLayerCompositor::requiresCompositingForVideo(RenderObject* renderer) RenderVideo* video = toRenderVideo(renderer); return canAccelerateVideoRendering(video); } +#else + UNUSED_PARAM(renderer); #endif return false; } @@ -976,6 +1042,11 @@ bool RenderLayerCompositor::requiresCompositingForCanvas(RenderObject* renderer) return false; } +bool RenderLayerCompositor::requiresCompositingForPlugin(RenderObject* renderer) const +{ + return renderer->isEmbeddedObject() && toRenderEmbeddedObject(renderer)->allowsAcceleratedCompositing(); +} + bool RenderLayerCompositor::requiresCompositingForAnimation(RenderObject* renderer) const { if (AnimationController* animController = renderer->animation()) { @@ -987,7 +1058,7 @@ bool RenderLayerCompositor::requiresCompositingForAnimation(RenderObject* render bool RenderLayerCompositor::requiresCompositingWhenDescendantsAreCompositing(RenderObject* renderer) const { - return renderer->hasTransform() || renderer->isTransparent() || renderer->hasMask(); + return renderer->hasTransform() || renderer->isTransparent() || renderer->hasMask() || renderer->hasReflection(); } // If an element has negative z-index children, those children render in front of the diff --git a/WebCore/rendering/RenderLayerCompositor.h b/WebCore/rendering/RenderLayerCompositor.h index 73683f3..5f1a178 100644 --- a/WebCore/rendering/RenderLayerCompositor.h +++ b/WebCore/rendering/RenderLayerCompositor.h @@ -38,6 +38,12 @@ class GraphicsLayer; class RenderVideo; #endif +enum CompositingUpdateType { + CompositingUpdateAfterLayoutOrStyleChange, + CompositingUpdateOnPaitingOrHitTest, + CompositingUpdateOnScroll +}; + // RenderLayerCompositor manages the hierarchy of // composited RenderLayers. It determines which RenderLayers // become compositing, and creates and maintains a hierarchy of @@ -47,7 +53,6 @@ class RenderVideo; class RenderLayerCompositor { public: - RenderLayerCompositor(RenderView*); ~RenderLayerCompositor(); @@ -80,7 +85,7 @@ public: void scheduleSync(); // Rebuild the tree of compositing layers - void updateCompositingLayers(RenderLayer* updateRoot = 0); + void updateCompositingLayers(CompositingUpdateType = CompositingUpdateAfterLayoutOrStyleChange, RenderLayer* updateRoot = 0); // Update the compositing state of the given layer. Returns true if that state changed. enum CompositingChangeRepaint { CompositingChangeRepaintNow, CompositingChangeWillRepaintLater }; @@ -173,6 +178,7 @@ private: bool requiresCompositingForTransform(RenderObject*) const; bool requiresCompositingForVideo(RenderObject*) const; bool requiresCompositingForCanvas(RenderObject*) const; + bool requiresCompositingForPlugin(RenderObject*) const; bool requiresCompositingWhenDescendantsAreCompositing(RenderObject*) const; private: diff --git a/WebCore/rendering/RenderListItem.cpp b/WebCore/rendering/RenderListItem.cpp index 539b8c7..54a7dd2 100644 --- a/WebCore/rendering/RenderListItem.cpp +++ b/WebCore/rendering/RenderListItem.cpp @@ -1,7 +1,7 @@ /** * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) * * This library is free software; you can redistribute it and/or @@ -51,8 +51,8 @@ void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* old { RenderBlock::styleDidChange(diff, oldStyle); - if (style()->listStyleType() != LNONE || - (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) { + if (style()->listStyleType() != NoneListStyle + || (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) { RefPtr<RenderStyle> newStyle = RenderStyle::create(); // The marker always inherits from the list item, regardless of where it might end // up (e.g., in some deeply nested line box). See CSS3 spec. @@ -75,11 +75,16 @@ void RenderListItem::destroy() RenderBlock::destroy(); } +static bool isList(Node* node) +{ + return (node->hasTagName(ulTag) || node->hasTagName(olTag)); +} + static Node* enclosingList(Node* node) { Node* parent = node->parentNode(); for (Node* n = parent; n; n = n->parentNode()) - if (n->hasTagName(ulTag) || n->hasTagName(olTag)) + if (isList(n)) return n; // If there's no actual <ul> or <ol> list element, then our parent acts as // our list for purposes of determining what other list items should be @@ -87,22 +92,38 @@ static Node* enclosingList(Node* node) return parent; } +static Node* enclosingList(const RenderObject* renderer) +{ + Node* node = renderer->node(); + if (node) + return enclosingList(node); + + renderer = renderer->parent(); + while (renderer && !renderer->node()) + renderer = renderer->parent(); + + node = renderer->node(); + if (isList(node)) + return node; + + return enclosingList(node); +} + static RenderListItem* previousListItem(Node* list, const RenderListItem* item) { - for (Node* node = item->node()->traversePreviousNode(); node != list; node = node->traversePreviousNode()) { - RenderObject* renderer = node->renderer(); - if (!renderer || !renderer->isListItem()) + for (RenderObject* renderer = item->previousInPreOrder(); renderer != list->renderer(); renderer = renderer->previousInPreOrder()) { + if (!renderer->isListItem()) continue; - Node* otherList = enclosingList(node); + Node* otherList = enclosingList(renderer); // This item is part of our current list, so it's what we're looking for. if (list == otherList) return toRenderListItem(renderer); // We found ourself inside another list; lets skip the rest of it. - // Use traverseNextNode() here because the other list itself may actually + // Use nextInPreOrder() here because the other list itself may actually // be a list item itself. We need to examine it, so we do this to counteract - // the traversePreviousNode() that will be done by the loop. + // the previousInPreOrder() that will be done by the loop. if (otherList) - node = otherList->traverseNextNode(); + renderer = otherList->renderer()->nextInPreOrder(); } return 0; } @@ -111,7 +132,7 @@ inline int RenderListItem::calcValue() const { if (m_hasExplicitValue) return m_explicitValue; - Node* list = enclosingList(node()); + Node* list = enclosingList(this); // FIXME: This recurses to a possible depth of the length of the list. // That's not good -- we need to change this to an iterative algorithm. if (RenderListItem* previousItem = previousListItem(list, this)) @@ -322,6 +343,8 @@ void RenderListItem::explicitValueChanged() void RenderListItem::setExplicitValue(int value) { + ASSERT(node()); + if (m_hasExplicitValue && m_explicitValue == value) return; m_explicitValue = value; @@ -332,6 +355,8 @@ void RenderListItem::setExplicitValue(int value) void RenderListItem::clearExplicitValue() { + ASSERT(node()); + if (!m_hasExplicitValue) return; m_hasExplicitValue = false; diff --git a/WebCore/rendering/RenderListMarker.cpp b/WebCore/rendering/RenderListMarker.cpp index eab4404..f67bc0f 100644 --- a/WebCore/rendering/RenderListMarker.cpp +++ b/WebCore/rendering/RenderListMarker.cpp @@ -3,6 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * Copyright (C) 2010 Daniel Bates (dbates@intudata.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -97,6 +98,11 @@ static String toAlphabetic(int number, const UChar* alphabet, int alphabetSize) return String(&letters[lettersSize - length], length); } +template <size_t size> static inline String toAlphabetic(int number, const UChar(&alphabet)[size]) +{ + return toAlphabetic(number, alphabet, size); +} + static int toHebrewUnder1000(int number, UChar letters[5]) { // FIXME: CSS3 mentions various refinements not implemented here. @@ -337,58 +343,128 @@ static String toCJKIdeographic(int number, const UChar table[16]) return String(characters, length); } +static UChar listMarkerSuffix(EListStyleType type) +{ + // Note, the following switch statement has been explicitly + // grouped by list-style-type suffix. + switch (type) { + case NoneListStyle: + case Disc: + case Circle: + case Square: + ASSERT_NOT_REACHED(); + return ' '; + case Afar: + case Amharic: + case AmharicAbegede: + case Ethiopic: + case EthiopicAbegede: + case EthiopicAbegedeAmEt: + case EthiopicAbegedeGez: + case EthiopicAbegedeTiEr: + case EthiopicAbegedeTiEt: + case EthiopicHalehameAaEr: + case EthiopicHalehameAaEt: + case EthiopicHalehameAmEt: + case EthiopicHalehameGez: + case EthiopicHalehameOmEt: + case EthiopicHalehameSidEt: + case EthiopicHalehameSoEt: + case EthiopicHalehameTiEr: + case EthiopicHalehameTiEt: + case EthiopicHalehameTig: + case Oromo: + case Sidama: + case Somali: + case Tigre: + case TigrinyaEr: + case TigrinyaErAbegede: + case TigrinyaEt: + case TigrinyaEtAbegede: + return ethiopicPrefaceColon; + case Armenian: + case CJKIdeographic: + case CjkEarthlyBranch: + case CjkHeavenlyStem: + case DecimalLeadingZero: + case DecimalListStyle: + case Georgian: + case Hangul: + case HangulConsonant: + case Hebrew: + case Hiragana: + case HiraganaIroha: + case Katakana: + case KatakanaIroha: + case LowerAlpha: + case LowerGreek: + case LowerLatin: + case LowerNorwegian: + case LowerRoman: + case UpperAlpha: + case UpperGreek: + case UpperLatin: + case UpperNorwegian: + case UpperRoman: + return '.'; + } + + ASSERT_NOT_REACHED(); + return '.'; +} + String listMarkerText(EListStyleType type, int value) { switch (type) { - case LNONE: + case NoneListStyle: return ""; // We use the same characters for text security. // See RenderText::setInternalString. - case CIRCLE: + case Circle: return String(&whiteBullet, 1); - case DISC: + case Disc: return String(&bullet, 1); - case SQUARE: + case Square: // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE // instead, but I think this looks better. return String(&blackSquare, 1); - case LDECIMAL: + case DecimalListStyle: return String::number(value); - case DECIMAL_LEADING_ZERO: + case DecimalLeadingZero: if (value < -9 || value > 9) return String::number(value); if (value < 0) return "-0" + String::number(-value); // -01 to -09 return "0" + String::number(value); // 00 to 09 - case LOWER_ALPHA: - case LOWER_LATIN: { + case LowerAlpha: + case LowerLatin: { static const UChar lowerLatinAlphabet[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; - return toAlphabetic(value, lowerLatinAlphabet, 26); + return toAlphabetic(value, lowerLatinAlphabet); } - case UPPER_ALPHA: - case UPPER_LATIN: { + case UpperAlpha: + case UpperLatin: { static const UChar upperLatinAlphabet[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; - return toAlphabetic(value, upperLatinAlphabet, 26); + return toAlphabetic(value, upperLatinAlphabet); } - case LOWER_GREEK: { + case LowerGreek: { static const UChar lowerGreekAlphabet[24] = { 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9 }; - return toAlphabetic(value, lowerGreekAlphabet, 24); + return toAlphabetic(value, lowerGreekAlphabet); } - case HIRAGANA: { + case Hiragana: { // FIXME: This table comes from the CSS3 draft, and is probably // incorrect, given the comments in that draft. static const UChar hiraganaAlphabet[48] = { @@ -399,9 +475,9 @@ String listMarkerText(EListStyleType type, int value) 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093 }; - return toAlphabetic(value, hiraganaAlphabet, 48); + return toAlphabetic(value, hiraganaAlphabet); } - case HIRAGANA_IROHA: { + case HiraganaIroha: { // FIXME: This table comes from the CSS3 draft, and is probably // incorrect, given the comments in that draft. static const UChar hiraganaIrohaAlphabet[47] = { @@ -412,9 +488,9 @@ String listMarkerText(EListStyleType type, int value) 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081, 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059 }; - return toAlphabetic(value, hiraganaIrohaAlphabet, 47); + return toAlphabetic(value, hiraganaIrohaAlphabet); } - case KATAKANA: { + case Katakana: { // FIXME: This table comes from the CSS3 draft, and is probably // incorrect, given the comments in that draft. static const UChar katakanaAlphabet[48] = { @@ -425,9 +501,9 @@ String listMarkerText(EListStyleType type, int value) 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3 }; - return toAlphabetic(value, katakanaAlphabet, 48); + return toAlphabetic(value, katakanaAlphabet); } - case KATAKANA_IROHA: { + case KatakanaIroha: { // FIXME: This table comes from the CSS3 draft, and is probably // incorrect, given the comments in that draft. static const UChar katakanaIrohaAlphabet[47] = { @@ -438,10 +514,187 @@ String listMarkerText(EListStyleType type, int value) 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1, 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9 }; - return toAlphabetic(value, katakanaIrohaAlphabet, 47); + return toAlphabetic(value, katakanaIrohaAlphabet); } - case CJK_IDEOGRAPHIC: { + case Afar: + case EthiopicHalehameAaEt: + case EthiopicHalehameAaEr: { + static const UChar ethiopicHalehameAaErAlphabet[18] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290, + 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348 + }; + return toAlphabetic(value, ethiopicHalehameAaErAlphabet); + } + case Amharic: + case EthiopicHalehameAmEt: { + static const UChar ethiopicHalehameAmEtAlphabet[33] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240, + 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, + 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, + 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameAmEtAlphabet); + } + case AmharicAbegede: + case EthiopicAbegedeAmEt: { + static const UChar ethiopicAbegedeAmEtAlphabet[33] = { + 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0, + 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290, + 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238, + 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350 + }; + return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet); + } + case CjkEarthlyBranch: { + static const UChar cjkEarthlyBranchAlphabet[12] = { + 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533, + 0x9149, 0x620C, 0x4EA5 + }; + return toAlphabetic(value, cjkEarthlyBranchAlphabet); + } + case CjkHeavenlyStem: { + static const UChar cjkHeavenlyStemAlphabet[10] = { + 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC, + 0x7678 + }; + return toAlphabetic(value, cjkHeavenlyStemAlphabet); + } + case Ethiopic: + case EthiopicHalehameGez: { + static const UChar ethiopicHalehameGezAlphabet[26] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260, + 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8, + 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameGezAlphabet); + } + case EthiopicAbegede: + case EthiopicAbegedeGez: { + static const UChar ethiopicAbegedeGezAlphabet[26] = { + 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320, + 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338, + 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350 + }; + return toAlphabetic(value, ethiopicAbegedeGezAlphabet); + } + case HangulConsonant: { + static const UChar hangulConsonantAlphabet[14] = { + 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148, + 0x314A, 0x314B, 0x314C, 0x314D, 0x314E + }; + return toAlphabetic(value, hangulConsonantAlphabet); + } + case Hangul: { + static const UChar hangulAlphabet[14] = { + 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790, + 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558 + }; + return toAlphabetic(value, hangulAlphabet); + } + case Oromo: + case EthiopicHalehameOmEt: { + static const UChar ethiopicHalehameOmEtAlphabet[25] = { + 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270, + 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8, + 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348 + }; + return toAlphabetic(value, ethiopicHalehameOmEtAlphabet); + } + case Sidama: + case EthiopicHalehameSidEt: { + static const UChar ethiopicHalehameSidEtAlphabet[26] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, + 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, + 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348 + }; + return toAlphabetic(value, ethiopicHalehameSidEtAlphabet); + } + case Somali: + case EthiopicHalehameSoEt: { + static const UChar ethiopicHalehameSoEtAlphabet[22] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, + 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, + 0x1300, 0x1308, 0x1338, 0x1348 + }; + return toAlphabetic(value, ethiopicHalehameSoEtAlphabet); + } + case Tigre: + case EthiopicHalehameTig: { + static const UChar ethiopicHalehameTigAlphabet[27] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, + 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8, + 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameTigAlphabet); + } + case TigrinyaEr: + case EthiopicHalehameTiEr: { + static const UChar ethiopicHalehameTiErAlphabet[31] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250, + 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8, + 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, + 0x1330, 0x1338, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameTiErAlphabet); + } + case TigrinyaErAbegede: + case EthiopicAbegedeTiEr: { + static const UChar ethiopicAbegedeTiErAlphabet[31] = { + 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0, + 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290, + 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238, + 0x1270, 0x1278, 0x1330, 0x1350 + }; + return toAlphabetic(value, ethiopicAbegedeTiErAlphabet); + } + case TigrinyaEt: + case EthiopicHalehameTiEt: { + static const UChar ethiopicHalehameTiEtAlphabet[34] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240, + 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, + 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, + 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameTiEtAlphabet); + } + case TigrinyaEtAbegede: + case EthiopicAbegedeTiEt: { + static const UChar ethiopicAbegedeTiEtAlphabet[34] = { + 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0, + 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290, + 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, + 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350 + }; + return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet); + } + case UpperGreek: { + static const UChar upperGreekAlphabet[24] = { + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, + 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, + 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9 + }; + return toAlphabetic(value, upperGreekAlphabet); + } + case LowerNorwegian: { + static const UChar lowerNorwegianAlphabet[29] = { + 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, + 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, + 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E6, + 0x00F8, 0x00E5 + }; + return toAlphabetic(value, lowerNorwegianAlphabet); + } + case UpperNorwegian: { + static const UChar upperNorwegianAlphabet[29] = { + 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, + 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, + 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C6, + 0x00D8, 0x00C5 + }; + return toAlphabetic(value, upperNorwegianAlphabet); + } + case CJKIdeographic: { static const UChar traditionalChineseInformalTable[16] = { 0x842C, 0x5104, 0x5146, 0x5341, 0x767E, 0x5343, @@ -451,19 +704,19 @@ String listMarkerText(EListStyleType type, int value) return toCJKIdeographic(value, traditionalChineseInformalTable); } - case LOWER_ROMAN: + case LowerRoman: return toRoman(value, false); - case UPPER_ROMAN: + case UpperRoman: return toRoman(value, true); - case ARMENIAN: + case Armenian: // CSS3 says "armenian" means "lower-armenian". // But the CSS2.1 test suite contains uppercase test results for "armenian", // so we'll match the test suite. return toArmenian(value, true); - case GEORGIAN: + case Georgian: return toGeorgian(value); - case HEBREW: + case Hebrew: return toHebrew(value); } @@ -519,6 +772,15 @@ bool RenderListMarker::isImage() const return m_image && !m_image->errorOccurred(); } +IntRect RenderListMarker::localSelectionRect() +{ + InlineBox* box = inlineBoxWrapper(); + if (!box) + return IntRect(); + RootInlineBox* root = box->root(); + return IntRect(x(), root->selectionTop() - y(), width(), root->selectionHeight()); +} + void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) { if (paintInfo.phase != PaintPhaseForeground) @@ -547,8 +809,9 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) #endif context->drawImage(m_image->image(this, marker.size()), style()->colorSpace(), marker.location()); if (selectionState() != SelectionNone) { - // FIXME: selectionRect() is in absolute, not painting coordinates. - context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace()); + IntRect selRect = localSelectionRect(); + selRect.move(tx, ty); + context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace()); } return; } @@ -560,8 +823,9 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) #endif if (selectionState() != SelectionNone) { - // FIXME: selectionRect() is in absolute, not painting coordinates. - context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace()); + IntRect selRect = localSelectionRect(); + selRect.move(tx, ty); + context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace()); } const Color color(style()->color()); @@ -570,36 +834,71 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) context->setStrokeThickness(1.0f); context->setFillColor(color, style()->colorSpace()); - switch (style()->listStyleType()) { - case DISC: + EListStyleType type = style()->listStyleType(); + switch (type) { + case Disc: context->drawEllipse(marker); return; - case CIRCLE: + case Circle: context->setFillColor(Color::transparent, DeviceColorSpace); context->drawEllipse(marker); return; - case SQUARE: + case Square: context->drawRect(marker); return; - case LNONE: + case NoneListStyle: return; - case ARMENIAN: - case CJK_IDEOGRAPHIC: - case DECIMAL_LEADING_ZERO: - case GEORGIAN: - case HEBREW: - case HIRAGANA: - case HIRAGANA_IROHA: - case KATAKANA: - case KATAKANA_IROHA: - case LDECIMAL: - case LOWER_ALPHA: - case LOWER_GREEK: - case LOWER_LATIN: - case LOWER_ROMAN: - case UPPER_ALPHA: - case UPPER_LATIN: - case UPPER_ROMAN: + case Afar: + case Amharic: + case AmharicAbegede: + case Armenian: + case CJKIdeographic: + case CjkEarthlyBranch: + case CjkHeavenlyStem: + case DecimalLeadingZero: + case DecimalListStyle: + case Ethiopic: + case EthiopicAbegede: + case EthiopicAbegedeAmEt: + case EthiopicAbegedeGez: + case EthiopicAbegedeTiEr: + case EthiopicAbegedeTiEt: + case EthiopicHalehameAaEr: + case EthiopicHalehameAaEt: + case EthiopicHalehameAmEt: + case EthiopicHalehameGez: + case EthiopicHalehameOmEt: + case EthiopicHalehameSidEt: + case EthiopicHalehameSoEt: + case EthiopicHalehameTiEr: + case EthiopicHalehameTiEt: + case EthiopicHalehameTig: + case Georgian: + case Hangul: + case HangulConsonant: + case Hebrew: + case Hiragana: + case HiraganaIroha: + case Katakana: + case KatakanaIroha: + case LowerAlpha: + case LowerGreek: + case LowerLatin: + case LowerNorwegian: + case LowerRoman: + case Oromo: + case Sidama: + case Somali: + case Tigre: + case TigrinyaEr: + case TigrinyaErAbegede: + case TigrinyaEt: + case TigrinyaEtAbegede: + case UpperAlpha: + case UpperGreek: + case UpperLatin: + case UpperNorwegian: + case UpperRoman: break; } if (m_text.isEmpty()) @@ -620,16 +919,17 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) } const Font& font = style()->font(); + const UChar suffix = listMarkerSuffix(type); if (style()->direction() == LTR) { int width = font.width(textRun); context->drawText(style()->font(), textRun, marker.location()); - const UChar periodSpace[2] = { '.', ' ' }; - context->drawText(style()->font(), TextRun(periodSpace, 2), marker.location() + IntSize(width, 0)); + UChar suffixSpace[2] = { suffix, ' ' }; + context->drawText(style()->font(), TextRun(suffixSpace, 2), marker.location() + IntSize(width, 0)); } else { - const UChar spacePeriod[2] = { ' ', '.' }; - TextRun spacePeriodRun(spacePeriod, 2); - int width = font.width(spacePeriodRun); - context->drawText(style()->font(), spacePeriodRun, marker.location()); + UChar spaceSuffix[2] = { ' ', suffix }; + TextRun spaceSuffixRun(spaceSuffix, 2); + int width = font.width(spaceSuffixRun); + context->drawText(style()->font(), spaceSuffixRun, marker.location()); context->drawText(style()->font(), textRun, marker.location() + IntSize(width, 0)); } } @@ -693,39 +993,73 @@ void RenderListMarker::calcPrefWidths() int width = 0; EListStyleType type = style()->listStyleType(); switch (type) { - case LNONE: + case NoneListStyle: break; - case CIRCLE: - case DISC: - case SQUARE: + case Circle: + case Disc: + case Square: m_text = listMarkerText(type, 0); // value is ignored for these types width = (font.ascent() * 2 / 3 + 1) / 2 + 2; break; - case ARMENIAN: - case CJK_IDEOGRAPHIC: - case DECIMAL_LEADING_ZERO: - case GEORGIAN: - case HEBREW: - case HIRAGANA: - case HIRAGANA_IROHA: - case KATAKANA: - case KATAKANA_IROHA: - case LDECIMAL: - case LOWER_ALPHA: - case LOWER_GREEK: - case LOWER_LATIN: - case LOWER_ROMAN: - case UPPER_ALPHA: - case UPPER_LATIN: - case UPPER_ROMAN: + case Afar: + case Amharic: + case AmharicAbegede: + case Armenian: + case CJKIdeographic: + case CjkEarthlyBranch: + case CjkHeavenlyStem: + case DecimalLeadingZero: + case DecimalListStyle: + case Ethiopic: + case EthiopicAbegede: + case EthiopicAbegedeAmEt: + case EthiopicAbegedeGez: + case EthiopicAbegedeTiEr: + case EthiopicAbegedeTiEt: + case EthiopicHalehameAaEr: + case EthiopicHalehameAaEt: + case EthiopicHalehameAmEt: + case EthiopicHalehameGez: + case EthiopicHalehameOmEt: + case EthiopicHalehameSidEt: + case EthiopicHalehameSoEt: + case EthiopicHalehameTiEr: + case EthiopicHalehameTiEt: + case EthiopicHalehameTig: + case Georgian: + case Hangul: + case HangulConsonant: + case Hebrew: + case Hiragana: + case HiraganaIroha: + case Katakana: + case KatakanaIroha: + case LowerAlpha: + case LowerGreek: + case LowerLatin: + case LowerNorwegian: + case LowerRoman: + case Oromo: + case Sidama: + case Somali: + case Tigre: + case TigrinyaEr: + case TigrinyaErAbegede: + case TigrinyaEt: + case TigrinyaEtAbegede: + case UpperAlpha: + case UpperGreek: + case UpperLatin: + case UpperNorwegian: + case UpperRoman: m_text = listMarkerText(type, m_listItem->value()); if (m_text.isEmpty()) width = 0; else { int itemWidth = font.width(m_text); - const UChar periodSpace[2] = { '.', ' ' }; - int periodSpaceWidth = font.width(TextRun(periodSpace, 2)); - width = itemWidth + periodSpaceWidth; + UChar suffixSpace[2] = { listMarkerSuffix(type), ' ' }; + int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2)); + width = itemWidth + suffixSpaceWidth; } break; } @@ -752,9 +1086,9 @@ void RenderListMarker::updateMargins() else marginLeft = cMarkerPadding; } else switch (style()->listStyleType()) { - case DISC: - case CIRCLE: - case SQUARE: + case Disc: + case Circle: + case Square: if (style()->direction() == LTR) { marginLeft = -1; marginRight = font.ascent() - minPrefWidth() + 1; @@ -773,12 +1107,12 @@ void RenderListMarker::updateMargins() else { int offset = font.ascent() * 2 / 3; switch (style()->listStyleType()) { - case DISC: - case CIRCLE: - case SQUARE: + case Disc: + case Circle: + case Square: marginLeft = -offset - cMarkerPadding - 1; break; - case LNONE: + case NoneListStyle: break; default: marginLeft = m_text.isEmpty() ? 0 : -minPrefWidth() - offset / 2; @@ -790,12 +1124,12 @@ void RenderListMarker::updateMargins() else { int offset = font.ascent() * 2 / 3; switch (style()->listStyleType()) { - case DISC: - case CIRCLE: - case SQUARE: + case Disc: + case Circle: + case Square: marginLeft = offset + cMarkerPadding + 1 - minPrefWidth(); break; - case LNONE: + case NoneListStyle: break; default: marginLeft = m_text.isEmpty() ? 0 : offset / 2; @@ -835,42 +1169,77 @@ IntRect RenderListMarker::getRelativeMarkerRect() if (isImage()) return IntRect(x(), y(), m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height()); - switch (style()->listStyleType()) { - case DISC: - case CIRCLE: - case SQUARE: { + EListStyleType type = style()->listStyleType(); + switch (type) { + case Disc: + case Circle: + case Square: { // FIXME: Are these particular rounding rules necessary? const Font& font = style()->font(); int ascent = font.ascent(); int bulletWidth = (ascent * 2 / 3 + 1) / 2; return IntRect(x() + 1, y() + 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth); } - case LNONE: + case NoneListStyle: return IntRect(); - case ARMENIAN: - case CJK_IDEOGRAPHIC: - case DECIMAL_LEADING_ZERO: - case GEORGIAN: - case HEBREW: - case HIRAGANA: - case HIRAGANA_IROHA: - case KATAKANA: - case KATAKANA_IROHA: - case LDECIMAL: - case LOWER_ALPHA: - case LOWER_GREEK: - case LOWER_LATIN: - case LOWER_ROMAN: - case UPPER_ALPHA: - case UPPER_LATIN: - case UPPER_ROMAN: + case Afar: + case Amharic: + case AmharicAbegede: + case Armenian: + case CJKIdeographic: + case CjkEarthlyBranch: + case CjkHeavenlyStem: + case DecimalLeadingZero: + case DecimalListStyle: + case Ethiopic: + case EthiopicAbegede: + case EthiopicAbegedeAmEt: + case EthiopicAbegedeGez: + case EthiopicAbegedeTiEr: + case EthiopicAbegedeTiEt: + case EthiopicHalehameAaEr: + case EthiopicHalehameAaEt: + case EthiopicHalehameAmEt: + case EthiopicHalehameGez: + case EthiopicHalehameOmEt: + case EthiopicHalehameSidEt: + case EthiopicHalehameSoEt: + case EthiopicHalehameTiEr: + case EthiopicHalehameTiEt: + case EthiopicHalehameTig: + case Georgian: + case Hangul: + case HangulConsonant: + case Hebrew: + case Hiragana: + case HiraganaIroha: + case Katakana: + case KatakanaIroha: + case LowerAlpha: + case LowerGreek: + case LowerLatin: + case LowerNorwegian: + case LowerRoman: + case Oromo: + case Sidama: + case Somali: + case Tigre: + case TigrinyaEr: + case TigrinyaErAbegede: + case TigrinyaEt: + case TigrinyaEtAbegede: + case UpperAlpha: + case UpperGreek: + case UpperLatin: + case UpperNorwegian: + case UpperRoman: if (m_text.isEmpty()) return IntRect(); const Font& font = style()->font(); int itemWidth = font.width(m_text); - const UChar periodSpace[2] = { '.', ' ' }; - int periodSpaceWidth = font.width(TextRun(periodSpace, 2)); - return IntRect(x(), y() + font.ascent(), itemWidth + periodSpaceWidth, font.height()); + UChar suffixSpace[2] = { listMarkerSuffix(type), ' ' }; + int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2)); + return IntRect(x(), y() + font.ascent(), itemWidth + suffixSpaceWidth, font.height()); } return IntRect(); diff --git a/WebCore/rendering/RenderListMarker.h b/WebCore/rendering/RenderListMarker.h index 5b46278..971877b 100644 --- a/WebCore/rendering/RenderListMarker.h +++ b/WebCore/rendering/RenderListMarker.h @@ -73,6 +73,7 @@ private: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); IntRect getRelativeMarkerRect(); + IntRect localSelectionRect(); String m_text; RefPtr<StyleImage> m_image; diff --git a/WebCore/rendering/RenderMedia.cpp b/WebCore/rendering/RenderMedia.cpp index 2ff50df..8acebfb 100644 --- a/WebCore/rendering/RenderMedia.cpp +++ b/WebCore/rendering/RenderMedia.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -45,32 +45,30 @@ using namespace HTMLNames; static const double cTimeUpdateRepeatDelay = 0.2; static const double cOpacityAnimationRepeatDelay = 0.05; -// FIXME get this from style -static const double cOpacityAnimationDurationFadeIn = 0.1; -static const double cOpacityAnimationDurationFadeOut = 0.3; RenderMedia::RenderMedia(HTMLMediaElement* video) - : RenderReplaced(video) + : RenderImage(video) , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired) , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired) , m_mouseOver(false) , m_opacityAnimationStartTime(0) - , m_opacityAnimationDuration(cOpacityAnimationDurationFadeIn) + , m_opacityAnimationDuration(0) , m_opacityAnimationFrom(0) , m_opacityAnimationTo(1.0f) { } RenderMedia::RenderMedia(HTMLMediaElement* video, const IntSize& intrinsicSize) - : RenderReplaced(video, intrinsicSize) + : RenderImage(video) , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired) , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired) , m_mouseOver(false) , m_opacityAnimationStartTime(0) - , m_opacityAnimationDuration(cOpacityAnimationDurationFadeIn) + , m_opacityAnimationDuration(0) , m_opacityAnimationFrom(0) , m_opacityAnimationTo(1.0f) { + setIntrinsicSize(intrinsicSize); } RenderMedia::~RenderMedia() @@ -89,7 +87,7 @@ void RenderMedia::destroy() m_controlsShadowRoot->detach(); m_controlsShadowRoot = 0; } - RenderReplaced::destroy(); + RenderImage::destroy(); } HTMLMediaElement* RenderMedia::mediaElement() const @@ -104,7 +102,7 @@ MediaPlayer* RenderMedia::player() const void RenderMedia::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { - RenderReplaced::styleDidChange(diff, oldStyle); + RenderImage::styleDidChange(diff, oldStyle); if (m_controlsShadowRoot) { if (m_panel) @@ -146,7 +144,7 @@ void RenderMedia::layout() { IntSize oldSize = contentBoxRect().size(); - RenderReplaced::layout(); + RenderImage::layout(); RenderBox* controlsRenderer = m_controlsShadowRoot ? m_controlsShadowRoot->renderBox() : 0; if (!controlsRenderer) @@ -452,9 +450,9 @@ void RenderMedia::updateControlVisibility() } if (animateFrom < animateTo) - m_opacityAnimationDuration = cOpacityAnimationDurationFadeIn; + m_opacityAnimationDuration = m_panel->renderer()->theme()->mediaControlsFadeInDuration(); else - m_opacityAnimationDuration = cOpacityAnimationDurationFadeOut; + m_opacityAnimationDuration = m_panel->renderer()->theme()->mediaControlsFadeOutDuration(); m_opacityAnimationFrom = animateFrom; m_opacityAnimationTo = animateTo; @@ -573,7 +571,7 @@ void RenderMedia::forwardEvent(Event* event) int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) const { - int bottom = RenderReplaced::lowestPosition(includeOverflowInterior, includeSelf); + int bottom = RenderImage::lowestPosition(includeOverflowInterior, includeSelf); if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return bottom; @@ -582,7 +580,7 @@ int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const { - int right = RenderReplaced::rightmostPosition(includeOverflowInterior, includeSelf); + int right = RenderImage::rightmostPosition(includeOverflowInterior, includeSelf); if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return right; @@ -591,7 +589,7 @@ int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSel int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const { - int left = RenderReplaced::leftmostPosition(includeOverflowInterior, includeSelf); + int left = RenderImage::leftmostPosition(includeOverflowInterior, includeSelf); if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return left; diff --git a/WebCore/rendering/RenderMedia.h b/WebCore/rendering/RenderMedia.h index 066b83d..0d24c4c 100644 --- a/WebCore/rendering/RenderMedia.h +++ b/WebCore/rendering/RenderMedia.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,7 +28,7 @@ #if ENABLE(VIDEO) -#include "RenderReplaced.h" +#include "RenderImage.h" #include "Timer.h" namespace WebCore { @@ -51,12 +51,12 @@ class MediaControlVolumeSliderContainerElement; class MediaControlElement; class MediaPlayer; -class RenderMedia : public RenderReplaced { +class RenderMedia : public RenderImage { public: RenderMedia(HTMLMediaElement*); RenderMedia(HTMLMediaElement*, const IntSize& intrinsicSize); virtual ~RenderMedia(); - + const RenderObjectChildList* children() const { return &m_children; } RenderObjectChildList* children() { return &m_children; } @@ -83,6 +83,7 @@ private: virtual const char* renderName() const { return "RenderMedia"; } virtual bool isMedia() const { return true; } + virtual bool isImage() const { return false; } virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; diff --git a/WebCore/rendering/RenderMenuList.cpp b/WebCore/rendering/RenderMenuList.cpp index cbbc7cb..05a9873 100644 --- a/WebCore/rendering/RenderMenuList.cpp +++ b/WebCore/rendering/RenderMenuList.cpp @@ -1,7 +1,7 @@ /* * This file is part of the select element renderer in WebCore. * - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * This library is free software; you can redistribute it and/or @@ -24,6 +24,7 @@ #include "config.h" #include "RenderMenuList.h" +#include "AXObjectCache.h" #include "CSSStyleSelector.h" #include "Frame.h" #include "FrameView.h" @@ -50,6 +51,7 @@ RenderMenuList::RenderMenuList(Element* element) , m_innerBlock(0) , m_optionsChanged(true) , m_optionsWidth(0) + , m_lastSelectedIndex(-1) , m_popup(0) , m_popupIsVisible(false) { @@ -306,10 +308,25 @@ void RenderMenuList::valueChanged(unsigned listIndex, bool fireOnChange) select->setSelectedIndexByUser(select->listToOptionIndex(listIndex), true, fireOnChange); } +void RenderMenuList::didSetSelectedIndex() +{ + int index = selectedIndex(); + if (m_lastSelectedIndex == index) + return; + + m_lastSelectedIndex = index; + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->postNotification(this, AXObjectCache::AXMenuListValueChanged, true, PostSynchronously); +} + String RenderMenuList::itemText(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return String(); + Element* element = listItems[listIndex]; if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) return optionGroupElement->groupLabelText(); else if (OptionElement* optionElement = toOptionElement(element)) @@ -320,14 +337,20 @@ String RenderMenuList::itemText(unsigned listIndex) const String RenderMenuList::itemToolTip(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return String(); + Element* element = listItems[listIndex]; return element->title(); } bool RenderMenuList::itemIsEnabled(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return false; + Element* element = listItems[listIndex]; if (!isOptionElement(element)) return false; @@ -345,7 +368,18 @@ bool RenderMenuList::itemIsEnabled(unsigned listIndex) const PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) { + // If we are making an out of bounds access, then we want to use the style + // of a different option element (index 0). However, if there isn't an option element + // before at index 0, we fall back to the menu's style. + if (!listIndex) + return menuStyle(); + + // Try to retrieve the style of an option element we know exists (index 0). + listIndex = 0; + } + Element* element = listItems[listIndex]; RenderStyle* style = element->renderStyle() ? element->renderStyle() : element->computedStyle(); return style ? PopupMenuStyle(style->color(), itemBackgroundColor(listIndex), style->font(), style->visibility() == VISIBLE, style->textIndent(), style->direction()) : menuStyle(); @@ -354,7 +388,10 @@ PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const Color RenderMenuList::itemBackgroundColor(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return style()->backgroundColor(); + Element* element = listItems[listIndex]; Color backgroundColor; if (element->renderStyle()) @@ -374,7 +411,6 @@ Color RenderMenuList::itemBackgroundColor(unsigned listIndex) const PopupMenuStyle RenderMenuList::menuStyle() const { - RenderStyle* s = m_innerBlock ? m_innerBlock->style() : style(); return PopupMenuStyle(s->color(), s->backgroundColor(), s->font(), s->visibility() == VISIBLE, s->textIndent(), s->direction()); } @@ -410,8 +446,19 @@ int RenderMenuList::clientPaddingLeft() const return paddingLeft(); } +const int endOfLinePadding = 2; int RenderMenuList::clientPaddingRight() const { + if (style()->appearance() == MenulistPart || style()->appearance() == MenulistButtonPart) { + // For these appearance values, the theme applies padding to leave room for the + // drop-down button. But leaving room for the button inside the popup menu itself + // looks strange, so we return a small default padding to avoid having a large empty + // space appear on the side of the popup menu. + return endOfLinePadding; + } + + // If the appearance isn't MenulistPart, then the select is styled (non-native), so + // we want to return the user specified padding. return paddingRight(); } @@ -435,21 +482,30 @@ void RenderMenuList::popupDidHide() bool RenderMenuList::itemIsSeparator(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return false; + Element* element = listItems[listIndex]; return element->hasTagName(hrTag); } bool RenderMenuList::itemIsLabel(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return false; + Element* element = listItems[listIndex]; return isOptionGroupElement(element); } bool RenderMenuList::itemIsSelected(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return false; + Element* element = listItems[listIndex]; if (OptionElement* optionElement = toOptionElement(element)) return optionElement->selected(); return false; diff --git a/WebCore/rendering/RenderMenuList.h b/WebCore/rendering/RenderMenuList.h index 2d617c1..a5aa041 100644 --- a/WebCore/rendering/RenderMenuList.h +++ b/WebCore/rendering/RenderMenuList.h @@ -1,7 +1,7 @@ /* * This file is part of the select element renderer in WebCore. * - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -49,6 +49,8 @@ public: void setOptionsChanged(bool changed) { m_optionsChanged = changed; } + void didSetSelectedIndex(); + String text() const; private: @@ -110,6 +112,8 @@ private: bool m_optionsChanged; int m_optionsWidth; + int m_lastSelectedIndex; + RefPtr<PopupMenu> m_popup; bool m_popupIsVisible; }; diff --git a/WebCore/rendering/RenderObject.cpp b/WebCore/rendering/RenderObject.cpp index 712519b..0368490 100644 --- a/WebCore/rendering/RenderObject.cpp +++ b/WebCore/rendering/RenderObject.cpp @@ -28,6 +28,7 @@ #include "RenderObject.h" #include "AXObjectCache.h" +#include "Chrome.h" #include "CSSStyleSelector.h" #include "FloatQuad.h" #include "Frame.h" @@ -67,6 +68,10 @@ #include "WMLNames.h" #endif +#if ENABLE(SVG) +#include "SVGRenderSupport.h" +#endif + using namespace std; namespace WebCore { @@ -307,7 +312,7 @@ void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) // Just add it... children->insertChildNode(this, newChild, beforeChild); } - + RenderCounter::rendererSubtreeAttached(newChild); if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalText(); if (textToTransform) @@ -366,19 +371,14 @@ RenderObject* RenderObject::nextInPreOrderAfterChildren(RenderObject* stayWithin if (this == stayWithin) return 0; - RenderObject* o; - if (!(o = nextSibling())) { - o = parent(); - while (o && !o->nextSibling()) { - if (o == stayWithin) - return 0; - o = o->parent(); - } - if (o) - o = o->nextSibling(); + const RenderObject* current = this; + RenderObject* next; + while (!(next = current->nextSibling())) { + current = current->parent(); + if (!current || current == stayWithin) + return 0; } - - return o; + return next; } RenderObject* RenderObject::previousInPreOrder() const @@ -1011,13 +1011,12 @@ void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty if (style->outlineStyleIsAuto() || hasOutlineAnnotation()) { if (!theme()->supportsFocusRing(style)) { // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. - graphicsContext->initFocusRing(ow, offset); - addFocusRingRects(graphicsContext, tx, ty); + Vector<IntRect> focusRingRects; + addFocusRingRects(focusRingRects, tx, ty); if (style->outlineStyleIsAuto()) - graphicsContext->drawFocusRing(oc); + graphicsContext->drawFocusRing(focusRingRects, ow, offset, oc); else - addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); - graphicsContext->clearFocusRing(); + addPDFURLRect(graphicsContext, unionRect(focusRingRects)); } } @@ -1075,6 +1074,23 @@ IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) return result; } +void RenderObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads) +{ + Vector<IntRect> rects; + // FIXME: addFocusRingRects() needs to be passed this transform-unaware + // localToAbsolute() offset here because RenderInline::addFocusRingRects() + // implicitly assumes that. This doesn't work correctly with transformed + // descendants. + FloatPoint absolutePoint = localToAbsolute(); + addFocusRingRects(rects, absolutePoint.x(), absolutePoint.y()); + size_t count = rects.size(); + for (size_t i = 0; i < count; ++i) { + IntRect rect = rects[i]; + rect.move(-absolutePoint.x(), -absolutePoint.y()); + quads.append(localToAbsoluteQuad(FloatQuad(rect))); + } +} + void RenderObject::addAbsoluteRectForLayer(IntRect& result) { if (hasLayer()) @@ -1637,8 +1653,20 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS if (view()->frameView()) { // FIXME: A better solution would be to only invalidate the fixed regions when scrolling. It's overkill to // prevent the entire view from blitting on a scroll. - bool newStyleSlowScroll = newStyle && (newStyle->position() == FixedPosition || newStyle->hasFixedBackgroundImage()); - bool oldStyleSlowScroll = m_style && (m_style->position() == FixedPosition || m_style->hasFixedBackgroundImage()); + + bool shouldBlitOnFixedBackgroundImage = false; +#if ENABLE(FAST_MOBILE_SCROLLING) + // On low-powered/mobile devices, preventing blitting on a scroll can cause noticeable delays + // when scrolling a page with a fixed background image. As an optimization, assuming there are + // no fixed positoned elements on the page, we can acclerate scrolling (via blitting) if we + // ignore the CSS property "background-attachment: fixed". + shouldBlitOnFixedBackgroundImage = true; +#endif + + bool newStyleSlowScroll = newStyle && (newStyle->position() == FixedPosition + || (!shouldBlitOnFixedBackgroundImage && newStyle->hasFixedBackgroundImage())); + bool oldStyleSlowScroll = m_style && (m_style->position() == FixedPosition + || (!shouldBlitOnFixedBackgroundImage && m_style->hasFixedBackgroundImage())); if (oldStyleSlowScroll != newStyleSlowScroll) { if (oldStyleSlowScroll) view()->frameView()->removeSlowRepaintObject(); @@ -1648,7 +1676,7 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS } } -void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle*) +void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { if (s_affectsParentBlock) handleDynamicFloatPositionChange(); @@ -1656,9 +1684,10 @@ void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle*) if (!m_parent) return; - if (diff == StyleDifferenceLayout) + if (diff == StyleDifferenceLayout) { + RenderCounter::rendererStyleChanged(this, oldStyle, m_style.get()); setNeedsLayoutAndPrefWidthsRecalc(); - else if (diff == StyleDifferenceLayoutPositionedMovementOnly) + } else if (diff == StyleDifferenceLayoutPositionedMovementOnly) setNeedsPositionedMovementLayout(); // Don't check for repaint here; we need to wait until the layer has been @@ -1912,7 +1941,7 @@ void RenderObject::destroy() // If this renderer is being autoscrolled, stop the autoscroll timer - // FIXME: RenderObject::destroy should not get called with a renderar whose document + // FIXME: RenderObject::destroy should not get called with a renderer whose document // has a null frame, so we assert this. However, we don't want release builds to crash which is why we // check that the frame is not null. ASSERT(document()->frame()); @@ -2474,6 +2503,11 @@ VisiblePosition RenderObject::createVisiblePosition(const Position& position) } #if ENABLE(SVG) +const SVGRenderBase* RenderObject::toSVGRenderBase() const +{ + ASSERT_NOT_REACHED(); + return 0; +} FloatRect RenderObject::objectBoundingBox() const { @@ -2491,14 +2525,14 @@ FloatRect RenderObject::repaintRectInLocalCoordinates() const TransformationMatrix RenderObject::localTransform() const { - return TransformationMatrix(); + static const TransformationMatrix identity; + return identity; } -TransformationMatrix RenderObject::localToParentTransform() const +const TransformationMatrix& RenderObject::localToParentTransform() const { - // FIXME: This double virtual call indirection is temporary until I can land the - // rest of the of the localToParentTransform() support for SVG. - return localTransform(); + static const TransformationMatrix identity; + return identity; } TransformationMatrix RenderObject::absoluteTransform() const diff --git a/WebCore/rendering/RenderObject.h b/WebCore/rendering/RenderObject.h index d40ae6d..03ba1e6 100644 --- a/WebCore/rendering/RenderObject.h +++ b/WebCore/rendering/RenderObject.h @@ -52,6 +52,9 @@ class RenderLayer; class RenderTheme; class TransformState; class VisiblePosition; +#if ENABLE(SVG) +class SVGRenderBase; +#endif /* * The painting of a layer occurs in three distinct phases. Each phase involves @@ -260,6 +263,7 @@ public: virtual bool isBlockFlow() const { return false; } virtual bool isBoxModelObject() const { return false; } virtual bool isCounter() const { return false; } + virtual bool isEmbeddedObject() const { return false; } virtual bool isFieldset() const { return false; } virtual bool isFileUploadControl() const { return false; } virtual bool isFrame() const { return false; } @@ -277,6 +281,7 @@ public: virtual bool isRenderInline() const { return false; } virtual bool isRenderPart() const { return false; } virtual bool isRenderView() const { return false; } + virtual bool isReplica() const { return false; } virtual bool isRuby() const { return false; } virtual bool isRubyBase() const { return false; } virtual bool isRubyRun() const { return false; } @@ -310,6 +315,10 @@ public: bool cellWidthChanged() const { return m_cellWidthChanged; } void setCellWidthChanged(bool b = true) { m_cellWidthChanged = b; } +#if ENABLE(MATHML) + virtual bool isRenderMathMLBlock() const { return false; } +#endif // ENABLE(MATHML) + #if ENABLE(SVG) // FIXME: Until all SVG renders can be subclasses of RenderSVGModelObject we have // to add SVG renderer methods to RenderObject with an ASSERT_NOT_REACHED() default implementation. @@ -321,6 +330,8 @@ public: virtual bool isSVGImage() const { return false; } virtual bool isSVGForeignObject() const { return false; } + virtual const SVGRenderBase* toSVGRenderBase() const; + // Per SVG 1.1 objectBoundingBox ignores clipping, masking, filter effects, opacity and stroke-width. // This is used for all computation of objectBoundingBox relative units and by SVGLocateable::getBBox(). // NOTE: Markers are not specifically ignored here by SVG 1.1 spec, but we ignore them @@ -340,7 +351,7 @@ public: // Returns the full transform mapping from local coordinates to local coords for the parent SVG renderer // This includes any viewport transforms and x/y offsets as well as the transform="" value off the element. - virtual TransformationMatrix localToParentTransform() const; + virtual const TransformationMatrix& localToParentTransform() const; // Walks up the parent chain to create a transform which maps from local to document coords // NOTE: This method is deprecated! It doesn't respect scroll offsets or repaint containers. @@ -565,6 +576,8 @@ public: // Build an array of quads in absolute coords for line boxes virtual void absoluteQuads(Vector<FloatQuad>&) { } + void absoluteFocusRingQuads(Vector<FloatQuad>&); + // the rect that will be painted if this object is passed as the paintingRoot IntRect paintingRootRect(IntRect& topLevelRect); @@ -750,7 +763,7 @@ public: bool shouldUseTransformFromContainer(const RenderObject* container) const; void getTransformFromContainer(const RenderObject* container, const IntSize& offsetInContainer, TransformationMatrix&) const; - virtual void addFocusRingRects(GraphicsContext*, int /*tx*/, int /*ty*/) { }; + virtual void addFocusRingRects(Vector<IntRect>&, int /*tx*/, int /*ty*/) { }; IntRect absoluteOutlineBounds() const { diff --git a/WebCore/rendering/RenderPart.cpp b/WebCore/rendering/RenderPart.cpp index cb56c0c..5c4a6ec 100644 --- a/WebCore/rendering/RenderPart.cpp +++ b/WebCore/rendering/RenderPart.cpp @@ -31,6 +31,7 @@ namespace WebCore { RenderPart::RenderPart(Element* node) : RenderWidget(node) + , m_hasFallbackContent(false) { // init RenderObject attributes setInline(false); diff --git a/WebCore/rendering/RenderPart.h b/WebCore/rendering/RenderPart.h index 08abf99..8303543 100644 --- a/WebCore/rendering/RenderPart.h +++ b/WebCore/rendering/RenderPart.h @@ -27,6 +27,10 @@ namespace WebCore { +// Renderer for frames via RenderPartObject, and plug-ins via RenderEmbeddedObject. + +// FIXME: This class is subclassed in RenderPartObject for iframes, which is in turn +// subclassed in RenderEmbeddedObject for object and embed. This class itself could be removed. class RenderPart : public RenderWidget { public: RenderPart(Element*); diff --git a/WebCore/rendering/RenderPartObject.cpp b/WebCore/rendering/RenderPartObject.cpp index 8e9118e..09573c8 100644 --- a/WebCore/rendering/RenderPartObject.cpp +++ b/WebCore/rendering/RenderPartObject.cpp @@ -48,271 +48,6 @@ using namespace HTMLNames; RenderPartObject::RenderPartObject(Element* element) : RenderPart(element) { - // init RenderObject attributes - setInline(true); - m_hasFallbackContent = false; - - if (element->hasTagName(embedTag) || element->hasTagName(objectTag)) - view()->frameView()->setIsVisuallyNonEmpty(); -} - -RenderPartObject::~RenderPartObject() -{ - if (frameView()) - frameView()->removeWidgetToUpdate(this); -} - -static bool isURLAllowed(Document* doc, const String& url) -{ - if (doc->frame()->page()->frameCount() >= 200) - return false; - - // We allow one level of self-reference because some sites depend on that. - // But we don't allow more than one. - KURL completeURL = doc->completeURL(url); - bool foundSelfReference = false; - for (Frame* frame = doc->frame(); frame; frame = frame->tree()->parent()) { - if (equalIgnoringFragmentIdentifier(frame->loader()->url(), completeURL)) { - if (foundSelfReference) - return false; - foundSelfReference = true; - } - } - return true; -} - -typedef HashMap<String, String, CaseFoldingHash> ClassIdToTypeMap; - -static ClassIdToTypeMap* createClassIdToTypeMap() -{ - ClassIdToTypeMap* map = new ClassIdToTypeMap; - map->add("clsid:D27CDB6E-AE6D-11CF-96B8-444553540000", "application/x-shockwave-flash"); - map->add("clsid:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA", "audio/x-pn-realaudio-plugin"); - map->add("clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B", "video/quicktime"); - map->add("clsid:166B1BCA-3F9C-11CF-8075-444553540000", "application/x-director"); - map->add("clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6", "application/x-mplayer2"); - map->add("clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95", "application/x-mplayer2"); - return map; -} - -static String serviceTypeForClassId(const String& classId) -{ - // Return early if classId is empty (since we won't do anything below). - // Furthermore, if classId is null, calling get() below will crash. - if (classId.isEmpty()) - return String(); - - static ClassIdToTypeMap* map = createClassIdToTypeMap(); - return map->get(classId); -} - -static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues) -{ - // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP - // require "src" attribute). - int srcIndex = -1, dataIndex = -1; - for (unsigned int i = 0; i < paramNames->size(); ++i) { - if (equalIgnoringCase((*paramNames)[i], "src")) - srcIndex = i; - else if (equalIgnoringCase((*paramNames)[i], "data")) - dataIndex = i; - } - - if (srcIndex == -1 && dataIndex != -1) { - paramNames->append("src"); - paramValues->append((*paramValues)[dataIndex]); - } -} - -void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins) -{ - String url; - String serviceType; - Vector<String> paramNames; - Vector<String> paramValues; - Frame* frame = frameView()->frame(); - - // The calls to FrameLoader::requestObject within this function can result in a plug-in being initialized. - // This can run cause arbitrary JavaScript to run and may result in this RenderObject being detached from - // the render tree and destroyed, causing a crash like <rdar://problem/6954546>. By extending our lifetime - // artifically to ensure that we remain alive for the duration of plug-in initialization. - RenderWidgetProtector protector(this); - - if (node()->hasTagName(objectTag)) { - HTMLObjectElement* o = static_cast<HTMLObjectElement*>(node()); - - o->setNeedWidgetUpdate(false); - if (!o->isFinishedParsingChildren()) - return; - - // Check for a child EMBED tag. - HTMLEmbedElement* embed = 0; - for (Node* child = o->firstChild(); child; ) { - if (child->hasTagName(embedTag)) { - embed = static_cast<HTMLEmbedElement*>(child); - break; - } else if (child->hasTagName(objectTag)) - child = child->nextSibling(); // Don't descend into nested OBJECT tags - else - child = child->traverseNextNode(o); // Otherwise descend (EMBEDs may be inside COMMENT tags) - } - - // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT. - HTMLElement *embedOrObject; - if (embed) { - embedOrObject = (HTMLElement *)embed; - url = embed->url(); - serviceType = embed->serviceType(); - } else - embedOrObject = (HTMLElement *)o; - - // If there was no URL or type defined in EMBED, try the OBJECT tag. - if (url.isEmpty()) - url = o->url(); - if (serviceType.isEmpty()) - serviceType = o->serviceType(); - - HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; - - // Scan the PARAM children. - // Get the URL and type from the params if we don't already have them. - // Get the attributes from the params if there is no EMBED tag. - Node *child = o->firstChild(); - while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) { - if (child->hasTagName(paramTag)) { - HTMLParamElement* p = static_cast<HTMLParamElement*>(child); - String name = p->name(); - if (url.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) - url = p->value(); - if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { - serviceType = p->value(); - int pos = serviceType.find(";"); - if (pos != -1) - serviceType = serviceType.left(pos); - } - if (!embed && !name.isEmpty()) { - uniqueParamNames.add(name.impl()); - paramNames.append(p->name()); - paramValues.append(p->value()); - } - } - child = child->nextSibling(); - } - - // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag - // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is - // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means - // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, - // else our Java plugin will misinterpret it. [4004531] - String codebase; - if (!embed && MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { - codebase = "codebase"; - uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already - } - - // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values. - NamedNodeMap* attributes = embedOrObject->attributes(); - if (attributes) { - for (unsigned i = 0; i < attributes->length(); ++i) { - Attribute* it = attributes->attributeItem(i); - const AtomicString& name = it->name().localName(); - if (embed || !uniqueParamNames.contains(name.impl())) { - paramNames.append(name.string()); - paramValues.append(it->value().string()); - } - } - } - - mapDataParamToSrc(¶mNames, ¶mValues); - - // If we still don't have a type, try to map from a specific CLASSID to a type. - if (serviceType.isEmpty()) - serviceType = serviceTypeForClassId(o->classId()); - - if (!isURLAllowed(document(), url)) - return; - - // Find out if we support fallback content. - m_hasFallbackContent = false; - for (Node *child = o->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) { - if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) || // Discount <embed> and <param> - (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace())) - m_hasFallbackContent = true; - } - - if (onlyCreateNonNetscapePlugins) { - KURL completedURL; - if (!url.isEmpty()) - completedURL = frame->loader()->completeURL(url); - - if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) - return; - } - - bool success = o->dispatchBeforeLoadEvent(url) && - frame->loader()->requestObject(this, url, o->getAttribute(nameAttr), serviceType, paramNames, paramValues); - if (!success && m_hasFallbackContent) - o->renderFallbackContent(); - } else if (node()->hasTagName(embedTag)) { - HTMLEmbedElement *o = static_cast<HTMLEmbedElement*>(node()); - o->setNeedWidgetUpdate(false); - url = o->url(); - serviceType = o->serviceType(); - - if (url.isEmpty() && serviceType.isEmpty()) - return; - if (!isURLAllowed(document(), url)) - return; - - // add all attributes set on the embed object - NamedNodeMap* a = o->attributes(); - if (a) { - for (unsigned i = 0; i < a->length(); ++i) { - Attribute* it = a->attributeItem(i); - paramNames.append(it->name().localName().string()); - paramValues.append(it->value().string()); - } - } - - if (onlyCreateNonNetscapePlugins) { - KURL completedURL; - if (!url.isEmpty()) - completedURL = frame->loader()->completeURL(url); - - if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) - return; - - } - - if (o->dispatchBeforeLoadEvent(url)) - frame->loader()->requestObject(this, url, o->getAttribute(nameAttr), serviceType, paramNames, paramValues); - } -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - else if (node()->hasTagName(videoTag) || node()->hasTagName(audioTag)) { - HTMLMediaElement* o = static_cast<HTMLMediaElement*>(node()); - - o->setNeedWidgetUpdate(false); - if (node()->hasTagName(videoTag)) { - HTMLVideoElement* vid = static_cast<HTMLVideoElement*>(node()); - String poster = vid->poster(); - if (!poster.isEmpty()) { - paramNames.append("_media_element_poster_"); - paramValues.append(poster); - } - } - - url = o->initialURL(); - if (!url.isEmpty()) { - paramNames.append("_media_element_src_"); - paramValues.append(url); - } - - serviceType = "application/x-media-element-proxy-plugin"; - - if (o->dispatchBeforeLoadEvent(url)) - frame->loader()->requestObject(this, url, nullAtom, serviceType, paramNames, paramValues); - } -#endif } void RenderPartObject::layout() @@ -401,9 +136,6 @@ void RenderPartObject::layout() m_overflow.clear(); addShadowOverflow(); - if (!widget() && frameView()) - frameView()->addWidgetToUpdate(this); - setNeedsLayout(false); } diff --git a/WebCore/rendering/RenderPartObject.h b/WebCore/rendering/RenderPartObject.h index 092395d..7160ea3 100644 --- a/WebCore/rendering/RenderPartObject.h +++ b/WebCore/rendering/RenderPartObject.h @@ -27,12 +27,10 @@ namespace WebCore { +// Renderer for iframes. Is subclassed in RenderEmbeddedObject for object and embed. class RenderPartObject : public RenderPart { public: RenderPartObject(Element*); - virtual ~RenderPartObject(); - - void updateWidget(bool onlyCreateNonNetscapePlugins); private: virtual const char* renderName() const { return "RenderPartObject"; } diff --git a/WebCore/rendering/RenderPath.cpp b/WebCore/rendering/RenderPath.cpp index 4a7662f..f497dcf 100644 --- a/WebCore/rendering/RenderPath.cpp +++ b/WebCore/rendering/RenderPath.cpp @@ -3,6 +3,7 @@ 2004, 2005, 2008 Rob Buis <buis@kde.org> 2005, 2007 Eric Seidel <eric@webkit.org> 2009 Google, Inc. + 2009 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -68,7 +69,7 @@ RenderPath::RenderPath(SVGStyledTransformableElement* node) { } -TransformationMatrix RenderPath::localToParentTransform() const +const TransformationMatrix& RenderPath::localToParentTransform() const { return m_localTransform; } @@ -112,6 +113,34 @@ FloatRect RenderPath::objectBoundingBox() const return m_cachedLocalFillBBox; } +FloatRect RenderPath::strokeBoundingBox() const +{ + if (m_path.isEmpty()) + return FloatRect(); + + if (!m_cachedLocalStrokeBBox.isEmpty()) + return m_cachedLocalStrokeBBox; + + m_cachedLocalStrokeBBox = objectBoundingBox(); + if (style()->svgStyle()->hasStroke()) { + BoundingRectStrokeStyleApplier strokeStyle(this, style()); + m_cachedLocalStrokeBBox.unite(m_path.strokeBoundingRect(&strokeStyle)); + } + + return m_cachedLocalStrokeBBox; +} + +FloatRect RenderPath::markerBoundingBox() const +{ + if (m_path.isEmpty()) + return FloatRect(); + + if (m_cachedLocalMarkerBBox.isEmpty()) + calculateMarkerBoundsIfNeeded(); + + return m_cachedLocalMarkerBBox; +} + FloatRect RenderPath::repaintRectInLocalCoordinates() const { if (m_path.isEmpty()) @@ -121,16 +150,25 @@ FloatRect RenderPath::repaintRectInLocalCoordinates() const if (!m_cachedLocalRepaintRect.isEmpty()) return m_cachedLocalRepaintRect; - if (!style()->svgStyle()->hasStroke()) - m_cachedLocalRepaintRect = objectBoundingBox(); + // FIXME: We need to be careful here. We assume that there is no filter, + // clipper, marker or masker if the rects are empty. + FloatRect rect = filterBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect = rect; else { - BoundingRectStrokeStyleApplier strokeStyle(this, style()); - m_cachedLocalRepaintRect = m_path.strokeBoundingRect(&strokeStyle); + m_cachedLocalRepaintRect = strokeBoundingBox(); + m_cachedLocalRepaintRect.unite(markerBoundingBox()); } - // Markers and filters can paint outside of the stroke path - m_cachedLocalRepaintRect.unite(m_markerBounds); - m_cachedLocalRepaintRect.unite(filterBoundingBoxForRenderer(this)); + rect = clipperBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect.intersect(rect); + + rect = maskerBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect.intersect(rect); + + style()->svgStyle()->inflateForShadow(m_cachedLocalRepaintRect); return m_cachedLocalRepaintRect; } @@ -139,12 +177,9 @@ void RenderPath::setPath(const Path& newPath) { m_path = newPath; m_cachedLocalRepaintRect = FloatRect(); + m_cachedLocalStrokeBBox = FloatRect(); m_cachedLocalFillBBox = FloatRect(); -} - -const Path& RenderPath::path() const -{ - return m_path; + m_cachedLocalMarkerBBox = FloatRect(); } void RenderPath::layout() @@ -180,39 +215,48 @@ void RenderPath::paint(PaintInfo& paintInfo, int, int) { if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty()) return; - - paintInfo.context->save(); - paintInfo.context->concatCTM(localToParentTransform()); - - SVGResourceFilter* filter = 0; FloatRect boundingBox = repaintRectInLocalCoordinates(); - if (paintInfo.phase == PaintPhaseForeground) { - PaintInfo savedInfo(paintInfo); + FloatRect nonLocalBoundingBox = m_localTransform.mapRect(boundingBox); + // FIXME: The empty rect check is to deal with incorrect initial clip in renderSubtreeToImage + // unfortunately fixing that problem is fairly complex unless we were willing to just futz the + // rect to something "close enough" + if (!nonLocalBoundingBox.intersects(paintInfo.rect) && !paintInfo.rect.isEmpty()) + return; + + PaintInfo childPaintInfo(paintInfo); + childPaintInfo.context->save(); + applyTransformToPaintInfo(childPaintInfo, m_localTransform); + SVGResourceFilter* filter = 0; - prepareToRenderSVGContent(this, paintInfo, boundingBox, filter); - if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES) - paintInfo.context->setShouldAntialias(false); - fillAndStrokePath(m_path, paintInfo.context, style(), this); + if (childPaintInfo.phase == PaintPhaseForeground) { + PaintInfo savedInfo(childPaintInfo); - if (static_cast<SVGStyledElement*>(node())->supportsMarkers()) - m_markerBounds = drawMarkersIfNeeded(paintInfo.context, paintInfo.rect, m_path); + if (prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter)) { + if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES) + childPaintInfo.context->setShouldAntialias(false); + fillAndStrokePath(m_path, childPaintInfo.context, style(), this); - finishRenderSVGContent(this, paintInfo, filter, savedInfo.context); + if (static_cast<SVGStyledElement*>(node())->supportsMarkers()) + m_markerLayoutInfo.drawMarkers(childPaintInfo); + } + finishRenderSVGContent(this, childPaintInfo, filter, savedInfo.context); } - if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth()) - paintOutline(paintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()), + if ((childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth()) + paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()), static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()), style()); - paintInfo.context->restore(); + childPaintInfo.context->restore(); } // This method is called from inside paintOutline() since we call paintOutline() // while transformed to our coord system, return local coords -void RenderPath::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +void RenderPath::addFocusRingRects(Vector<IntRect>& rects, int, int) { - graphicsContext->addFocusRingRect(enclosingIntRect(repaintRectInLocalCoordinates())); + IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates()); + if (!rect.isEmpty()) + rects.append(rect); } bool RenderPath::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) @@ -221,7 +265,7 @@ bool RenderPath::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, if (hitTestAction != HitTestForeground) return false; - FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent); + FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent); PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, style()->pointerEvents()); @@ -237,143 +281,27 @@ bool RenderPath::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, return false; } -enum MarkerType { - Start, - Mid, - End -}; - -struct MarkerData { - FloatPoint origin; - FloatPoint subpathStart; - double strokeWidth; - FloatPoint inslopePoints[2]; - FloatPoint outslopePoints[2]; - MarkerType type; - SVGResourceMarker* marker; -}; - -struct DrawMarkersData { - DrawMarkersData(GraphicsContext*, SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, double strokeWidth); - GraphicsContext* context; - int elementIndex; - MarkerData previousMarkerData; - SVGResourceMarker* midMarker; -}; - -DrawMarkersData::DrawMarkersData(GraphicsContext* c, SVGResourceMarker *start, SVGResourceMarker *mid, double strokeWidth) - : context(c) - , elementIndex(0) - , midMarker(mid) -{ - previousMarkerData.origin = FloatPoint(); - previousMarkerData.subpathStart = FloatPoint(); - previousMarkerData.strokeWidth = strokeWidth; - previousMarkerData.marker = start; - previousMarkerData.type = Start; -} - -static void drawMarkerWithData(GraphicsContext* context, MarkerData &data) -{ - if (!data.marker) - return; - - FloatPoint inslopeChange = data.inslopePoints[1] - FloatSize(data.inslopePoints[0].x(), data.inslopePoints[0].y()); - FloatPoint outslopeChange = data.outslopePoints[1] - FloatSize(data.outslopePoints[0].x(), data.outslopePoints[0].y()); - - double inslope = rad2deg(atan2(inslopeChange.y(), inslopeChange.x())); - double outslope = rad2deg(atan2(outslopeChange.y(), outslopeChange.x())); - - double angle = 0.0; - switch (data.type) { - case Start: - angle = outslope; - break; - case Mid: - angle = (inslope + outslope) / 2; - break; - case End: - angle = inslope; - } - - data.marker->draw(context, FloatRect(), data.origin.x(), data.origin.y(), data.strokeWidth, angle); -} - -static inline void updateMarkerDataForElement(MarkerData& previousMarkerData, const PathElement* element) -{ - FloatPoint* points = element->points; - - switch (element->type) { - case PathElementAddQuadCurveToPoint: - // TODO - previousMarkerData.origin = points[1]; - break; - case PathElementAddCurveToPoint: - previousMarkerData.inslopePoints[0] = points[1]; - previousMarkerData.inslopePoints[1] = points[2]; - previousMarkerData.origin = points[2]; - break; - case PathElementMoveToPoint: - previousMarkerData.subpathStart = points[0]; - case PathElementAddLineToPoint: - previousMarkerData.inslopePoints[0] = previousMarkerData.origin; - previousMarkerData.inslopePoints[1] = points[0]; - previousMarkerData.origin = points[0]; - break; - case PathElementCloseSubpath: - previousMarkerData.inslopePoints[0] = previousMarkerData.origin; - previousMarkerData.inslopePoints[1] = points[0]; - previousMarkerData.origin = previousMarkerData.subpathStart; - previousMarkerData.subpathStart = FloatPoint(); - } -} - -static void drawStartAndMidMarkers(void* info, const PathElement* element) -{ - DrawMarkersData& data = *reinterpret_cast<DrawMarkersData*>(info); - - int elementIndex = data.elementIndex; - MarkerData& previousMarkerData = data.previousMarkerData; - - FloatPoint* points = element->points; - - // First update the outslope for the previous element - previousMarkerData.outslopePoints[0] = previousMarkerData.origin; - previousMarkerData.outslopePoints[1] = points[0]; - - // Draw the marker for the previous element - if (elementIndex != 0) - drawMarkerWithData(data.context, previousMarkerData); - - // Update our marker data for this element - updateMarkerDataForElement(previousMarkerData, element); - - if (elementIndex == 1) { - // After drawing the start marker, switch to drawing mid markers - previousMarkerData.marker = data.midMarker; - previousMarkerData.type = Mid; - } - - data.elementIndex++; -} - -FloatRect RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatRect&, const Path& path) const +void RenderPath::calculateMarkerBoundsIfNeeded() const { Document* doc = document(); SVGElement* svgElement = static_cast<SVGElement*>(node()); - ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); + ASSERT(svgElement && svgElement->document()); + if (!svgElement->isStyled()) + return; SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); - const SVGRenderStyle* svgStyle = style()->svgStyle(); + if (!styledElement->supportsMarkers()) + return; + const SVGRenderStyle* svgStyle = style()->svgStyle(); AtomicString startMarkerId(svgStyle->startMarker()); AtomicString midMarkerId(svgStyle->midMarker()); AtomicString endMarkerId(svgStyle->endMarker()); - SVGResourceMarker* startMarker = getMarkerById(doc, startMarkerId); - SVGResourceMarker* midMarker = getMarkerById(doc, midMarkerId); - SVGResourceMarker* endMarker = getMarkerById(doc, endMarkerId); + SVGResourceMarker* startMarker = getMarkerById(doc, startMarkerId, this); + SVGResourceMarker* midMarker = getMarkerById(doc, midMarkerId, this); + SVGResourceMarker* endMarker = getMarkerById(doc, endMarkerId, this); if (!startMarker && !startMarkerId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(startMarkerId, styledElement); @@ -391,32 +319,10 @@ FloatRect RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatR endMarker->addClient(styledElement); if (!startMarker && !midMarker && !endMarker) - return FloatRect(); - - double strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, svgStyle->strokeWidth(), 1.0f); - DrawMarkersData data(context, startMarker, midMarker, strokeWidth); - - path.apply(&data, drawStartAndMidMarkers); - - data.previousMarkerData.marker = endMarker; - data.previousMarkerData.type = End; - drawMarkerWithData(context, data.previousMarkerData); - - // We know the marker boundaries, only after they're drawn! - // Otherwhise we'd need to do all the marker calculation twice - // once here (through paint()) and once in absoluteClippedOverflowRect(). - FloatRect bounds; - - if (startMarker) - bounds.unite(startMarker->cachedBounds()); - - if (midMarker) - bounds.unite(midMarker->cachedBounds()); - - if (endMarker) - bounds.unite(endMarker->cachedBounds()); + return; - return bounds; + float strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, svgStyle->strokeWidth(), 1.0f); + m_cachedLocalMarkerBBox = m_markerLayoutInfo.calculateBoundaries(startMarker, midMarker, endMarker, strokeWidth, m_path); } } diff --git a/WebCore/rendering/RenderPath.h b/WebCore/rendering/RenderPath.h index 2ff179e..be4c2dc 100644 --- a/WebCore/rendering/RenderPath.h +++ b/WebCore/rendering/RenderPath.h @@ -25,9 +25,9 @@ #define RenderPath_h #if ENABLE(SVG) - #include "FloatRect.h" #include "RenderSVGModelObject.h" +#include "SVGMarkerLayoutInfo.h" #include "TransformationMatrix.h" namespace WebCore { @@ -40,7 +40,7 @@ class RenderPath : public RenderSVGModelObject { public: RenderPath(SVGStyledTransformableElement*); - const Path& path() const; + const Path& path() const { return m_path; } private: // Hit-detection seperated for the fill and the stroke @@ -48,9 +48,11 @@ private: bool strokeContains(const FloatPoint&, bool requiresStroke = true) const; virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const; + virtual FloatRect markerBoundingBox() const; virtual FloatRect repaintRectInLocalCoordinates() const; - virtual TransformationMatrix localToParentTransform() const; + virtual const TransformationMatrix& localToParentTransform() const; void setPath(const Path&); @@ -59,19 +61,21 @@ private: virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); - FloatRect drawMarkersIfNeeded(GraphicsContext*, const FloatRect&, const Path&) const; + void calculateMarkerBoundsIfNeeded() const; private: virtual TransformationMatrix localTransform() const; mutable Path m_path; mutable FloatRect m_cachedLocalFillBBox; + mutable FloatRect m_cachedLocalStrokeBBox; mutable FloatRect m_cachedLocalRepaintRect; - FloatRect m_markerBounds; + mutable FloatRect m_cachedLocalMarkerBBox; + mutable SVGMarkerLayoutInfo m_markerLayoutInfo; TransformationMatrix m_localTransform; }; @@ -94,5 +98,3 @@ void toRenderPath(const RenderPath*); #endif // ENABLE(SVG) #endif - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderReplaced.h b/WebCore/rendering/RenderReplaced.h index 0ba6b8a..bcf565d 100644 --- a/WebCore/rendering/RenderReplaced.h +++ b/WebCore/rendering/RenderReplaced.h @@ -46,6 +46,7 @@ protected: void setIntrinsicSize(const IntSize&); virtual void intrinsicSizeChanged(); + virtual void paint(PaintInfo&, int tx, int ty); bool shouldPaint(PaintInfo&, int& tx, int& ty); void adjustOverflowForBoxShadowAndReflect(); IntRect localSelectionRect(bool checkWhetherSelected = true) const; @@ -62,7 +63,6 @@ private: virtual int minimumReplacedHeight() const { return 0; } - virtual void paint(PaintInfo&, int tx, int ty); virtual void paintReplaced(PaintInfo&, int /*tx*/, int /*ty*/) { } virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); diff --git a/WebCore/rendering/RenderReplica.h b/WebCore/rendering/RenderReplica.h index d5db3b7..48c64e4 100644 --- a/WebCore/rendering/RenderReplica.h +++ b/WebCore/rendering/RenderReplica.h @@ -46,6 +46,10 @@ public: virtual void calcPrefWidths(); virtual void paint(PaintInfo&, int tx, int ty); + +private: + virtual bool isReplica() const { return true; } + }; } // namespace WebCore diff --git a/WebCore/rendering/RenderRuby.h b/WebCore/rendering/RenderRuby.h index a74150c..49a84d8 100644 --- a/WebCore/rendering/RenderRuby.h +++ b/WebCore/rendering/RenderRuby.h @@ -54,13 +54,15 @@ public: RenderRubyAsInline(Node*); virtual ~RenderRubyAsInline(); - virtual const char* renderName() const { return "RenderRuby (inline)"; } - - virtual bool isRuby() const { return true; } - virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); virtual void removeChild(RenderObject* child); + +private: + virtual bool isRuby() const { return true; } + virtual const char* renderName() const { return "RenderRuby (inline)"; } + virtual bool createsAnonymousWrapper() const { return true; } + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { ASSERT_NOT_REACHED(); } }; // <ruby> when used as 'display:block' or 'display:inline-block' @@ -69,13 +71,15 @@ public: RenderRubyAsBlock(Node*); virtual ~RenderRubyAsBlock(); - virtual const char* renderName() const { return "RenderRuby (block)"; } - - virtual bool isRuby() const { return true; } - virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); virtual void removeChild(RenderObject* child); + +private: + virtual bool isRuby() const { return true; } + virtual const char* renderName() const { return "RenderRuby (block)"; } + virtual bool createsAnonymousWrapper() const { return true; } + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { ASSERT_NOT_REACHED(); } }; } // namespace WebCore diff --git a/WebCore/rendering/RenderRubyBase.cpp b/WebCore/rendering/RenderRubyBase.cpp index 5cb25f4..41ac4e3 100644 --- a/WebCore/rendering/RenderRubyBase.cpp +++ b/WebCore/rendering/RenderRubyBase.cpp @@ -48,39 +48,139 @@ bool RenderRubyBase::isChildAllowed(RenderObject* child, RenderStyle*) const return child->isInline(); } -void RenderRubyBase::splitToLeft(RenderBlock* leftBase, RenderObject* beforeChild) +bool RenderRubyBase::hasOnlyWrappedInlineChildren(RenderObject* beforeChild) const { - // This function removes all children that are before (!) beforeChild - // and appends them to leftBase. - ASSERT(leftBase); + // Tests whether all children in the base before beforeChild are either floated/positioned, + // or inline objects wrapped in anonymous blocks. + // Note that beforeChild may be 0, in which case all children are looked at. + for (RenderObject* child = firstChild(); child != beforeChild; child = child->nextSibling()) { + if (!child->isFloatingOrPositioned() && !(child->isAnonymousBlock() && child->childrenInline())) + return false; + } + return true; +} +void RenderRubyBase::moveChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) +{ + // This function removes all children that are before (!) beforeChild + // and appends them to toBase. + ASSERT(toBase); + // First make sure that beforeChild (if set) is indeed a direct child of this. - // Fallback: climb up the tree to make sure. This may result in somewhat incorrect rendering. - // FIXME: Can this happen? Is there a better/more correct way to solve this? - ASSERT(!beforeChild || beforeChild->parent() == this); - while (beforeChild && beforeChild->parent() != this) - beforeChild = beforeChild->parent(); - - RenderObject* child = firstChild(); - while (child != beforeChild) { - RenderObject* nextChild = child->nextSibling(); - moveChildTo(leftBase, leftBase->children(), child); - child = nextChild; + // Inline children might be wrapped in an anonymous block if there's a continuation. + // Theoretically, in ruby bases, this can happen with only the first such a child, + // so it should be OK to just climb the tree. + while (fromBeforeChild && fromBeforeChild->parent() != this) + fromBeforeChild = fromBeforeChild->parent(); + + if (childrenInline()) + moveInlineChildren(toBase, fromBeforeChild); + else + moveBlockChildren(toBase, fromBeforeChild); + + setNeedsLayoutAndPrefWidthsRecalc(); + toBase->setNeedsLayoutAndPrefWidthsRecalc(); +} + +void RenderRubyBase::moveInlineChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) +{ + RenderBlock* toBlock; + + if (toBase->childrenInline()) { + // The standard and easy case: move the children into the target base + toBlock = toBase; + } else { + // We need to wrap the inline objects into an anonymous block. + // If toBase has a suitable block, we re-use it, otherwise create a new one. + RenderObject* lastChild = toBase->lastChild(); + if (lastChild && lastChild->isAnonymousBlock() && lastChild->childrenInline()) + toBlock = toRenderBlock(lastChild); + else { + toBlock = toBase->createAnonymousBlock(); + toBase->children()->appendChildNode(toBase, toBlock); + } } + // Move our inline children into the target block we determined above. + for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild()) + moveChildTo(toBlock, toBlock->children(), child); } -void RenderRubyBase::mergeWithRight(RenderBlock* rightBase) +void RenderRubyBase::moveBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) { - // This function removes all children and prepends (!) them to rightBase. - ASSERT(rightBase); - - RenderObject* firstPos = rightBase->firstChild(); - RenderObject* child = lastChild(); - while (child) { - moveChildTo(rightBase, rightBase->children(), firstPos, child); - firstPos = child; - child = lastChild(); + if (toBase->childrenInline()) { + // First check whether we move only wrapped inline objects. + if (hasOnlyWrappedInlineChildren(fromBeforeChild)) { + // The reason why the base is in block flow must be after beforeChild. + // We therefore can extract the inline objects and move them to toBase. + for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild()) { + if (child->isAnonymousBlock()) { + RenderBlock* anonBlock = toRenderBlock(child); + ASSERT(anonBlock->childrenInline()); + ASSERT(!anonBlock->inlineContinuation()); + anonBlock->moveAllChildrenTo(toBase, toBase->children()); + anonBlock->deleteLineBoxTree(); + anonBlock->destroy(); + } else { + ASSERT(child->isFloatingOrPositioned()); + moveChildTo(toBase, toBase->children(), child); + } + } + } else { + // Moving block children -> have to set toBase as block flow + toBase->makeChildrenNonInline(); + // Move children, potentially collapsing anonymous block wrappers. + mergeBlockChildren(toBase, fromBeforeChild); + + // Now we need to check if the leftover children are all inline. + // If so, make this base inline again. + if (hasOnlyWrappedInlineChildren()) { + RenderObject* next = 0; + for (RenderObject* child = firstChild(); child; child = next) { + next = child->nextSibling(); + if (child->isFloatingOrPositioned()) + continue; + ASSERT(child->isAnonymousBlock()); + + RenderBlock* anonBlock = toRenderBlock(child); + ASSERT(anonBlock->childrenInline()); + ASSERT(!anonBlock->inlineContinuation()); + // Move inline children out of anonymous block. + anonBlock->moveAllChildrenTo(this, children(), anonBlock); + anonBlock->deleteLineBoxTree(); + anonBlock->destroy(); + } + setChildrenInline(true); + } + } + } else + mergeBlockChildren(toBase, fromBeforeChild); +} + +void RenderRubyBase::mergeBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) +{ + // This function removes all children that are before fromBeforeChild and appends them to toBase. + ASSERT(!childrenInline()); + ASSERT(toBase); + ASSERT(!toBase->childrenInline()); + + // Quick check whether we have anything to do, to simplify the following code. + if (fromBeforeChild != firstChild()) + return; + + // If an anonymous block would be put next to another such block, then merge those. + RenderObject* firstChildHere = firstChild(); + RenderObject* lastChildThere = toBase->lastChild(); + if (firstChildHere && firstChildHere->isAnonymousBlock() && firstChildHere->childrenInline() + && lastChildThere && lastChildThere->isAnonymousBlock() && lastChildThere->childrenInline()) { + RenderBlock* anonBlockHere = toRenderBlock(firstChildHere); + RenderBlock* anonBlockThere = toRenderBlock(lastChildThere); + anonBlockHere->moveAllChildrenTo(anonBlockThere, anonBlockThere->children()); + anonBlockHere->deleteLineBoxTree(); + anonBlockHere->destroy(); } + // Move all remaining children normally. + for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild()) + moveChildTo(toBase, toBase->children(), child); } } // namespace WebCore diff --git a/WebCore/rendering/RenderRubyBase.h b/WebCore/rendering/RenderRubyBase.h index 57baf99..c029bd5 100644 --- a/WebCore/rendering/RenderRubyBase.h +++ b/WebCore/rendering/RenderRubyBase.h @@ -46,8 +46,16 @@ public: virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; - void splitToLeft(RenderBlock* leftBase, RenderObject* beforeChild); - void mergeWithRight(RenderBlock* rightBase); +private: + bool hasOnlyWrappedInlineChildren(RenderObject* beforeChild = 0) const; + + void moveChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0); + void moveInlineChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0); + void moveBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0); + void mergeBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0); + + // Allow RenderRubyRun to manipulate the children within ruby bases. + friend class RenderRubyRun; }; } // namespace WebCore diff --git a/WebCore/rendering/RenderRubyRun.cpp b/WebCore/rendering/RenderRubyRun.cpp index 8578c55..a3e6281 100644 --- a/WebCore/rendering/RenderRubyRun.cpp +++ b/WebCore/rendering/RenderRubyRun.cpp @@ -148,7 +148,7 @@ void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild) RenderRubyRun* newRun = staticCreateRubyRun(ruby); ruby->addChild(newRun, this); newRun->addChild(child); - rubyBaseSafe()->splitToLeft(newRun->rubyBaseSafe(), beforeChild); + rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild); } } else { // child is not a text -> insert it into the base @@ -170,7 +170,11 @@ void RenderRubyRun::removeChild(RenderObject* child) // Ruby run without a base can happen only at the first run. RenderRubyRun* rightRun = static_cast<RenderRubyRun*>(rightNeighbour); ASSERT(rightRun->hasRubyBase()); - base->mergeWithRight(rightRun->rubyBaseSafe()); + RenderRubyBase* rightBase = rightRun->rubyBaseSafe(); + // Collect all children in a single base, then swap the bases. + rightBase->moveChildren(base); + moveChildTo(rightRun, rightRun->children(), base); + rightRun->moveChildTo(this, children(), rightBase); // The now empty ruby base will be removed below. } } diff --git a/WebCore/rendering/RenderRubyRun.h b/WebCore/rendering/RenderRubyRun.h index 361dfe5..222ddb6 100644 --- a/WebCore/rendering/RenderRubyRun.h +++ b/WebCore/rendering/RenderRubyRun.h @@ -48,10 +48,6 @@ public: virtual void destroy(); - virtual const char* renderName() const { return "RenderRubyRun (anonymous)"; } - - virtual bool isRubyRun() const { return true; } - bool hasRubyText() const; bool hasRubyBase() const; bool isEmpty() const; @@ -70,8 +66,13 @@ public: protected: RenderRubyBase* createRubyBase() const; - + private: + virtual bool isRubyRun() const { return true; } + virtual const char* renderName() const { return "RenderRubyRun (anonymous)"; } + virtual bool createsAnonymousWrapper() const { return true; } + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { } + bool m_beingDestroyed; }; diff --git a/WebCore/rendering/RenderSVGBlock.h b/WebCore/rendering/RenderSVGBlock.h index a4ececb..0b0d107 100644 --- a/WebCore/rendering/RenderSVGBlock.h +++ b/WebCore/rendering/RenderSVGBlock.h @@ -35,6 +35,8 @@ class RenderSVGBlock : public RenderBlock, protected SVGRenderBase { public: RenderSVGBlock(SVGElement*); + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } + private: virtual void setStyle(PassRefPtr<RenderStyle>); }; diff --git a/WebCore/rendering/RenderSVGContainer.cpp b/WebCore/rendering/RenderSVGContainer.cpp index d7aec99..6d1b965 100644 --- a/WebCore/rendering/RenderSVGContainer.cpp +++ b/WebCore/rendering/RenderSVGContainer.cpp @@ -3,6 +3,7 @@ 2004, 2005, 2007, 2008 Rob Buis <buis@kde.org> 2007 Eric Seidel <eric@webkit.org> Copyright (C) 2009 Google, Inc. All rights reserved. + 2009 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -25,14 +26,11 @@ #if ENABLE(SVG) #include "RenderSVGContainer.h" -#include "AXObjectCache.h" -#include "FloatQuad.h" #include "GraphicsContext.h" #include "RenderView.h" #include "SVGRenderSupport.h" #include "SVGResourceFilter.h" #include "SVGStyledElement.h" -#include "SVGURIReference.h" namespace WebCore { @@ -62,17 +60,7 @@ void RenderSVGContainer::layout() LayoutRepainter repainter(*this, checkForRepaintDuringLayout() || selfWillPaint()); calculateLocalTransform(); // Allow RenderSVGTransformableContainer to update its transform - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - // Only force our kids to layout if we're being asked to relayout as a result of a parent changing - // FIXME: We should be able to skip relayout of non-relative kids when only bounds size has changed - // that's a possible future optimization using LayoutState - // http://bugs.webkit.org/show_bug.cgi?id=15391 - if (selfNeedsLayout()) - child->setNeedsLayout(true); - - child->layoutIfNeeded(); - ASSERT(!child->needsLayout()); - } + layoutChildren(this, selfNeedsLayout()); repainter.repaintAfterLayout(); setNeedsLayout(false); @@ -82,7 +70,7 @@ bool RenderSVGContainer::selfWillPaint() const { #if ENABLE(FILTERS) const SVGRenderStyle* svgStyle = style()->svgStyle(); - SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter()); + SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter(), this); if (filter) return true; #endif @@ -94,7 +82,7 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int) if (paintInfo.context->paintingDisabled() || !drawsContents()) return; - // Spec: groups w/o children still may render filter content. + // Spec: groups w/o children still may render filter content. if (!firstChild() && !selfWillPaint()) return; @@ -109,12 +97,16 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int) SVGResourceFilter* filter = 0; FloatRect boundingBox = repaintRectInLocalCoordinates(); + + bool continueRendering = true; if (childPaintInfo.phase == PaintPhaseForeground) - prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); + continueRendering = prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); - childPaintInfo.paintingRoot = paintingRootForChildren(childPaintInfo); - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) - child->paint(childPaintInfo, 0, 0); + if (continueRendering) { + childPaintInfo.paintingRoot = paintingRootForChildren(childPaintInfo); + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) + child->paint(childPaintInfo, 0, 0); + } if (paintInfo.phase == PaintPhaseForeground) finishRenderSVGContent(this, childPaintInfo, filter, paintInfo.context); @@ -132,10 +124,11 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int) } // addFocusRingRects is called from paintOutline and needs to be in the same coordinates as the paintOuline call -void RenderSVGContainer::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +void RenderSVGContainer::addFocusRingRects(Vector<IntRect>& rects, int, int) { IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRectInLocalCoordinates())); - graphicsContext->addFocusRingRect(paintRectInParent); + if (!paintRectInParent.isEmpty()) + rects.append(paintRectInParent); } FloatRect RenderSVGContainer::objectBoundingBox() const @@ -143,14 +136,30 @@ FloatRect RenderSVGContainer::objectBoundingBox() const return computeContainerBoundingBox(this, false); } +FloatRect RenderSVGContainer::strokeBoundingBox() const +{ + return computeContainerBoundingBox(this, true); +} + // RenderSVGContainer is used for <g> elements which do not themselves have a // width or height, so we union all of our child rects as our repaint rect. FloatRect RenderSVGContainer::repaintRectInLocalCoordinates() const { FloatRect repaintRect = computeContainerBoundingBox(this, true); - // A filter on this container can paint outside of the union of the child repaint rects - repaintRect.unite(filterBoundingBoxForRenderer(this)); + FloatRect rect = filterBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect = rect; + + rect = clipperBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect.intersect(rect); + + rect = maskerBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect.intersect(rect); + + style()->svgStyle()->inflateForShadow(repaintRect); return repaintRect; } @@ -178,5 +187,3 @@ bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTest } #endif // ENABLE(SVG) - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderSVGContainer.h b/WebCore/rendering/RenderSVGContainer.h index f2195e3..f681e50 100644 --- a/WebCore/rendering/RenderSVGContainer.h +++ b/WebCore/rendering/RenderSVGContainer.h @@ -42,10 +42,9 @@ public: void setDrawsContents(bool); bool drawsContents() const; -protected: virtual void paint(PaintInfo&, int parentX, int parentY); -private: +protected: virtual RenderObjectChildList* virtualChildren() { return children(); } virtual const RenderObjectChildList* virtualChildren() const { return children(); } @@ -54,9 +53,10 @@ private: virtual void layout(); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const; virtual FloatRect repaintRectInLocalCoordinates() const; virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); @@ -71,6 +71,7 @@ private: bool selfWillPaint() const; +private: RenderObjectChildList m_children; bool m_drawsContents : 1; }; @@ -78,14 +79,14 @@ private: inline RenderSVGContainer* toRenderSVGContainer(RenderObject* object) { // Note: isSVGContainer is also true for RenderSVGViewportContainer, which is not derived from this. - ASSERT(!object || object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer")); + ASSERT(!object || (object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer"))); return static_cast<RenderSVGContainer*>(object); } inline const RenderSVGContainer* toRenderSVGContainer(const RenderObject* object) { // Note: isSVGContainer is also true for RenderSVGViewportContainer, which is not derived from this. - ASSERT(!object || object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer")); + ASSERT(!object || (object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer"))); return static_cast<const RenderSVGContainer*>(object); } diff --git a/WebCore/rendering/RenderSVGGradientStop.cpp b/WebCore/rendering/RenderSVGGradientStop.cpp index b81e7f4..66391c8 100644 --- a/WebCore/rendering/RenderSVGGradientStop.cpp +++ b/WebCore/rendering/RenderSVGGradientStop.cpp @@ -49,7 +49,7 @@ void RenderSVGGradientStop::styleDidChange(StyleDifference diff, const RenderSty // <stop> elements should only be allowed to make renderers under gradient elements // but I can imagine a few cases we might not be catching, so let's not crash if our parent isn't a gradient. if (SVGGradientElement* gradient = gradientElement()) { - if (SVGResource* resource = gradient->canvasResource()) + if (SVGResource* resource = gradient->canvasResource(this)) resource->invalidate(); } } diff --git a/WebCore/rendering/RenderSVGHiddenContainer.cpp b/WebCore/rendering/RenderSVGHiddenContainer.cpp index d4b39d3..bb0a15d 100644 --- a/WebCore/rendering/RenderSVGHiddenContainer.cpp +++ b/WebCore/rendering/RenderSVGHiddenContainer.cpp @@ -38,17 +38,7 @@ RenderSVGHiddenContainer::RenderSVGHiddenContainer(SVGStyledElement* element) void RenderSVGHiddenContainer::layout() { ASSERT(needsLayout()); - - // Layout our kids to prevent a kid from being marked as needing layout - // then never being asked to layout. - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (selfNeedsLayout()) - child->setNeedsLayout(true); - - child->layoutIfNeeded(); - ASSERT(!child->needsLayout()); - } - + layoutChildren(this, selfNeedsLayout()); setNeedsLayout(false); } diff --git a/WebCore/rendering/RenderSVGImage.cpp b/WebCore/rendering/RenderSVGImage.cpp index 41a1a10..c8fb132 100644 --- a/WebCore/rendering/RenderSVGImage.cpp +++ b/WebCore/rendering/RenderSVGImage.cpp @@ -4,6 +4,7 @@ Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> Copyright (C) 2007, 2008, 2009 Rob Buis <buis@kde.org> Copyright (C) 2009, Google, Inc. + Copyright (C) 2009 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -47,81 +48,6 @@ RenderSVGImage::RenderSVGImage(SVGImageElement* impl) { } -void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio* aspectRatio) -{ - float origDestWidth = destRect.width(); - float origDestHeight = destRect.height(); - if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET) { - float widthToHeightMultiplier = srcRect.height() / srcRect.width(); - if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) { - destRect.setHeight(origDestWidth * widthToHeightMultiplier); - switch (aspectRatio->align()) { - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: - destRect.setY(destRect.y() + origDestHeight / 2.0f - destRect.height() / 2.0f); - break; - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: - destRect.setY(destRect.y() + origDestHeight - destRect.height()); - break; - } - } - if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) { - destRect.setWidth(origDestHeight / widthToHeightMultiplier); - switch (aspectRatio->align()) { - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: - destRect.setX(destRect.x() + origDestWidth / 2.0f - destRect.width() / 2.0f); - break; - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: - destRect.setX(destRect.x() + origDestWidth - destRect.width()); - break; - } - } - } else if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) { - float widthToHeightMultiplier = srcRect.height() / srcRect.width(); - // if the destination height is less than the height of the image we'll be drawing - if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) { - float destToSrcMultiplier = srcRect.width() / destRect.width(); - srcRect.setHeight(destRect.height() * destToSrcMultiplier); - switch (aspectRatio->align()) { - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: - srcRect.setY(destRect.y() + image()->height() / 2.0f - srcRect.height() / 2.0f); - break; - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: - srcRect.setY(destRect.y() + image()->height() - srcRect.height()); - break; - } - } - // if the destination width is less than the width of the image we'll be drawing - if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) { - float destToSrcMultiplier = srcRect.height() / destRect.height(); - srcRect.setWidth(destRect.width() * destToSrcMultiplier); - switch (aspectRatio->align()) { - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: - srcRect.setX(destRect.x() + image()->width() / 2.0f - srcRect.width() / 2.0f); - break; - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: - srcRect.setX(destRect.x() + image()->width() - srcRect.width()); - break; - } - } - } -} - void RenderSVGImage::layout() { ASSERT(needsLayout()); @@ -138,6 +64,7 @@ void RenderSVGImage::layout() calcHeight(); m_localBounds = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image)); + m_cachedLocalRepaintRect = FloatRect(); repainter.repaintAfterLayout(); @@ -157,16 +84,16 @@ void RenderSVGImage::paint(PaintInfo& paintInfo, int, int) PaintInfo savedInfo(paintInfo); - prepareToRenderSVGContent(this, paintInfo, m_localBounds, filter); + if (prepareToRenderSVGContent(this, paintInfo, m_localBounds, filter)) { + FloatRect destRect = m_localBounds; + FloatRect srcRect(0, 0, image()->width(), image()->height()); - FloatRect destRect = m_localBounds; - FloatRect srcRect(0, 0, image()->width(), image()->height()); + SVGImageElement* imageElt = static_cast<SVGImageElement*>(node()); + if (imageElt->preserveAspectRatio().align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) + imageElt->preserveAspectRatio().transformRect(destRect, srcRect); - SVGImageElement* imageElt = static_cast<SVGImageElement*>(node()); - if (imageElt->preserveAspectRatio()->align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) - adjustRectsForAspectRatio(destRect, srcRect, imageElt->preserveAspectRatio()); - - paintInfo.context->drawImage(image(), DeviceColorSpace, destRect, srcRect); + paintInfo.context->drawImage(image(), DeviceColorSpace, destRect, srcRect); + } finishRenderSVGContent(this, paintInfo, filter, savedInfo.context); } @@ -212,12 +139,29 @@ FloatRect RenderSVGImage::objectBoundingBox() const FloatRect RenderSVGImage::repaintRectInLocalCoordinates() const { - FloatRect repaintRect = m_localBounds; + // If we already have a cached repaint rect, return that + if (!m_cachedLocalRepaintRect.isEmpty()) + return m_cachedLocalRepaintRect; + + m_cachedLocalRepaintRect = m_localBounds; + + // FIXME: We need to be careful here. We assume that there is no filter, + // clipper or masker if the rects are empty. + FloatRect rect = filterBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect = rect; + + rect = clipperBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect.intersect(rect); + + rect = maskerBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect.intersect(rect); - // Filters can paint outside the image content - repaintRect.unite(filterBoundingBoxForRenderer(this)); + style()->svgStyle()->inflateForShadow(m_cachedLocalRepaintRect); - return repaintRect; + return m_cachedLocalRepaintRect; } void RenderSVGImage::imageChanged(WrappedImagePtr image, const IntRect* rect) @@ -233,6 +177,7 @@ IntRect RenderSVGImage::clippedOverflowRectForRepaint(RenderBoxModelObject* repa void RenderSVGImage::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) { + style()->svgStyle()->inflateForShadow(repaintRect); SVGRenderBase::computeRectForRepaint(this, repaintContainer, repaintRect, fixed); } @@ -241,11 +186,12 @@ void RenderSVGImage::mapLocalToContainer(RenderBoxModelObject* repaintContainer, SVGRenderBase::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState); } -void RenderSVGImage::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, int, int) { // this is called from paint() after the localTransform has already been applied IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates()); - graphicsContext->addFocusRingRect(contentRect); + if (!contentRect.isEmpty()) + rects.append(contentRect); } void RenderSVGImage::absoluteRects(Vector<IntRect>& rects, int, int) diff --git a/WebCore/rendering/RenderSVGImage.h b/WebCore/rendering/RenderSVGImage.h index ef11719..0558aed 100644 --- a/WebCore/rendering/RenderSVGImage.h +++ b/WebCore/rendering/RenderSVGImage.h @@ -24,28 +24,29 @@ #define RenderSVGImage_h #if ENABLE(SVG) - -#include "TransformationMatrix.h" #include "FloatRect.h" #include "RenderImage.h" +#include "SVGPreserveAspectRatio.h" #include "SVGRenderSupport.h" +#include "TransformationMatrix.h" namespace WebCore { class SVGImageElement; - class SVGPreserveAspectRatio; class RenderSVGImage : public RenderImage, SVGRenderBase { public: RenderSVGImage(SVGImageElement*); private: + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } virtual const char* renderName() const { return "RenderSVGImage"; } virtual bool isSVGImage() const { return true; } - virtual TransformationMatrix localToParentTransform() const { return m_localTransform; } + virtual const TransformationMatrix& localToParentTransform() const { return m_localTransform; } virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const { return m_localBounds; } virtual FloatRect repaintRectInLocalCoordinates() const; virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); @@ -55,10 +56,9 @@ namespace WebCore { virtual void absoluteRects(Vector<IntRect>&, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); - void adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio*); virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); @@ -72,6 +72,7 @@ namespace WebCore { TransformationMatrix m_localTransform; FloatRect m_localBounds; + mutable FloatRect m_cachedLocalRepaintRect; }; } // namespace WebCore diff --git a/WebCore/rendering/RenderSVGInline.h b/WebCore/rendering/RenderSVGInline.h index 9f9f3f5..53fd4b7 100644 --- a/WebCore/rendering/RenderSVGInline.h +++ b/WebCore/rendering/RenderSVGInline.h @@ -27,6 +27,8 @@ #if ENABLE(SVG) #include "RenderInline.h" +#include "SVGRenderSupport.h" + namespace WebCore { class RenderSVGInline : public RenderInline { @@ -38,6 +40,9 @@ public: // These are shared between RenderSVGTSpan and RenderSVGTextPath virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); + + virtual FloatRect objectBoundingBox() const { return FloatRect(); } + virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); } private: virtual InlineFlowBox* createInlineFlowBox(); diff --git a/WebCore/rendering/RenderSVGModelObject.cpp b/WebCore/rendering/RenderSVGModelObject.cpp index 3fab5a6..7a76fbd 100644 --- a/WebCore/rendering/RenderSVGModelObject.cpp +++ b/WebCore/rendering/RenderSVGModelObject.cpp @@ -56,6 +56,7 @@ IntRect RenderSVGModelObject::clippedOverflowRectForRepaint(RenderBoxModelObject void RenderSVGModelObject::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) { + style()->svgStyle()->inflateForShadow(repaintRect); SVGRenderBase::computeRectForRepaint(this, repaintContainer, repaintRect, fixed); } diff --git a/WebCore/rendering/RenderSVGModelObject.h b/WebCore/rendering/RenderSVGModelObject.h index 0aa13ad..4c50734 100644 --- a/WebCore/rendering/RenderSVGModelObject.h +++ b/WebCore/rendering/RenderSVGModelObject.h @@ -49,6 +49,8 @@ class RenderSVGModelObject : public RenderObject, protected SVGRenderBase { public: RenderSVGModelObject(SVGStyledElement*); + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } + virtual bool requiresLayer() const { return false; } virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); diff --git a/WebCore/rendering/RenderSVGRoot.cpp b/WebCore/rendering/RenderSVGRoot.cpp index 0a39bf4..4a3bbcc 100644 --- a/WebCore/rendering/RenderSVGRoot.cpp +++ b/WebCore/rendering/RenderSVGRoot.cpp @@ -86,23 +86,20 @@ void RenderSVGRoot::layout() LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout()); + int oldWidth = width(); calcWidth(); + + int oldHeight = height(); calcHeight(); SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); setWidth(static_cast<int>(width() * svg->currentScale())); setHeight(static_cast<int>(height() * svg->currentScale())); - calcViewport(); - - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (selfNeedsLayout()) // either bounds or transform changed, force kids to relayout - child->setNeedsLayout(true, false); - - child->layoutIfNeeded(); - ASSERT(!child->needsLayout()); - } + // RenderSVGRoot needs to take special care to propagate window size changes to the children, + // if the outermost <svg> is using relative x/y/width/height values. Hence the additonal parameters. + layoutChildren(this, selfNeedsLayout() || (svg->hasRelativeValues() && (width() != oldWidth || height() != oldHeight))); repainter.repaintAfterLayout(); view()->enableLayoutState(); @@ -113,7 +110,7 @@ bool RenderSVGRoot::selfWillPaint() const { #if ENABLE(FILTERS) const SVGRenderStyle* svgStyle = style()->svgStyle(); - SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter()); + SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter(), this); if (filter) return true; #endif @@ -153,10 +150,13 @@ void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) SVGResourceFilter* filter = 0; FloatRect boundingBox = repaintRectInLocalCoordinates(); + + bool continueRendering = true; if (childPaintInfo.phase == PaintPhaseForeground) - prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); + continueRendering = prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); - RenderBox::paint(childPaintInfo, 0, 0); + if (continueRendering) + RenderBox::paint(childPaintInfo, 0, 0); if (childPaintInfo.phase == PaintPhaseForeground) finishRenderSVGContent(this, childPaintInfo, filter, paintInfo.context); @@ -224,14 +224,15 @@ TransformationMatrix RenderSVGRoot::localToRepaintContainerTransform(const IntPo return localToParentTransform() * parentToContainer; } -TransformationMatrix RenderSVGRoot::localToParentTransform() const +const TransformationMatrix& RenderSVGRoot::localToParentTransform() const { IntSize parentToBorderBoxOffset = parentOriginToBorderBox(); TransformationMatrix borderBoxOriginToParentOrigin; borderBoxOriginToParentOrigin.translate(parentToBorderBoxOffset.width(), parentToBorderBoxOffset.height()); - return localToBorderBoxTransform() * borderBoxOriginToParentOrigin; + m_localToParentTransform = localToBorderBoxTransform() * borderBoxOriginToParentOrigin; + return m_localToParentTransform; } // FIXME: This method should be removed as soon as callers to RenderBox::absoluteTransform() can be removed. @@ -249,7 +250,9 @@ FloatRect RenderSVGRoot::objectBoundingBox() const FloatRect RenderSVGRoot::repaintRectInLocalCoordinates() const { // FIXME: This does not include the border but it should! - return computeContainerBoundingBox(this, true); + FloatRect repaintRect = computeContainerBoundingBox(this, true); + style()->svgStyle()->inflateForShadow(repaintRect); + return repaintRect; } TransformationMatrix RenderSVGRoot::localTransform() const @@ -259,8 +262,10 @@ TransformationMatrix RenderSVGRoot::localTransform() const void RenderSVGRoot::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) { - // Apply our local transforms (except for x/y translation) and call RenderBox's method to handle all the normal CSS Box model bits + // Apply our local transforms (except for x/y translation), then our shadow, + // and then call RenderBox's method to handle all the normal CSS Box model bits repaintRect = localToBorderBoxTransform().mapRect(repaintRect); + style()->svgStyle()->inflateForShadow(repaintRect); RenderBox::computeRectForRepaint(repaintContainer, repaintRect, fixed); } @@ -310,5 +315,3 @@ bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& re } #endif // ENABLE(SVG) - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderSVGRoot.h b/WebCore/rendering/RenderSVGRoot.h index 08c3058..b2f8f7c 100644 --- a/WebCore/rendering/RenderSVGRoot.h +++ b/WebCore/rendering/RenderSVGRoot.h @@ -54,12 +54,13 @@ private: virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); - virtual TransformationMatrix localToParentTransform() const; + virtual const TransformationMatrix& localToParentTransform() const; bool fillContains(const FloatPoint&) const; bool strokeContains(const FloatPoint&) const; virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const { return computeContainerBoundingBox(this, true); } virtual FloatRect repaintRectInLocalCoordinates() const; // FIXME: Both of these overrides should be removed. @@ -84,6 +85,7 @@ private: RenderObjectChildList m_children; FloatSize m_viewportSize; + mutable TransformationMatrix m_localToParentTransform; }; inline RenderSVGRoot* toRenderSVGRoot(RenderObject* object) diff --git a/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp b/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp new file mode 100644 index 0000000..9d3d26f --- /dev/null +++ b/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp @@ -0,0 +1,101 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#if ENABLE(SVG) +#include "RenderSVGShadowTreeRootContainer.h" + +#include "MouseEvent.h" +#include "SVGShadowTreeElements.h" +#include "SVGUseElement.h" + +namespace WebCore { + +RenderSVGShadowTreeRootContainer::RenderSVGShadowTreeRootContainer(SVGUseElement* node) + : RenderSVGTransformableContainer(node) + , m_recreateTree(false) +{ +} + +RenderSVGShadowTreeRootContainer::~RenderSVGShadowTreeRootContainer() +{ + if (m_shadowRoot && m_shadowRoot->attached()) + m_shadowRoot->detach(); +} + +void RenderSVGShadowTreeRootContainer::updateStyle(Node::StyleChange change) +{ + if (m_shadowRoot && m_shadowRoot->attached()) + m_shadowRoot->recalcStyle(change); +} + +void RenderSVGShadowTreeRootContainer::updateFromElement() +{ + bool hadExistingTree = m_shadowRoot; + + SVGUseElement* useElement = static_cast<SVGUseElement*>(node()); + if (!m_shadowRoot) { + ASSERT(!m_recreateTree); + m_shadowRoot = new SVGShadowTreeRootElement(document(), useElement); + useElement->buildPendingResource(); + } + + ASSERT(m_shadowRoot->shadowParentNode() == useElement); + + bool shouldRecreateTree = m_recreateTree; + if (m_recreateTree) { + ASSERT(hadExistingTree); + + if (m_shadowRoot->attached()) + m_shadowRoot->detach(); + + m_shadowRoot->removeAllChildren(); + m_recreateTree = false; + } + + // Only rebuild the shadow tree, if we a) never had a tree or b) we were specifically asked to do so + // If the use element is a pending resource, and a) or b) is true, do nothing, and wait for the use + // element to be asked to buildPendingResource(), this will call us again, with m_recreateTrue=true. + if ((shouldRecreateTree || !hadExistingTree) && !useElement->isPendingResource()) { + useElement->buildShadowAndInstanceTree(m_shadowRoot.get()); + + // Attach shadow root element + m_shadowRoot->attachElement(style(), renderArena()); + + // Attach subtree, as if it was a regular non-shadow tree + for (Node* child = m_shadowRoot->firstChild(); child; child = child->nextSibling()) + child->attach(); + } + + ASSERT(!m_recreateTree); + RenderSVGTransformableContainer::updateFromElement(); +} + +void RenderSVGShadowTreeRootContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderSVGTransformableContainer::styleDidChange(diff, oldStyle); + + if (RenderObject* shadowRootRenderer = m_shadowRoot ? m_shadowRoot->renderer() : 0) + shadowRootRenderer->setStyle(style()); +} + +} + +#endif diff --git a/WebCore/rendering/RenderSVGShadowTreeRootContainer.h b/WebCore/rendering/RenderSVGShadowTreeRootContainer.h new file mode 100644 index 0000000..01cd427 --- /dev/null +++ b/WebCore/rendering/RenderSVGShadowTreeRootContainer.h @@ -0,0 +1,50 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef RenderSVGShadowTreeRootContainer_h +#define RenderSVGShadowTreeRootContainer_h + +#if ENABLE(SVG) +#include "RenderSVGTransformableContainer.h" + +namespace WebCore { + +class SVGUseElement; +class SVGShadowTreeRootElement; + +class RenderSVGShadowTreeRootContainer : public RenderSVGTransformableContainer { +public: + RenderSVGShadowTreeRootContainer(SVGUseElement*); + virtual ~RenderSVGShadowTreeRootContainer(); + + void markShadowTreeForRecreation() { m_recreateTree = true; } + void updateStyle(Node::StyleChange); + virtual void updateFromElement(); + +private: + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + bool m_recreateTree; + RefPtr<SVGShadowTreeRootElement> m_shadowRoot; +}; + +} + +#endif +#endif diff --git a/WebCore/rendering/RenderSVGText.cpp b/WebCore/rendering/RenderSVGText.cpp index 3919d7f..0cf0332 100644 --- a/WebCore/rendering/RenderSVGText.cpp +++ b/WebCore/rendering/RenderSVGText.cpp @@ -6,6 +6,7 @@ * 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> * 2007 Nikolas Zimmermann <zimmermann@kde.org> * 2008 Rob Buis <buis@kde.org> + * 2009 Dirk Schulze <krit@webkit.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -58,6 +59,7 @@ IntRect RenderSVGText::clippedOverflowRectForRepaint(RenderBoxModelObject* repai void RenderSVGText::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) { + style()->svgStyle()->inflateForShadow(repaintRect); SVGRenderBase::computeRectForRepaint(this, repaintContainer, repaintRect, fixed); } @@ -184,7 +186,7 @@ FloatRect RenderSVGText::objectBoundingBox() const return boundingBox; } -FloatRect RenderSVGText::repaintRectInLocalCoordinates() const +FloatRect RenderSVGText::strokeBoundingBox() const { FloatRect repaintRect = objectBoundingBox(); @@ -205,7 +207,28 @@ FloatRect RenderSVGText::repaintRectInLocalCoordinates() const repaintRect.inflate(strokeWidth); } - repaintRect.unite(filterBoundingBoxForRenderer(this)); + return repaintRect; +} + +FloatRect RenderSVGText::repaintRectInLocalCoordinates() const +{ + FloatRect repaintRect = strokeBoundingBox(); + + // FIXME: We need to be careful here. We assume that there is no filter, + // clipper or masker if the rects are empty. + FloatRect rect = filterBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect = rect; + + rect = clipperBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect.intersect(rect); + + rect = maskerBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect.intersect(rect); + + style()->svgStyle()->inflateForShadow(repaintRect); return repaintRect; } diff --git a/WebCore/rendering/RenderSVGText.h b/WebCore/rendering/RenderSVGText.h index 9a2770b..d001d1c 100644 --- a/WebCore/rendering/RenderSVGText.h +++ b/WebCore/rendering/RenderSVGText.h @@ -40,9 +40,11 @@ public: private: virtual const char* renderName() const { return "RenderSVGText"; } + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } + virtual bool isSVGText() const { return true; } - virtual TransformationMatrix localToParentTransform() const { return m_localTransform; } + virtual const TransformationMatrix& localToParentTransform() const { return m_localTransform; } virtual void paint(PaintInfo&, int tx, int ty); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); @@ -60,6 +62,7 @@ private: virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const; virtual FloatRect repaintRectInLocalCoordinates() const; // FIXME: This can be removed when localTransform() is removed from RenderObject diff --git a/WebCore/rendering/RenderSVGTransformableContainer.cpp b/WebCore/rendering/RenderSVGTransformableContainer.cpp index 2324eee..050e1bd 100644 --- a/WebCore/rendering/RenderSVGTransformableContainer.cpp +++ b/WebCore/rendering/RenderSVGTransformableContainer.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org> + Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org> 2004, 2005, 2006 Rob Buis <buis@kde.org> 2009 Google, Inc. @@ -20,12 +20,12 @@ */ #include "config.h" -#if ENABLE(SVG) +#if ENABLE(SVG) #include "RenderSVGTransformableContainer.h" +#include "SVGShadowTreeElements.h" #include "SVGStyledTransformableElement.h" -#include "SVGTransformList.h" namespace WebCore { @@ -34,7 +34,7 @@ RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGStyledTransf { } -TransformationMatrix RenderSVGTransformableContainer::localToParentTransform() const +const TransformationMatrix& RenderSVGTransformableContainer::localToParentTransform() const { return m_localTransform; } @@ -47,6 +47,14 @@ TransformationMatrix RenderSVGTransformableContainer::localTransform() const void RenderSVGTransformableContainer::calculateLocalTransform() { m_localTransform = static_cast<SVGStyledTransformableElement*>(node())->animatedLocalTransform(); + if (!node()->hasTagName(SVGNames::gTag) || !static_cast<SVGGElement*>(node())->isShadowTreeContainerElement()) + return; + + FloatSize translation = static_cast<SVGShadowTreeContainerElement*>(node())->containerTranslation(); + if (translation.width() == 0 && translation.height() == 0) + return; + + m_localTransform.translateRight(translation.width(), translation.height()); } } diff --git a/WebCore/rendering/RenderSVGTransformableContainer.h b/WebCore/rendering/RenderSVGTransformableContainer.h index c929761..43e4001 100644 --- a/WebCore/rendering/RenderSVGTransformableContainer.h +++ b/WebCore/rendering/RenderSVGTransformableContainer.h @@ -31,7 +31,7 @@ namespace WebCore { public: RenderSVGTransformableContainer(SVGStyledTransformableElement*); - virtual TransformationMatrix localToParentTransform() const; + virtual const TransformationMatrix& localToParentTransform() const; private: virtual void calculateLocalTransform(); diff --git a/WebCore/rendering/RenderSVGViewportContainer.cpp b/WebCore/rendering/RenderSVGViewportContainer.cpp index a432ef3..b46e8c2 100644 --- a/WebCore/rendering/RenderSVGViewportContainer.cpp +++ b/WebCore/rendering/RenderSVGViewportContainer.cpp @@ -38,17 +38,27 @@ RenderSVGViewportContainer::RenderSVGViewportContainer(SVGStyledElement* node) { } -void RenderSVGViewportContainer::paint(PaintInfo& paintInfo, int parentX, int parentY) +FloatRect RenderSVGViewportContainer::markerBoundaries(const TransformationMatrix& markerTransformation) const { - // FIXME: The if statement here evaluates to false. isEmpty() is exactly the same - // as what is on the right side, so it's basically !isEmpty && isEmpty. So this - // function does nothing. + FloatRect coordinates = repaintRectInLocalCoordinates(); - // A value of zero disables rendering of the element. - if (!m_viewport.isEmpty() && (m_viewport.width() <= 0. || m_viewport.height() <= 0.)) - return; + // Map repaint rect into parent coordinate space, in which the marker boundaries have to be evaluated + coordinates = localToParentTransform().mapRect(coordinates); - RenderSVGContainer::paint(paintInfo, parentX, parentY); + return markerTransformation.mapRect(coordinates); +} + +TransformationMatrix RenderSVGViewportContainer::markerContentTransformation(const TransformationMatrix& contentTransformation, const FloatPoint& origin, float strokeWidth) const +{ + // The 'origin' coordinate maps to SVGs refX/refY, given in coordinates relative to the viewport established by the marker + FloatPoint mappedOrigin = viewportTransform().mapPoint(origin); + + TransformationMatrix transformation = contentTransformation; + if (strokeWidth != -1) + transformation.scaleNonUniform(strokeWidth, strokeWidth); + + transformation.translate(-mappedOrigin.x(), -mappedOrigin.y()); + return transformation; } void RenderSVGViewportContainer::applyViewportClip(PaintInfo& paintInfo) @@ -95,11 +105,12 @@ TransformationMatrix RenderSVGViewportContainer::viewportTransform() const return TransformationMatrix(); } -TransformationMatrix RenderSVGViewportContainer::localToParentTransform() const +const TransformationMatrix& RenderSVGViewportContainer::localToParentTransform() const { TransformationMatrix viewportTranslation; viewportTranslation.translate(m_viewport.x(), m_viewport.y()); - return viewportTransform() * viewportTranslation; + m_localToParentTransform = viewportTransform() * viewportTranslation; + return m_localToParentTransform; // If this class were ever given a localTransform(), then the above would read: // return viewportTransform() * localTransform() * viewportTranslation; } @@ -125,5 +136,3 @@ bool RenderSVGViewportContainer::pointIsInsideViewportClip(const FloatPoint& poi } #endif // ENABLE(SVG) - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderSVGViewportContainer.h b/WebCore/rendering/RenderSVGViewportContainer.h index b8b30b5..ee08b60 100644 --- a/WebCore/rendering/RenderSVGViewportContainer.h +++ b/WebCore/rendering/RenderSVGViewportContainer.h @@ -24,7 +24,6 @@ #define RenderSVGViewportContainer_h #if ENABLE(SVG) - #include "RenderSVGContainer.h" namespace WebCore { @@ -35,16 +34,19 @@ class RenderSVGViewportContainer : public RenderSVGContainer { public: RenderSVGViewportContainer(SVGStyledElement*); - // FIXME: This is only public for SVGResourceMarker::draw, likely the callsite should be changed. - TransformationMatrix viewportTransform() const; + // Calculates marker boundaries, mapped to the target element's coordinate space + FloatRect markerBoundaries(const TransformationMatrix& markerTransformation) const; - virtual void paint(PaintInfo&, int parentX, int parentY); + // Generates a transformation matrix usable to render marker content. Handles scaling the marker content + // acording to SVGs markerUnits="strokeWidth" concept, when a strokeWidth value != -1 is passed in. + TransformationMatrix markerContentTransformation(const TransformationMatrix& contentTransformation, const FloatPoint& origin, float strokeWidth = -1) const; private: virtual bool isSVGContainer() const { return true; } virtual const char* renderName() const { return "RenderSVGViewportContainer"; } - virtual TransformationMatrix localToParentTransform() const; + TransformationMatrix viewportTransform() const; + virtual const TransformationMatrix& localToParentTransform() const; // FIXME: This override should be removed once callers of RenderBox::absoluteTransform() can be removed. virtual TransformationMatrix absoluteTransform() const; @@ -55,6 +57,7 @@ private: virtual bool pointIsInsideViewportClip(const FloatPoint& pointInParent); FloatRect m_viewport; + mutable TransformationMatrix m_localToParentTransform; }; inline RenderSVGViewportContainer* toRenderSVGViewportContainer(RenderObject* object) @@ -70,5 +73,3 @@ void toRenderSVGViewportContainer(const RenderSVGViewportContainer*); #endif // ENABLE(SVG) #endif // RenderSVGViewportContainer_h - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderTableCell.cpp b/WebCore/rendering/RenderTableCell.cpp index 4e7036e..2395527 100644 --- a/WebCore/rendering/RenderTableCell.cpp +++ b/WebCore/rendering/RenderTableCell.cpp @@ -237,7 +237,7 @@ void RenderTableCell::computeRectForRepaint(RenderBoxModelObject* repaintContain return; r.setY(r.y()); RenderView* v = view(); - if ((!v || !v->layoutStateEnabled()) && parent()) + if ((!v || !v->layoutStateEnabled() || repaintContainer) && parent()) r.move(-parentBox()->x(), -parentBox()->y()); // Rows are in the same coordinate space, so don't add their offset in. RenderBlock::computeRectForRepaint(repaintContainer, r, fixed); } @@ -845,7 +845,7 @@ void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, i paintInfo.context->save(); paintInfo.context->clip(clipRect); } - paintFillLayers(paintInfo, c, bgLayer, tx, ty, w, h); + paintFillLayers(paintInfo, c, bgLayer, tx, ty, w, h, CompositeSourceOver, backgroundObject); if (shouldClip) paintInfo.context->restore(); } diff --git a/WebCore/rendering/RenderTableCell.h b/WebCore/rendering/RenderTableCell.h index f285198..0f8580d 100644 --- a/WebCore/rendering/RenderTableCell.h +++ b/WebCore/rendering/RenderTableCell.h @@ -106,6 +106,8 @@ public: virtual void setOverrideSize(int); + bool hasVisibleOverflow() const { return m_overflow; } + protected: virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); diff --git a/WebCore/rendering/RenderTableSection.cpp b/WebCore/rendering/RenderTableSection.cpp index b627afe..a2457e1 100644 --- a/WebCore/rendering/RenderTableSection.cpp +++ b/WebCore/rendering/RenderTableSection.cpp @@ -733,12 +733,11 @@ int RenderTableSection::layoutRows(int toAdd) if (r < totalRows - 1 && cell == cellAt(r + 1, c).cell) continue; addOverflowFromChild(cell); + m_hasOverflowingCell |= cell->hasVisibleOverflow(); } } - m_hasOverflowingCell = m_overflow; - - statePusher.pop(); + statePusher.pop(); return height(); } diff --git a/WebCore/rendering/RenderText.cpp b/WebCore/rendering/RenderText.cpp index 95aa277..2e696a9 100644 --- a/WebCore/rendering/RenderText.cpp +++ b/WebCore/rendering/RenderText.cpp @@ -25,7 +25,9 @@ #include "config.h" #include "RenderText.h" +#include "AXObjectCache.h" #include "CharacterNames.h" +#include "EllipsisBox.h" #include "FloatQuad.h" #include "FrameView.h" #include "InlineTextBox.h" @@ -286,7 +288,7 @@ void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, } else { unsigned realEnd = min(box->end() + 1, end); IntRect r = box->selectionRect(0, 0, start, realEnd); - if (!r.isEmpty()) { + if (r.height()) { if (!useSelectionHeight) { // change the height and y position because selectionRect uses selection-specific values r.setHeight(box->height()); @@ -338,7 +340,7 @@ VisiblePosition RenderText::positionForPoint(const IntPoint& point) // at the y coordinate of the last line or below // and the x coordinate is to the right of the last text box right edge offset = lastTextBox()->offsetForPosition(point.x()); - return createVisiblePosition(offset + lastTextBox()->start(), DOWNSTREAM); + return createVisiblePosition(offset + lastTextBox()->start(), VP_UPSTREAM_IF_POSSIBLE); } InlineTextBox* lastBoxAbove = 0; @@ -1014,6 +1016,10 @@ void RenderText::setText(PassRefPtr<StringImpl> text, bool force) setTextInternal(text); setNeedsLayoutAndPrefWidthsRecalc(); m_knownNotToUseFallbackFonts = false; + + AXObjectCache* axObjectCache = document()->axObjectCache(); + if (axObjectCache->accessibilityEnabled()) + axObjectCache->contentChanged(this); } int RenderText::lineHeight(bool firstLine, bool) const @@ -1060,8 +1066,15 @@ void RenderText::positionLineBox(InlineBox* box) if (!s->len()) { // We want the box to be destroyed. s->remove(); + if (m_firstTextBox == s) + m_firstTextBox = s->nextTextBox(); + else + s->prevTextBox()->setNextLineBox(s->nextTextBox()); + if (m_lastTextBox == s) + m_lastTextBox = s->prevTextBox(); + else + s->nextTextBox()->setPreviousLineBox(s->prevTextBox()); s->destroy(renderArena()); - m_firstTextBox = m_lastTextBox = 0; return; } @@ -1164,9 +1177,25 @@ IntRect RenderText::selectionRectForRepaint(RenderBoxModelObject* repaintContain return IntRect(); IntRect rect; - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { rect.unite(box->selectionRect(0, 0, startPos, endPos)); + // Check if there are ellipsis which fall within the selection. + unsigned short truncation = box->truncation(); + if (truncation != cNoTruncation) { + if (EllipsisBox* ellipsis = box->root()->ellipsisBox()) { + int ePos = min<int>(endPos - box->start(), box->len()); + int sPos = max<int>(startPos - box->start(), 0); + // The ellipsis should be considered to be selected if the end of + // the selection is past the beginning of the truncation and the + // beginning of the selection is before or at the beginning of the + // truncation. + if (ePos >= truncation && sPos <= truncation) + rect.unite(ellipsis->selectionRect(0, 0)); + } + } + } + if (clipToVisibleContent) computeRectForRepaint(repaintContainer, rect); else { @@ -1362,7 +1391,7 @@ void RenderText::checkConsistency() const #ifdef CHECK_CONSISTENCY const InlineTextBox* prev = 0; for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) { - ASSERT(child->object() == this); + ASSERT(child->renderer() == this); ASSERT(child->prevTextBox() == prev); prev = child; } diff --git a/WebCore/rendering/RenderTextControl.cpp b/WebCore/rendering/RenderTextControl.cpp index b258597..c0ba070 100644 --- a/WebCore/rendering/RenderTextControl.cpp +++ b/WebCore/rendering/RenderTextControl.cpp @@ -195,7 +195,7 @@ void RenderTextControl::setInnerTextValue(const String& innerTextValue) ASSERT(!ec); } - // We set m_lastChangeWasUserEdit to false since this change was not explicty made by the user (say, via typing on the keyboard), see <rdar://problem/5359921>. + // We set m_lastChangeWasUserEdit to false since this change was not explicitly made by the user (say, via typing on the keyboard), see <rdar://problem/5359921>. m_lastChangeWasUserEdit = false; } @@ -505,9 +505,10 @@ void RenderTextControl::selectionChanged(bool userTriggered) } } -void RenderTextControl::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) { - graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); + if (width() && height()) + rects.append(IntRect(tx, ty, width(), height())); } HTMLElement* RenderTextControl::innerTextElement() const diff --git a/WebCore/rendering/RenderTextControl.h b/WebCore/rendering/RenderTextControl.h index 394eb9c..d1f3749 100644 --- a/WebCore/rendering/RenderTextControl.h +++ b/WebCore/rendering/RenderTextControl.h @@ -99,7 +99,7 @@ private: virtual bool avoidsFloats() const { return true; } void setInnerTextStyle(PassRefPtr<RenderStyle>); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); virtual bool canBeProgramaticallyScrolled(bool) const { return true; } diff --git a/WebCore/rendering/RenderTextControlSingleLine.cpp b/WebCore/rendering/RenderTextControlSingleLine.cpp index 56d4363..b68f004 100644 --- a/WebCore/rendering/RenderTextControlSingleLine.cpp +++ b/WebCore/rendering/RenderTextControlSingleLine.cpp @@ -472,8 +472,12 @@ void RenderTextControlSingleLine::updateFromElement() ExceptionCode ec = 0; innerTextElement()->setInnerText(static_cast<Element*>(node())->getAttribute(placeholderAttr), ec); ASSERT(!ec); - } else - setInnerTextValue(inputElement()->value()); + } else { + if (!inputElement()->suggestedValue().isNull()) + setInnerTextValue(inputElement()->suggestedValue()); + else + setInnerTextValue(inputElement()->value()); + } if (m_searchPopupIsVisible) m_searchPopup->updateFromElement(); diff --git a/WebCore/rendering/RenderTheme.cpp b/WebCore/rendering/RenderTheme.cpp index f6afb77..af92465 100644 --- a/WebCore/rendering/RenderTheme.cpp +++ b/WebCore/rendering/RenderTheme.cpp @@ -87,6 +87,8 @@ void RenderTheme::adjustStyle(CSSStyleSelector* selector, RenderStyle* style, El switch (part) { case ListButtonPart: case CheckboxPart: + case InnerSpinButtonPart: + case OuterSpinButtonPart: case RadioPart: case PushButtonPart: case SquareButtonPart: @@ -176,6 +178,10 @@ void RenderTheme::adjustStyle(CSSStyleSelector* selector, RenderStyle* style, El case DefaultButtonPart: case ButtonPart: return adjustButtonStyle(selector, style, e); + case InnerSpinButtonPart: + return adjustInnerSpinButtonStyle(selector, style, e); + case OuterSpinButtonPart: + return adjustOuterSpinButtonStyle(selector, style, e); #endif case TextFieldPart: return adjustTextFieldStyle(selector, style, e); @@ -236,6 +242,8 @@ bool RenderTheme::paint(RenderObject* o, const RenderObject::PaintInfo& paintInf case ListButtonPart: case DefaultButtonPart: case ButtonPart: + case InnerSpinButtonPart: + case OuterSpinButtonPart: m_theme->paint(part, controlStatesForRenderer(o), const_cast<GraphicsContext*>(paintInfo.context), r, o->style()->effectiveZoom(), o->view()->frameView()); return false; default: @@ -256,6 +264,10 @@ bool RenderTheme::paint(RenderObject* o, const RenderObject::PaintInfo& paintInf case DefaultButtonPart: case ButtonPart: return paintButton(o, paintInfo, r); + case InnerSpinButtonPart: + return paintInnerSpinButton(o, paintInfo, r); + case OuterSpinButtonPart: + return paintOuterSpinButton(o, paintInfo, r); #endif case MenulistPart: return paintMenuList(o, paintInfo, r); @@ -589,7 +601,7 @@ bool RenderTheme::supportsFocusRing(const RenderStyle* style) const bool RenderTheme::stateChanged(RenderObject* o, ControlState state) const { - // Default implementation assumes the controls dont respond to changes in :hover state + // Default implementation assumes the controls don't respond to changes in :hover state if (state == HoverState && !supportsHover(o->style())) return false; @@ -766,6 +778,14 @@ void RenderTheme::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Eleme setButtonSize(style); } +void RenderTheme::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustOuterSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + #endif void RenderTheme::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const diff --git a/WebCore/rendering/RenderTheme.h b/WebCore/rendering/RenderTheme.h index ee359d7..a7c6e13 100644 --- a/WebCore/rendering/RenderTheme.h +++ b/WebCore/rendering/RenderTheme.h @@ -173,6 +173,8 @@ public: // Media controls virtual bool hitTestMediaControlPart(RenderObject*, const IntPoint& absPoint); virtual bool shouldRenderMediaControlPart(ControlPart, Element*); + virtual double mediaControlsFadeInDuration() { return 0.1; } + virtual double mediaControlsFadeOutDuration() { return 0.3; } #endif protected: @@ -203,6 +205,11 @@ protected: virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; virtual bool paintButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } virtual void setButtonSize(RenderStyle*) const { } + + virtual void adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintInnerSpinButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual void adjustOuterSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintOuterSpinButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } #endif virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; diff --git a/WebCore/rendering/RenderThemeChromiumLinux.cpp b/WebCore/rendering/RenderThemeChromiumLinux.cpp index 4b09174..f972a12 100644 --- a/WebCore/rendering/RenderThemeChromiumLinux.cpp +++ b/WebCore/rendering/RenderThemeChromiumLinux.cpp @@ -35,6 +35,14 @@ namespace WebCore { unsigned RenderThemeChromiumLinux::m_thumbInactiveColor = 0xf0ebe5; unsigned RenderThemeChromiumLinux::m_thumbActiveColor = 0xfaf8f5; unsigned RenderThemeChromiumLinux::m_trackColor = 0xe3ddd8; +unsigned RenderThemeChromiumLinux::m_activeSelectionBackgroundColor = + 0xff1e90ff; +unsigned RenderThemeChromiumLinux::m_activeSelectionForegroundColor = + Color::black; +unsigned RenderThemeChromiumLinux::m_inactiveSelectionBackgroundColor = + 0xffc8c8c8; +unsigned RenderThemeChromiumLinux::m_inactiveSelectionForegroundColor = + 0xff323232; PassRefPtr<RenderTheme> RenderThemeChromiumLinux::create() { @@ -96,6 +104,26 @@ Color RenderThemeChromiumLinux::inactiveListBoxSelectionForegroundColor() const return Color(0x32, 0x32, 0x32); } +Color RenderThemeChromiumLinux::platformActiveSelectionBackgroundColor() const +{ + return m_activeSelectionBackgroundColor; +} + +Color RenderThemeChromiumLinux::platformInactiveSelectionBackgroundColor() const +{ + return m_inactiveSelectionBackgroundColor; +} + +Color RenderThemeChromiumLinux::platformActiveSelectionForegroundColor() const +{ + return m_activeSelectionForegroundColor; +} + +Color RenderThemeChromiumLinux::platformInactiveSelectionForegroundColor() const +{ + return m_inactiveSelectionForegroundColor; +} + void RenderThemeChromiumLinux::adjustSliderThumbSize(RenderObject* o) const { // These sizes match the sizes in Chromium Win. @@ -126,6 +154,18 @@ double RenderThemeChromiumLinux::caretBlinkIntervalInternal() const return m_caretBlinkInterval; } +void RenderThemeChromiumLinux::setSelectionColors( + unsigned activeBackgroundColor, + unsigned activeForegroundColor, + unsigned inactiveBackgroundColor, + unsigned inactiveForegroundColor) +{ + m_activeSelectionBackgroundColor = activeBackgroundColor; + m_activeSelectionForegroundColor = activeForegroundColor; + m_inactiveSelectionBackgroundColor = inactiveBackgroundColor; + m_inactiveSelectionForegroundColor = inactiveForegroundColor; +} + void RenderThemeChromiumLinux::setScrollbarColors( SkColor inactiveColor, SkColor activeColor, SkColor trackColor) { diff --git a/WebCore/rendering/RenderThemeChromiumLinux.h b/WebCore/rendering/RenderThemeChromiumLinux.h index 8736b0d..90b043d 100644 --- a/WebCore/rendering/RenderThemeChromiumLinux.h +++ b/WebCore/rendering/RenderThemeChromiumLinux.h @@ -49,11 +49,21 @@ namespace WebCore { virtual Color inactiveListBoxSelectionBackgroundColor() const; virtual Color inactiveListBoxSelectionForegroundColor() const; + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color platformActiveSelectionForegroundColor() const; + virtual Color platformInactiveSelectionForegroundColor() const; + virtual void adjustSliderThumbSize(RenderObject*) const; void setCaretBlinkInterval(double interval); virtual double caretBlinkIntervalInternal() const; + static void setSelectionColors(unsigned activeBackgroundColor, + unsigned activeForegroundColor, + unsigned inactiveBackgroundColor, + unsigned inactiveForegroundColor); + static void setScrollbarColors(unsigned inactive_color, unsigned active_color, unsigned track_color); @@ -70,6 +80,11 @@ namespace WebCore { double m_caretBlinkInterval; + static unsigned m_activeSelectionBackgroundColor; + static unsigned m_activeSelectionForegroundColor; + static unsigned m_inactiveSelectionBackgroundColor; + static unsigned m_inactiveSelectionForegroundColor; + static unsigned m_thumbInactiveColor; static unsigned m_thumbActiveColor; static unsigned m_trackColor; diff --git a/WebCore/rendering/RenderThemeChromiumSkia.cpp b/WebCore/rendering/RenderThemeChromiumSkia.cpp index 016a264..7d3bcec 100644 --- a/WebCore/rendering/RenderThemeChromiumSkia.cpp +++ b/WebCore/rendering/RenderThemeChromiumSkia.cpp @@ -217,6 +217,22 @@ int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle* style) const return 0; } +// These are the default dimensions of radio buttons and checkboxes. +static const int widgetStandardWidth = 13; +static const int widgetStandardHeight = 13; + +// Return a rectangle that has the same center point as |original|, but with a +// size capped at |width| by |height|. +IntRect center(const IntRect& original, int width, int height) +{ + width = std::min(original.width(), width); + height = std::min(original.height(), height); + int x = original.x() + (original.width() - width) / 2; + int y = original.y() + (original.height() - height) / 2; + + return IntRect(x, y, width, height); +} + bool RenderThemeChromiumSkia::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) { static Image* const checkedImage = Image::loadPlatformResource("linuxCheckboxOn").releaseRef(); @@ -231,7 +247,7 @@ bool RenderThemeChromiumSkia::paintCheckbox(RenderObject* o, const RenderObject: else image = this->isChecked(o) ? disabledCheckedImage : disabledUncheckedImage; - i.context->drawImage(image, o->style()->colorSpace(), rect); + i.context->drawImage(image, o->style()->colorSpace(), center(rect, widgetStandardHeight, widgetStandardWidth)); return false; } @@ -246,7 +262,7 @@ void RenderThemeChromiumSkia::setCheckboxSize(RenderStyle* style) const // querying the theme gives you a larger size that accounts for the higher // DPI. Until our entire engine honors a DPI setting other than 96, we // can't rely on the theme's metrics. - const IntSize size(13, 13); + const IntSize size(widgetStandardHeight, widgetStandardWidth); setSizeIfAuto(style, size); } @@ -263,7 +279,7 @@ bool RenderThemeChromiumSkia::paintRadio(RenderObject* o, const RenderObject::Pa else image = this->isChecked(o) ? disabledCheckedImage : disabledUncheckedImage; - i.context->drawImage(image, o->style()->colorSpace(), rect); + i.context->drawImage(image, o->style()->colorSpace(), center(rect, widgetStandardHeight, widgetStandardWidth)); return false; } diff --git a/WebCore/rendering/RenderThemeMac.mm b/WebCore/rendering/RenderThemeMac.mm index 6304947..ddb538b 100644 --- a/WebCore/rendering/RenderThemeMac.mm +++ b/WebCore/rendering/RenderThemeMac.mm @@ -1434,13 +1434,10 @@ static int mediaControllerTheme() controllerTheme = MediaControllerThemeClassic; - if (!wkMediaControllerThemeAvailable(MediaControllerThemeQuickTime)) - return controllerTheme; - Boolean validKey; Boolean useQTMediaUIPref = CFPreferencesGetAppBooleanValue(CFSTR("UseQuickTimeMediaUI"), CFSTR("com.apple.WebCore"), &validKey); -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) +#if !defined(BUILDING_ON_TIGER) if (validKey && !useQTMediaUIPref) return controllerTheme; #else diff --git a/WebCore/rendering/RenderThemeWince.cpp b/WebCore/rendering/RenderThemeWince.cpp index fb89678..c4aaaad 100644 --- a/WebCore/rendering/RenderThemeWince.cpp +++ b/WebCore/rendering/RenderThemeWince.cpp @@ -28,6 +28,7 @@ #include "CSSValueKeywords.h" #include "Document.h" #include "GraphicsContext.h" +#include "NotImplemented.h" #if ENABLE(VIDEO) #include "HTMLMediaElement.h" #endif @@ -377,12 +378,12 @@ bool RenderThemeWince::paintSearchFieldCancelButton(RenderObject* o, const Rende IntRect cancelBounds(IntPoint(x, y), cancelSize); paintInfo.context->save(); paintInfo.context->addRoundedRectClip(cancelBounds, cancelRadius, cancelRadius, cancelRadius, cancelRadius); - paintInfo.context->fillRect(cancelBounds, buttonColor); + paintInfo.context->fillRect(cancelBounds, buttonColor, DeviceColorSpace); // Draw the 'x' IntSize xSize(3, 3); IntRect xBounds(cancelBounds.location() + IntSize(3, 3), xSize); - paintInfo.context->setStrokeColor(Color::white); + paintInfo.context->setStrokeColor(Color::white, DeviceColorSpace); paintInfo.context->drawLine(xBounds.location(), xBounds.location() + xBounds.size()); paintInfo.context->drawLine(IntPoint(xBounds.right(), xBounds.y()), IntPoint(xBounds.x(), xBounds.bottom())); @@ -489,11 +490,11 @@ bool RenderThemeWince::paintSliderTrack(RenderObject* o, const RenderObject::Pai bool rc = RenderTheme::paintSliderTrack(o, i, r); IntPoint left = IntPoint(r.x() + 2, (r.y() + r.bottom()) / 2); i.context->save(); - i.context->setStrokeColor(Color::gray); - i.context->setFillColor(Color::gray); + i.context->setStrokeColor(Color::gray, DeviceColorSpace); + i.context->setFillColor(Color::gray, DeviceColorSpace); i.context->fillRect(r); #if ENABLE(VIDEO) - HTMLMediaElement *mediaElement = mediaElementParent(o->node()); + HTMLMediaElement* mediaElement = mediaElementParent(o->node()); if (mediaElement) { i.context->setStrokeColor(Color(0, 0xff, 0)); IntPoint right = IntPoint(left.x() + mediaElement->percentLoaded() * (r.right() - r.x() - 4), (r.y() + r.bottom()) / 2); @@ -501,7 +502,7 @@ bool RenderThemeWince::paintSliderTrack(RenderObject* o, const RenderObject::Pai left = right; } #endif - i.context->setStrokeColor(Color::black); + i.context->setStrokeColor(Color::black, DeviceColorSpace); i.context->drawLine(left, IntPoint(r.right() - 2, left.y())); i.context->restore(); return rc; @@ -511,10 +512,10 @@ bool RenderThemeWince::paintSliderThumb(RenderObject* o, const RenderObject::Pai { bool rc = RenderTheme::paintSliderThumb(o, i, r); i.context->save(); - i.context->setStrokeColor(Color::black); - i.context->setFillColor(Color::black); + i.context->setStrokeColor(Color::black, DeviceColorSpace); + i.context->setFillColor(Color::black, DeviceColorSpace); #if ENABLE(VIDEO) - HTMLMediaElement *mediaElement = mediaElementParent(o->node()); + HTMLMediaElement* mediaElement = mediaElementParent(o->node()); if (mediaElement) { float pt = (mediaElement->currentTime() - mediaElement->startTime()) / mediaElement->duration(); FloatRect intRect = r; @@ -574,7 +575,7 @@ bool RenderThemeWince::paintMediaFullscreenButton(RenderObject* o, const RenderO bool RenderThemeWince::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { bool rc = paintButton(o, paintInfo, r); - HTMLMediaElement *mediaElement = mediaElementParent(o->node()); + HTMLMediaElement* mediaElement = mediaElementParent(o->node()); bool muted = !mediaElement || mediaElement->muted(); FloatRect imRect = r; imRect.inflate(-2); @@ -604,7 +605,7 @@ bool RenderThemeWince::paintMediaPlayButton(RenderObject* o, const RenderObject: paintInfo.context->save(); paintInfo.context->setStrokeColor(Color::black); paintInfo.context->setFillColor(Color::black); - HTMLMediaElement *mediaElement = mediaElementParent(o->node()); + HTMLMediaElement* mediaElement = mediaElementParent(o->node()); bool paused = !mediaElement || mediaElement->paused(); if (paused) { float width = imRect.width(); diff --git a/WebCore/rendering/RenderTreeAsText.cpp b/WebCore/rendering/RenderTreeAsText.cpp index a6f5144..ca4d9d1 100644 --- a/WebCore/rendering/RenderTreeAsText.cpp +++ b/WebCore/rendering/RenderTreeAsText.cpp @@ -45,6 +45,7 @@ #include "RenderWidget.h" #include "SelectionController.h" #include "TextStream.h" +#include <wtf/UnusedParam.h> #include <wtf/Vector.h> #if ENABLE(SVG) @@ -57,6 +58,10 @@ #include "SVGRenderTreeAsText.h" #endif +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerBacking.h" +#endif + #if PLATFORM(QT) #include <QWidget> #endif @@ -65,7 +70,7 @@ namespace WebCore { using namespace HTMLNames; -static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0); +static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal); #if !ENABLE(SVG) static TextStream &operator<<(TextStream& ts, const IntRect& r) @@ -444,9 +449,15 @@ void write(TextStream& ts, const RenderObject& o, int indent) } } +enum LayerPaintPhase { + LayerPaintPhaseAll = 0, + LayerPaintPhaseBackground = -1, + LayerPaintPhaseForeground = 1 +}; + static void write(TextStream& ts, RenderLayer& l, const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect, - int layerType = 0, int indent = 0) + LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal) { writeIndent(ts, indent); @@ -472,19 +483,28 @@ static void write(TextStream& ts, RenderLayer& l, ts << " scrollHeight " << l.scrollHeight(); } - if (layerType == -1) + if (paintPhase == LayerPaintPhaseBackground) ts << " layerType: background only"; - else if (layerType == 1) + else if (paintPhase == LayerPaintPhaseForeground) ts << " layerType: foreground only"; - + +#if USE(ACCELERATED_COMPOSITING) + if (behavior & RenderAsTextShowCompositedLayers) { + if (l.isComposited()) + ts << " (composited, bounds " << l.backing()->compositedBounds() << ")"; + } +#else + UNUSED_PARAM(behavior); +#endif + ts << "\n"; - if (layerType != -1) + if (paintPhase != LayerPaintPhaseBackground) write(ts, *l.renderer(), indent + 1); } static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l, - const IntRect& paintDirtyRect, int indent) + const IntRect& paintDirtyRect, int indent, RenderAsTextBehavior behavior) { // Calculate the clip rects we should use. IntRect layerBounds, damageRect, clipRectToApply, outlineRect; @@ -494,29 +514,46 @@ static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLaye l->updateZOrderLists(); l->updateNormalFlowList(); - bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect, rootLayer); + bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect, rootLayer); Vector<RenderLayer*>* negList = l->negZOrderList(); - if (shouldPaint && negList && negList->size() > 0) - write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, -1, indent); + bool paintsBackgroundSeparately = negList && negList->size() > 0; + if (shouldPaint && paintsBackgroundSeparately) + write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, LayerPaintPhaseBackground, indent, behavior); if (negList) { + int currIndent = indent; + if (behavior & RenderAsTextShowLayerNesting) { + writeIndent(ts, indent); + ts << " negative z-order list(" << negList->size() << ")\n"; + ++currIndent; + } for (unsigned i = 0; i != negList->size(); ++i) - writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent); + writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior); } if (shouldPaint) - write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, negList && negList->size() > 0, indent); - - Vector<RenderLayer*>* normalFlowList = l->normalFlowList(); - if (normalFlowList) { + write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior); + + if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) { + int currIndent = indent; + if (behavior & RenderAsTextShowLayerNesting) { + writeIndent(ts, indent); + ts << " normal flow list(" << normalFlowList->size() << ")\n"; + ++currIndent; + } for (unsigned i = 0; i != normalFlowList->size(); ++i) - writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, indent); + writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior); } - Vector<RenderLayer*>* posList = l->posZOrderList(); - if (posList) { + if (Vector<RenderLayer*>* posList = l->posZOrderList()) { + int currIndent = indent; + if (behavior & RenderAsTextShowLayerNesting) { + writeIndent(ts, indent); + ts << " positive z-order list(" << posList->size() << ")\n"; + ++currIndent; + } for (unsigned i = 0; i != posList->size(); ++i) - writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent); + writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior); } } @@ -562,7 +599,7 @@ static void writeSelection(TextStream& ts, const RenderObject* o) << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().node()) << "\n"; } -String externalRepresentation(Frame* frame) +String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior) { frame->document()->updateLayout(); @@ -576,7 +613,7 @@ String externalRepresentation(Frame* frame) #endif if (o->hasLayer()) { RenderLayer* l = toRenderBox(o)->layer(); - writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height())); + writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), 0, behavior); writeSelection(ts, o); } return ts.release(); diff --git a/WebCore/rendering/RenderTreeAsText.h b/WebCore/rendering/RenderTreeAsText.h index b00f7c9..13525e7 100644 --- a/WebCore/rendering/RenderTreeAsText.h +++ b/WebCore/rendering/RenderTreeAsText.h @@ -34,7 +34,15 @@ class RenderObject; class String; class TextStream; -String externalRepresentation(Frame*); +enum RenderAsTextBehaviorFlags { + RenderAsTextBehaviorNormal = 0, + RenderAsTextShowAllLayers = 1 << 0, // Dump all layers, not just those that would paint. + RenderAsTextShowLayerNesting = 1 << 1, // Annotate the layer lists. + RenderAsTextShowCompositedLayers = 1 << 2 // Show which layers are composited. +}; +typedef unsigned RenderAsTextBehavior; + +String externalRepresentation(Frame*, RenderAsTextBehavior = RenderAsTextBehaviorNormal); void write(TextStream&, const RenderObject&, int indent = 0); // Helper function shared with SVGRenderTreeAsText diff --git a/WebCore/rendering/RenderVideo.cpp b/WebCore/rendering/RenderVideo.cpp index 246d0c0..d2a9620 100644 --- a/WebCore/rendering/RenderVideo.cpp +++ b/WebCore/rendering/RenderVideo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,6 +34,7 @@ #include "HTMLNames.h" #include "HTMLVideoElement.h" #include "MediaPlayer.h" +#include "RenderView.h" #if USE(ACCELERATED_COMPOSITING) #include "RenderLayer.h" @@ -49,18 +50,25 @@ using namespace HTMLNames; static const int cDefaultWidth = 300; static const int cDefaultHeight = 150; -RenderVideo::RenderVideo(HTMLMediaElement* video) +RenderVideo::RenderVideo(HTMLVideoElement* video) : RenderMedia(video) { if (video->player()) setIntrinsicSize(video->player()->naturalSize()); else { - // Video in standalone media documents should not use the default 300x150 - // size since they also have audio thrown at them. By setting the intrinsic - // size to 300x1 the video will resize itself in these cases, and audio will - // have the correct height (it needs to be > 0 for controls to render properly). - if (video->ownerDocument() && video->ownerDocument()->isMediaDocument()) + // When the natural size of the video is unavailable, we use the provided + // width and height attributes of the video element as the intrinsic size until + // better values become available. If these attributes are not set, we fall back + // to a default video size (300x150). + if (video->hasAttribute(widthAttr) && video->hasAttribute(heightAttr)) + setIntrinsicSize(IntSize(video->width(), video->height())); + else if (video->ownerDocument() && video->ownerDocument()->isMediaDocument()) { + // Video in standalone media documents should not use the default 300x150 + // size since they also have audio thrown at them. By setting the intrinsic + // size to 300x1 the video will resize itself in these cases, and audio will + // have the correct height (it needs to be > 0 for controls to render properly). setIntrinsicSize(IntSize(cDefaultWidth, 1)); + } else setIntrinsicSize(IntSize(cDefaultWidth, cDefaultHeight)); } @@ -73,7 +81,15 @@ RenderVideo::~RenderVideo() p->setFrameView(0); } } - + +void RenderVideo::intrinsicSizeChanged() +{ + if (videoElement()->shouldDisplayPosterImage()) + RenderVideo::intrinsicSizeChanged(); + videoSizeChanged(); +} + + void RenderVideo::videoSizeChanged() { if (!player()) @@ -86,41 +102,72 @@ void RenderVideo::videoSizeChanged() } } -IntRect RenderVideo::videoBox() const +void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect) { + RenderImage::imageChanged(newImage, rect); + + // Cache the image intrinsic size so we can continue to use it to draw the image correctly + // even after we know the video intrisic size but aren't able to draw video frames yet + // (we don't want to scale the poster to the video size). + if (videoElement()->shouldDisplayPosterImage()) + m_cachedImageSize = intrinsicSize(); +} + +IntRect RenderVideo::videoBox() const +{ + if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage()) + return IntRect(); + + IntSize elementSize; + if (videoElement()->shouldDisplayPosterImage()) + elementSize = m_cachedImageSize; + else + elementSize = intrinsicSize(); + IntRect contentRect = contentBoxRect(); - - if (intrinsicSize().isEmpty() || contentRect.isEmpty()) + if (elementSize.isEmpty() || contentRect.isEmpty()) return IntRect(); - IntRect resultRect = contentRect; - int ratio = contentRect.width() * intrinsicSize().height() - contentRect.height() * intrinsicSize().width(); + IntRect renderBox = contentRect; + int ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width(); if (ratio > 0) { - int newWidth = contentRect.height() * intrinsicSize().width() / intrinsicSize().height(); + int newWidth = renderBox.height() * elementSize.width() / elementSize.height(); // Just fill the whole area if the difference is one pixel or less (in both sides) - if (resultRect.width() - newWidth > 2) - resultRect.setWidth(newWidth); - resultRect.move((contentRect.width() - resultRect.width()) / 2, 0); + if (renderBox.width() - newWidth > 2) + renderBox.setWidth(newWidth); + renderBox.move((contentRect.width() - renderBox.width()) / 2, 0); } else if (ratio < 0) { - int newHeight = contentRect.width() * intrinsicSize().height() / intrinsicSize().width(); - if (resultRect.height() - newHeight > 2) - resultRect.setHeight(newHeight); - resultRect.move(0, (contentRect.height() - resultRect.height()) / 2); + int newHeight = renderBox.width() * elementSize.height() / elementSize.width(); + if (renderBox.height() - newHeight > 2) + renderBox.setHeight(newHeight); + renderBox.move(0, (contentRect.height() - renderBox.height()) / 2); } - return resultRect; + + return renderBox; } void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty) { MediaPlayer* mediaPlayer = player(); - if (!mediaPlayer) + bool displayingPoster = videoElement()->shouldDisplayPosterImage(); + + if (displayingPoster && document()->printing() && !view()->printImages()) return; - updatePlayer(); + + if (!displayingPoster) { + if (!mediaPlayer) + return; + updatePlayer(); + } + IntRect rect = videoBox(); if (rect.isEmpty()) return; rect.move(tx, ty); - mediaPlayer->paint(paintInfo.context, rect); + if (displayingPoster) + paintIntoRect(paintInfo.context, rect); + else + mediaPlayer->paint(paintInfo.context, rect); } void RenderVideo::layout() @@ -129,6 +176,12 @@ void RenderVideo::layout() updatePlayer(); } +HTMLVideoElement* RenderVideo::videoElement() const +{ + ASSERT(node()->hasTagName(videoTag)); + return static_cast<HTMLVideoElement*>(node()); +} + void RenderVideo::updateFromElement() { RenderMedia::updateFromElement(); @@ -140,7 +193,7 @@ void RenderVideo::updatePlayer() MediaPlayer* mediaPlayer = player(); if (!mediaPlayer) return; - if (!mediaElement()->inActiveDocument()) { + if (!videoElement()->inActiveDocument()) { mediaPlayer->setVisible(false); return; } @@ -155,40 +208,6 @@ void RenderVideo::updatePlayer() mediaPlayer->setVisible(true); } -bool RenderVideo::isWidthSpecified() const -{ - switch (style()->width().type()) { - case Fixed: - case Percent: - return true; - case Auto: - case Relative: // FIXME: Shouldn't this case return true? It doesn't for images. - case Static: - case Intrinsic: - case MinIntrinsic: - return false; - } - ASSERT(false); - return false; -} - -bool RenderVideo::isHeightSpecified() const -{ - switch (style()->height().type()) { - case Fixed: - case Percent: - return true; - case Auto: - case Relative: // FIXME: Shouldn't this case return true? It doesn't for images. - case Static: - case Intrinsic: - case MinIntrinsic: - return false; - } - ASSERT(false); - return false; -} - int RenderVideo::calcReplacedWidth(bool includeMaxWidth) const { int width; @@ -235,24 +254,9 @@ int RenderVideo::calcAspectRatioHeight() const return RenderBox::calcReplacedWidth() * intrinsicHeight / intrinsicWidth; } -void RenderVideo::calcPrefWidths() +int RenderVideo::minimumReplacedHeight() const { - ASSERT(prefWidthsDirty()); - - int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight(); - m_maxPrefWidth = calcReplacedWidth(false) + paddingAndBorders; - - if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) - m_maxPrefWidth = min(m_maxPrefWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0)); - - if (style()->width().isPercent() || style()->height().isPercent() || - style()->maxWidth().isPercent() || style()->maxHeight().isPercent() || - style()->minWidth().isPercent() || style()->minHeight().isPercent()) - m_minPrefWidth = 0; - else - m_minPrefWidth = m_maxPrefWidth; - - setPrefWidthsDirty(false); + return 0; } #if USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/rendering/RenderVideo.h b/WebCore/rendering/RenderVideo.h index 79e5b4e..3ca5328 100644 --- a/WebCore/rendering/RenderVideo.h +++ b/WebCore/rendering/RenderVideo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,13 +33,14 @@ namespace WebCore { class HTMLMediaElement; +class HTMLVideoElement; #if USE(ACCELERATED_COMPOSITING) class GraphicsLayer; #endif class RenderVideo : public RenderMedia { public: - RenderVideo(HTMLMediaElement*); + RenderVideo(HTMLVideoElement*); virtual ~RenderVideo(); void videoSizeChanged(); @@ -53,8 +54,10 @@ public: private: virtual void updateFromElement(); + inline HTMLVideoElement* videoElement() const; - virtual void intrinsicSizeChanged() { videoSizeChanged(); } + virtual void intrinsicSizeChanged(); + virtual void imageChanged(WrappedImagePtr, const IntRect*); virtual const char* renderName() const { return "RenderVideo"; } @@ -67,16 +70,14 @@ private: virtual int calcReplacedWidth(bool includeMaxWidth = true) const; virtual int calcReplacedHeight() const; - - virtual void calcPrefWidths(); + virtual int minimumReplacedHeight() const; int calcAspectRatioWidth() const; int calcAspectRatioHeight() const; - bool isWidthSpecified() const; - bool isHeightSpecified() const; - void updatePlayer(); + + IntSize m_cachedImageSize; }; inline RenderVideo* toRenderVideo(RenderObject* object) diff --git a/WebCore/rendering/RenderView.cpp b/WebCore/rendering/RenderView.cpp index 753afe4..502563e 100644 --- a/WebCore/rendering/RenderView.cpp +++ b/WebCore/rendering/RenderView.cpp @@ -664,6 +664,17 @@ void RenderView::pushLayoutState(RenderObject* root) m_layoutState = new (renderArena()) LayoutState(root); } +bool RenderView::shouldDisableLayoutStateForSubtree(RenderObject* renderer) const +{ + RenderObject* o = renderer; + while (o) { + if (o->hasColumns() || o->hasTransform() || o->hasReflection()) + return true; + o = o->container(); + } + return false; +} + void RenderView::updateHitTestResult(HitTestResult& result, const IntPoint& point) { if (result.innerNode()) diff --git a/WebCore/rendering/RenderView.h b/WebCore/rendering/RenderView.h index 37b3f01..c287579 100644 --- a/WebCore/rendering/RenderView.h +++ b/WebCore/rendering/RenderView.h @@ -140,6 +140,8 @@ public: state->destroy(renderArena()); } + bool shouldDisableLayoutStateForSubtree(RenderObject*) const; + // Returns true if layoutState should be used for its cached offset and clip. bool layoutStateEnabled() const { return m_layoutStateDisableCount == 0 && m_layoutState; } LayoutState* layoutState() const { return m_layoutState; } diff --git a/WebCore/rendering/RenderWidget.cpp b/WebCore/rendering/RenderWidget.cpp index 8b7e899..f6f6da8 100644 --- a/WebCore/rendering/RenderWidget.cpp +++ b/WebCore/rendering/RenderWidget.cpp @@ -252,18 +252,27 @@ void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) } if (m_widget) { - // Move the widget 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). - m_widget->move(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); - // Tell the widget to paint now. This is the only time the widget is allowed // to paint itself. That way it will composite properly with z-indexed layers. if (m_substituteImage) paintInfo.context->drawImage(m_substituteImage.get(), style()->colorSpace(), m_widget->frameRect()); - else - m_widget->paint(paintInfo.context, paintInfo.rect); + else { + IntPoint widgetLocation = m_widget->frameRect().location(); + IntPoint paintLocation(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); + IntRect paintRect = paintInfo.rect; + + IntSize paintOffset = paintLocation - widgetLocation; + // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, + // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. + if (!paintOffset.isZero()) { + paintInfo.context->translate(paintOffset); + paintRect.move(-paintOffset); + } + m_widget->paint(paintInfo.context, paintRect); + if (!paintOffset.isZero()) + paintInfo.context->translate(-paintOffset); + } if (m_widget->isFrameView() && paintInfo.overlapTestRequests && !static_cast<FrameView*>(m_widget.get())->useSlowRepaintsIfNotOverlapped()) { ASSERT(!paintInfo.overlapTestRequests->contains(this)); paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); diff --git a/WebCore/rendering/RootInlineBox.cpp b/WebCore/rendering/RootInlineBox.cpp index c8e072e..23316f7 100644 --- a/WebCore/rendering/RootInlineBox.cpp +++ b/WebCore/rendering/RootInlineBox.cpp @@ -21,6 +21,7 @@ #include "RootInlineBox.h" #include "BidiResolver.h" +#include "Chrome.h" #include "ChromeClient.h" #include "Document.h" #include "EllipsisBox.h" diff --git a/WebCore/rendering/SVGCharacterLayoutInfo.h b/WebCore/rendering/SVGCharacterLayoutInfo.h index b5b4f3e..fb29110 100644 --- a/WebCore/rendering/SVGCharacterLayoutInfo.h +++ b/WebCore/rendering/SVGCharacterLayoutInfo.h @@ -298,8 +298,10 @@ struct SVGTextChunkWalkerBase { virtual void start(InlineBox*) = 0; virtual void end(InlineBox*) = 0; + virtual bool setupBackground(InlineBox*) = 0; virtual bool setupFill(InlineBox*) = 0; virtual bool setupStroke(InlineBox*) = 0; + virtual bool setupForeground(InlineBox*) = 0; }; template<typename CallbackClass> @@ -315,21 +317,27 @@ public: typedef void (CallbackClass::*SVGTextChunkStartCallback)(InlineBox* box); typedef void (CallbackClass::*SVGTextChunkEndCallback)(InlineBox* box); + typedef bool (CallbackClass::*SVGTextChunkSetupBackgroundCallback)(InlineBox* box); typedef bool (CallbackClass::*SVGTextChunkSetupFillCallback)(InlineBox* box); typedef bool (CallbackClass::*SVGTextChunkSetupStrokeCallback)(InlineBox* box); + typedef bool (CallbackClass::*SVGTextChunkSetupForegroundCallback)(InlineBox* box); SVGTextChunkWalker(CallbackClass* object, SVGTextChunkWalkerCallback walker, SVGTextChunkStartCallback start = 0, SVGTextChunkEndCallback end = 0, + SVGTextChunkSetupBackgroundCallback background = 0, SVGTextChunkSetupFillCallback fill = 0, - SVGTextChunkSetupStrokeCallback stroke = 0) + SVGTextChunkSetupStrokeCallback stroke = 0, + SVGTextChunkSetupForegroundCallback foreground = 0) : m_object(object) , m_walkerCallback(walker) , m_startCallback(start) , m_endCallback(end) + , m_setupBackgroundCallback(background) , m_setupFillCallback(fill) , m_setupStrokeCallback(stroke) + , m_setupForegroundCallback(foreground) { ASSERT(object); ASSERT(walker); @@ -358,6 +366,15 @@ public: ASSERT_NOT_REACHED(); } + virtual bool setupBackground(InlineBox* box) + { + if (m_setupBackgroundCallback) + return (*m_object.*m_setupBackgroundCallback)(box); + + ASSERT_NOT_REACHED(); + return false; + } + virtual bool setupFill(InlineBox* box) { if (m_setupFillCallback) @@ -376,13 +393,24 @@ public: return false; } + virtual bool setupForeground(InlineBox* box) + { + if (m_setupForegroundCallback) + return (*m_object.*m_setupForegroundCallback)(box); + + ASSERT_NOT_REACHED(); + return false; + } + private: CallbackClass* m_object; SVGTextChunkWalkerCallback m_walkerCallback; SVGTextChunkStartCallback m_startCallback; SVGTextChunkEndCallback m_endCallback; + SVGTextChunkSetupBackgroundCallback m_setupBackgroundCallback; SVGTextChunkSetupFillCallback m_setupFillCallback; SVGTextChunkSetupStrokeCallback m_setupStrokeCallback; + SVGTextChunkSetupForegroundCallback m_setupForegroundCallback; }; struct SVGTextChunkLayoutInfo { diff --git a/WebCore/rendering/SVGInlineTextBox.cpp b/WebCore/rendering/SVGInlineTextBox.cpp index cf8464e..2f56e68 100644 --- a/WebCore/rendering/SVGInlineTextBox.cpp +++ b/WebCore/rendering/SVGInlineTextBox.cpp @@ -324,7 +324,7 @@ IntRect SVGInlineTextBox::selectionRect(int, int, int startPos, int endPos) return enclosingIntRect(walkerCallback.selectionRect()); } -void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int tx, int ty, const SVGChar& svgChar, const UChar* chars, int length, SVGPaintServer* activePaintServer) +void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int tx, int ty, const SVGChar& svgChar, const UChar* chars, int length, SVGTextPaintInfo& textPaintInfo) { if (renderer()->style()->visibility() != VISIBLE || paintInfo.phase == PaintPhaseOutline) return; @@ -356,7 +356,7 @@ void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int t // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection // and marked text. - if (paintInfo.phase != PaintPhaseSelection && !isPrinting) { + if (paintInfo.phase != PaintPhaseSelection && !isPrinting && textPaintInfo.subphase == SVGTextPaintSubphaseBackground) { #if PLATFORM(MAC) // Custom highlighters go behind everything else. if (styleToUse->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) @@ -376,29 +376,35 @@ void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int t } } - // Set a text shadow if we have one. - // FIXME: Support multiple shadow effects. Need more from the CG API before - // we can do this. - bool setShadow = false; - if (styleToUse->textShadow()) { - paintInfo.context->setShadow(IntSize(styleToUse->textShadow()->x, styleToUse->textShadow()->y), - styleToUse->textShadow()->blur, styleToUse->textShadow()->color, - styleToUse->colorSpace()); - setShadow = true; - } + if (textPaintInfo.subphase == SVGTextPaintSubphaseGlyphFill || textPaintInfo.subphase == SVGTextPaintSubphaseGlyphStroke) { + // Set a text shadow if we have one. + // FIXME: Support multiple shadow effects. Need more from the CG API before + // we can do this. + bool setShadow = false; + if (styleToUse->textShadow()) { + paintInfo.context->setShadow(IntSize(styleToUse->textShadow()->x, styleToUse->textShadow()->y), + styleToUse->textShadow()->blur, styleToUse->textShadow()->color, + styleToUse->colorSpace()); + setShadow = true; + } - IntPoint origin((int) svgChar.x, (int) svgChar.y); - TextRun run = svgTextRunForInlineTextBox(chars, length, styleToUse, this, svgChar.x); + IntPoint origin((int) svgChar.x, (int) svgChar.y); + TextRun run = svgTextRunForInlineTextBox(chars, length, styleToUse, this, svgChar.x); #if ENABLE(SVG_FONTS) - // SVG Fonts need access to the paint server used to draw the current text chunk. - // They need to be able to call renderPath() on a SVGPaintServer object. - run.setActivePaintServer(activePaintServer); + // SVG Fonts need access to the paint server used to draw the current text chunk. + // They need to be able to call renderPath() on a SVGPaintServer object. + ASSERT(textPaintInfo.activePaintServer); + run.setActivePaintServer(textPaintInfo.activePaintServer); #endif - paintInfo.context->drawText(font, run, origin); + paintInfo.context->drawText(font, run, origin); - if (paintInfo.phase != PaintPhaseSelection) { + if (setShadow) + paintInfo.context->clearShadow(); + } + + if (paintInfo.phase != PaintPhaseSelection && textPaintInfo.subphase == SVGTextPaintSubphaseForeground) { paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, false); if (useCustomUnderlines) { @@ -428,9 +434,6 @@ void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int t } - if (setShadow) - paintInfo.context->clearShadow(); - if (!ctm.isIdentity()) paintInfo.context->concatCTM(ctm.inverse()); } diff --git a/WebCore/rendering/SVGInlineTextBox.h b/WebCore/rendering/SVGInlineTextBox.h index ad39aab..eea6744 100644 --- a/WebCore/rendering/SVGInlineTextBox.h +++ b/WebCore/rendering/SVGInlineTextBox.h @@ -32,6 +32,20 @@ namespace WebCore { struct SVGChar; struct SVGTextDecorationInfo; + enum SVGTextPaintSubphase { + SVGTextPaintSubphaseBackground, + SVGTextPaintSubphaseGlyphFill, + SVGTextPaintSubphaseGlyphStroke, + SVGTextPaintSubphaseForeground + }; + + struct SVGTextPaintInfo { + SVGTextPaintInfo() : activePaintServer(0), subphase(SVGTextPaintSubphaseBackground) {} + + SVGPaintServer* activePaintServer; + SVGTextPaintSubphase subphase; + }; + class SVGInlineTextBox : public InlineTextBox { public: SVGInlineTextBox(RenderObject* obj); @@ -49,7 +63,7 @@ namespace WebCore { virtual IntRect selectionRect(int absx, int absy, int startPos, int endPos); // SVGs custom paint text method - void paintCharacters(RenderObject::PaintInfo&, int tx, int ty, const SVGChar&, const UChar* chars, int length, SVGPaintServer*); + void paintCharacters(RenderObject::PaintInfo&, int tx, int ty, const SVGChar&, const UChar* chars, int length, SVGTextPaintInfo&); // SVGs custom paint selection method void paintSelection(int boxStartOffset, const SVGChar&, const UChar*, int length, GraphicsContext*, RenderStyle*, const Font&); diff --git a/WebCore/rendering/SVGMarkerData.h b/WebCore/rendering/SVGMarkerData.h new file mode 100644 index 0000000..5ff2993 --- /dev/null +++ b/WebCore/rendering/SVGMarkerData.h @@ -0,0 +1,134 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SVGMarkerData_h +#define SVGMarkerData_h + +#if ENABLE(SVG) +#include "FloatConversion.h" +#include "Path.h" +#include <wtf/MathExtras.h> + +namespace WebCore { + +class SVGResourceMarker; + +class SVGMarkerData { +public: + enum Type { + Unknown = 0, + Start, + Mid, + End + }; + + SVGMarkerData(const Type& type = Unknown, SVGResourceMarker* marker = 0) + : m_type(type) + , m_marker(marker) + { + } + + FloatPoint origin() const { return m_origin; } + SVGResourceMarker* marker() const { return m_marker; } + + float currentAngle() const + { + FloatSize inslopeChange = m_inslopePoints[1] - m_inslopePoints[0]; + FloatSize outslopeChange = m_outslopePoints[1] - m_outslopePoints[0]; + + double inslope = rad2deg(atan2(inslopeChange.height(), inslopeChange.width())); + double outslope = rad2deg(atan2(outslopeChange.height(), outslopeChange.width())); + + double angle = 0; + switch (m_type) { + case Start: + angle = outslope; + break; + case Mid: + angle = (inslope + outslope) / 2; + break; + case End: + angle = inslope; + break; + default: + ASSERT_NOT_REACHED(); + break; + } + + return narrowPrecisionToFloat(angle); + } + + void updateTypeAndMarker(const Type& type, SVGResourceMarker* marker) + { + m_type = type; + m_marker = marker; + } + + void updateOutslope(const FloatPoint& point) + { + m_outslopePoints[0] = m_origin; + m_outslopePoints[1] = point; + } + + void updateMarkerDataForPathElement(const PathElement* element) + { + FloatPoint* points = element->points; + + switch (element->type) { + case PathElementAddQuadCurveToPoint: + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=33115 (PathElementAddQuadCurveToPoint not handled for <marker>) + m_origin = points[1]; + break; + case PathElementAddCurveToPoint: + m_inslopePoints[0] = points[1]; + m_inslopePoints[1] = points[2]; + m_origin = points[2]; + break; + case PathElementMoveToPoint: + m_subpathStart = points[0]; + case PathElementAddLineToPoint: + updateInslope(points[0]); + m_origin = points[0]; + break; + case PathElementCloseSubpath: + updateInslope(points[0]); + m_origin = m_subpathStart; + m_subpathStart = FloatPoint(); + } + } + +private: + void updateInslope(const FloatPoint& point) + { + m_inslopePoints[0] = m_origin; + m_inslopePoints[1] = point; + } + + Type m_type; + SVGResourceMarker* m_marker; + FloatPoint m_origin; + FloatPoint m_subpathStart; + FloatPoint m_inslopePoints[2]; + FloatPoint m_outslopePoints[2]; +}; + +} + +#endif // ENABLE(SVG) +#endif // SVGMarkerData_h diff --git a/WebCore/rendering/SVGMarkerLayoutInfo.cpp b/WebCore/rendering/SVGMarkerLayoutInfo.cpp new file mode 100644 index 0000000..3fe513f --- /dev/null +++ b/WebCore/rendering/SVGMarkerLayoutInfo.cpp @@ -0,0 +1,124 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2008 Rob Buis <buis@kde.org> + 2005, 2007 Eric Seidel <eric@webkit.org> + 2009 Google, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGMarkerLayoutInfo.h" + +#include "RenderSVGViewportContainer.h" +#include "SVGResourceMarker.h" + +namespace WebCore { + +SVGMarkerLayoutInfo::SVGMarkerLayoutInfo() + : m_midMarker(0) + , m_elementIndex(0) + , m_strokeWidth(0) +{ +} + +SVGMarkerLayoutInfo::~SVGMarkerLayoutInfo() +{ +} + +static inline void processStartAndMidMarkers(void* infoPtr, const PathElement* element) +{ + SVGMarkerLayoutInfo& info = *reinterpret_cast<SVGMarkerLayoutInfo*>(infoPtr); + SVGMarkerData& markerData = info.markerData(); + int& elementIndex = info.elementIndex(); + + // First update the outslope for the previous element + markerData.updateOutslope(element->points[0]); + + // Draw the marker for the previous element + SVGResourceMarker* marker = markerData.marker(); + if (elementIndex > 0 && marker) + info.addLayoutedMarker(marker, markerData.origin(), markerData.currentAngle()); + + // Update our marker data for this element + markerData.updateMarkerDataForPathElement(element); + + // After drawing the start marker, switch to drawing mid markers + if (elementIndex == 1) + markerData.updateTypeAndMarker(SVGMarkerData::Mid, info.midMarker()); + + ++elementIndex; +} + +FloatRect SVGMarkerLayoutInfo::calculateBoundaries(SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, SVGResourceMarker* endMarker, float strokeWidth, const Path& path) +{ + m_layout.clear(); + m_midMarker = midMarker; + m_strokeWidth = strokeWidth; + m_elementIndex = 0; + m_markerData = SVGMarkerData(SVGMarkerData::Start, startMarker); + path.apply(this, processStartAndMidMarkers); + + if (endMarker) { + m_markerData.updateTypeAndMarker(SVGMarkerData::End, endMarker); + addLayoutedMarker(endMarker, m_markerData.origin(), m_markerData.currentAngle()); + } + + if (m_layout.isEmpty()) + return FloatRect(); + + Vector<MarkerLayout>::iterator it = m_layout.begin(); + Vector<MarkerLayout>::iterator end = m_layout.end(); + + FloatRect bounds; + for (; it != end; ++it) { + MarkerLayout& layout = *it; + + RenderSVGViewportContainer* markerContent = layout.marker->renderer(); + ASSERT(markerContent); + + bounds.unite(markerContent->markerBoundaries(layout.matrix)); + } + + return bounds; +} + +void SVGMarkerLayoutInfo::drawMarkers(RenderObject::PaintInfo& paintInfo) +{ + if (m_layout.isEmpty()) + return; + + Vector<MarkerLayout>::iterator it = m_layout.begin(); + Vector<MarkerLayout>::iterator end = m_layout.end(); + + for (; it != end; ++it) { + MarkerLayout& layout = *it; + layout.marker->draw(paintInfo, layout.matrix); + } +} + +void SVGMarkerLayoutInfo::addLayoutedMarker(SVGResourceMarker* marker, const FloatPoint& origin, float angle) +{ + ASSERT(marker); + m_layout.append(MarkerLayout(marker, marker->markerTransformation(origin, angle, m_strokeWidth))); +} + +} + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGMarkerLayoutInfo.h b/WebCore/rendering/SVGMarkerLayoutInfo.h new file mode 100644 index 0000000..1dfeee9 --- /dev/null +++ b/WebCore/rendering/SVGMarkerLayoutInfo.h @@ -0,0 +1,74 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SVGMarkerLayoutInfo_h +#define SVGMarkerLayoutInfo_h + +#if ENABLE(SVG) +#include "RenderObject.h" +#include "SVGMarkerData.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class Path; +class SVGResourceMarker; + +struct MarkerLayout { + MarkerLayout(SVGResourceMarker* markerObj = 0, TransformationMatrix matrixObj = TransformationMatrix()) + : marker(markerObj) + , matrix(matrixObj) + { + ASSERT(marker); + } + + SVGResourceMarker* marker; + TransformationMatrix matrix; +}; + +class SVGMarkerLayoutInfo : public Noncopyable { +public: + SVGMarkerLayoutInfo(); + ~SVGMarkerLayoutInfo(); + + FloatRect calculateBoundaries(SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, SVGResourceMarker* endMarker, float strokeWidth, const Path&); + void drawMarkers(RenderObject::PaintInfo&); + + // Used by static inline helper functions in SVGMarkerLayoutInfo.cpp + SVGMarkerData& markerData() { return m_markerData; } + SVGResourceMarker* midMarker() const { return m_midMarker; } + int& elementIndex() { return m_elementIndex; } + void addLayoutedMarker(SVGResourceMarker*, const FloatPoint& origin, float angle); + +private: + SVGResourceMarker* m_midMarker; + + // Used while layouting markers + int m_elementIndex; + SVGMarkerData m_markerData; + float m_strokeWidth; + + // Holds the final computed result + Vector<MarkerLayout> m_layout; +}; + +} + +#endif // ENABLE(SVG) +#endif // SVGMarkerLayoutInfo_h diff --git a/WebCore/rendering/SVGRenderSupport.cpp b/WebCore/rendering/SVGRenderSupport.cpp index a594410..86cbf32 100644 --- a/WebCore/rendering/SVGRenderSupport.cpp +++ b/WebCore/rendering/SVGRenderSupport.cpp @@ -2,7 +2,8 @@ * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org> * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> * (C) 2007 Eric Seidel <eric@webkit.org> - * Copyright (C) 2009 Google, Inc. All rights reserved. + * (C) 2009 Google, Inc. All rights reserved. + * (C) 2009 Dirk Schulze <krit@webkit.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -41,6 +42,10 @@ namespace WebCore { +SVGRenderBase::~SVGRenderBase() +{ +} + IntRect SVGRenderBase::clippedOverflowRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer) { // Return early for any cases where we don't actually paint @@ -64,12 +69,12 @@ void SVGRenderBase::computeRectForRepaint(RenderObject* object, RenderBoxModelOb void SVGRenderBase::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) { ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree. - ASSERT(useTransforms); // mapping a point through SVG w/o respecting trasnforms is useless. + ASSERT(useTransforms); // Mapping a point through SVG w/o respecting transforms is useless. transformState.applyTransform(object->localToParentTransform()); object->parent()->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); } -void SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, const FloatRect& boundingBox, SVGResourceFilter*& filter, SVGResourceFilter* rootFilter) +bool SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, const FloatRect& repaintRect, SVGResourceFilter*& filter, SVGResourceFilter* rootFilter) { #if !ENABLE(FILTERS) UNUSED_PARAM(filter); @@ -90,18 +95,12 @@ void SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject // Setup transparency layers before setting up filters! float opacity = style->opacity(); if (opacity < 1.0f) { - paintInfo.context->clip(enclosingIntRect(boundingBox)); + paintInfo.context->clip(repaintRect); paintInfo.context->beginTransparencyLayer(opacity); } if (ShadowData* shadow = svgStyle->shadow()) { - int xShift = shadow->x < 0 ? shadow->x : 0; - int yShift = shadow->y < 0 ? shadow->y :0; - int widthShift = shadow->x < 0 ? 0 : shadow->x; - int heightShift = shadow->y < 0 ? 0 : shadow->y; - FloatRect shadowRect = FloatRect(boundingBox.x() + xShift, boundingBox.y() + yShift, - boundingBox.width() + widthShift, boundingBox.height() + heightShift); - paintInfo.context->clip(enclosingIntRect(shadowRect)); + paintInfo.context->clip(repaintRect); paintInfo.context->setShadow(IntSize(shadow->x, shadow->y), shadow->blur, shadow->color, style->colorSpace()); paintInfo.context->beginTransparencyLayer(1.0f); } @@ -116,7 +115,7 @@ void SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject Document* document = object->document(); #if ENABLE(FILTERS) - SVGResourceFilter* newFilter = getFilterById(document, filterId); + SVGResourceFilter* newFilter = getFilterById(document, filterId, object); if (newFilter == rootFilter) { // Catch <text filter="url(#foo)">Test<tspan filter="url(#foo)">123</tspan></text>. // The filter is NOT meant to be applied twice in that case! @@ -126,28 +125,32 @@ void SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject filter = newFilter; #endif - SVGResourceClipper* clipper = getClipperById(document, clipperId); - SVGResourceMasker* masker = getMaskerById(document, maskerId); + SVGResourceClipper* clipper = getClipperById(document, clipperId, object); + SVGResourceMasker* masker = getMaskerById(document, maskerId, object); + + if (masker) { + masker->addClient(styledElement); + if (!masker->applyMask(paintInfo.context, object)) + return false; + } else if (!maskerId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement); + + if (clipper) { + clipper->addClient(styledElement); + clipper->applyClip(paintInfo.context, object->objectBoundingBox()); + } else if (!clipperId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement); #if ENABLE(FILTERS) if (filter) { filter->addClient(styledElement); - filter->prepareFilter(paintInfo.context, object); + if (!filter->prepareFilter(paintInfo.context, object)) + return false; } else if (!filterId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(filterId, styledElement); #endif - if (clipper) { - clipper->addClient(styledElement); - clipper->applyClip(paintInfo.context, boundingBox); - } else if (!clipperId.isEmpty()) - svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement); - - if (masker) { - masker->addClient(styledElement); - masker->applyMask(paintInfo.context, boundingBox); - } else if (!maskerId.isEmpty()) - svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement); + return true; } void SVGRenderBase::finishRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, SVGResourceFilter*& filter, GraphicsContext* savedContext) @@ -232,18 +235,59 @@ FloatRect SVGRenderBase::computeContainerBoundingBox(const RenderObject* contain return boundingBox; } -FloatRect SVGRenderBase::filterBoundingBoxForRenderer(const RenderObject* object) +void SVGRenderBase::layoutChildren(RenderObject* start, bool selfNeedsLayout) +{ + for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { + // Only force our kids to layout if we're being asked to relayout as a result of a parent changing + // FIXME: We should be able to skip relayout of non-relative kids when only bounds size has changed + // that's a possible future optimization using LayoutState + // http://bugs.webkit.org/show_bug.cgi?id=15391 + bool needsLayout = selfNeedsLayout; + if (!needsLayout) { + if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) { + if (element->isStyled()) + needsLayout = static_cast<SVGStyledElement*>(element)->hasRelativeValues(); + } + } + + if (needsLayout) + child->setNeedsLayout(true, false); + + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + } +} + +FloatRect SVGRenderBase::filterBoundingBoxForRenderer(const RenderObject* object) const { #if ENABLE(FILTERS) - SVGResourceFilter* filter = getFilterById(object->document(), object->style()->svgStyle()->filter()); + SVGResourceFilter* filter = getFilterById(object->document(), object->style()->svgStyle()->filter(), object); if (filter) - return filter->filterBoundingBox(); + return filter->filterBoundingBox(object->objectBoundingBox()); #else UNUSED_PARAM(object); #endif return FloatRect(); } +FloatRect SVGRenderBase::clipperBoundingBoxForRenderer(const RenderObject* object) const +{ + SVGResourceClipper* clipper = getClipperById(object->document(), object->style()->svgStyle()->clipPath(), object); + if (clipper) + return clipper->clipperBoundingBox(object->objectBoundingBox()); + + return FloatRect(); +} + +FloatRect SVGRenderBase::maskerBoundingBoxForRenderer(const RenderObject* object) const +{ + SVGResourceMasker* masker = getMaskerById(object->document(), object->style()->svgStyle()->maskElement(), object); + if (masker) + return masker->maskerBoundingBox(object->objectBoundingBox()); + + return FloatRect(); +} + void applyTransformToPaintInfo(RenderObject::PaintInfo& paintInfo, const TransformationMatrix& localToAncestorTransform) { if (localToAncestorTransform.isIdentity()) diff --git a/WebCore/rendering/SVGRenderSupport.h b/WebCore/rendering/SVGRenderSupport.h index da2bf59..0804ede 100644 --- a/WebCore/rendering/SVGRenderSupport.h +++ b/WebCore/rendering/SVGRenderSupport.h @@ -38,12 +38,27 @@ namespace WebCore { // all SVG renderers inherit from RenderSVGModelObject. class SVGRenderBase { public: + virtual ~SVGRenderBase(); + + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } + // FIXME: These are only public for SVGRootInlineBox. // It's unclear if these should be exposed or not. SVGRootInlineBox may // pass the wrong RenderObject* and boundingBox to these functions. - static void prepareToRenderSVGContent(RenderObject*, RenderObject::PaintInfo&, const FloatRect& boundingBox, SVGResourceFilter*&, SVGResourceFilter* rootFilter = 0); + static bool prepareToRenderSVGContent(RenderObject*, RenderObject::PaintInfo&, const FloatRect& boundingBox, SVGResourceFilter*&, SVGResourceFilter* rootFilter = 0); static void finishRenderSVGContent(RenderObject*, RenderObject::PaintInfo&, SVGResourceFilter*&, GraphicsContext* savedContext); + // Layout all children of the passed render object + static void layoutChildren(RenderObject*, bool selfNeedsLayout); + + virtual FloatRect strokeBoundingBox() const { return FloatRect(); } + virtual FloatRect markerBoundingBox() const { return FloatRect(); } + + // returns the bounding box of filter, clipper, marker and masker (or the empty rect if no filter) in local coordinates + FloatRect filterBoundingBoxForRenderer(const RenderObject*) const; + FloatRect clipperBoundingBoxForRenderer(const RenderObject*) const; + FloatRect maskerBoundingBoxForRenderer(const RenderObject*) const; + protected: static IntRect clippedOverflowRectForRepaint(RenderObject*, RenderBoxModelObject* repaintContainer); static void computeRectForRepaint(RenderObject*, RenderBoxModelObject* repaintContainer, IntRect&, bool fixed); @@ -53,9 +68,6 @@ namespace WebCore { // Used to share the "walk all the children" logic between objectBoundingBox // and repaintRectInLocalCoordinates in RenderSVGRoot and RenderSVGContainer static FloatRect computeContainerBoundingBox(const RenderObject* container, bool includeAllPaintedContent); - - // returns the filter bounding box (or the empty rect if no filter) in local coordinates - static FloatRect filterBoundingBoxForRenderer(const RenderObject*); }; // FIXME: This should move to RenderObject or PaintInfo diff --git a/WebCore/rendering/SVGRenderTreeAsText.cpp b/WebCore/rendering/SVGRenderTreeAsText.cpp index 28e506a..bd6a465 100644 --- a/WebCore/rendering/SVGRenderTreeAsText.cpp +++ b/WebCore/rendering/SVGRenderTreeAsText.cpp @@ -521,11 +521,11 @@ void writeRenderResources(TextStream& ts, Node* parent) continue; SVGStyledElement* styled = static_cast<SVGStyledElement*>(svgElement); - RefPtr<SVGResource> resource(styled->canvasResource()); + RefPtr<SVGResource> resource(styled->canvasResource(node->renderer())); if (!resource) continue; - String elementId = svgElement->getAttribute(HTMLNames::idAttr); + String elementId = svgElement->getAttribute(svgElement->idAttributeName()); // FIXME: These names are lies! if (resource->isPaintServer()) { RefPtr<SVGPaintServer> paintServer = WTF::static_pointer_cast<SVGPaintServer>(resource); diff --git a/WebCore/rendering/SVGRootInlineBox.cpp b/WebCore/rendering/SVGRootInlineBox.cpp index 5829742..92e7654 100644 --- a/WebCore/rendering/SVGRootInlineBox.cpp +++ b/WebCore/rendering/SVGRootInlineBox.cpp @@ -429,6 +429,12 @@ struct SVGRootInlineBoxPaintWalker { m_paintInfo.rect = m_savedInfo.rect; } + bool chunkSetupBackgroundCallback(InlineBox* /*box*/) + { + m_textPaintInfo.subphase = SVGTextPaintSubphaseBackground; + return true; + } + bool chunkSetupFillCallback(InlineBox* box) { InlineFlowBox* flowBox = box->parent(); @@ -440,6 +446,7 @@ struct SVGRootInlineBoxPaintWalker { ASSERT(!m_strokePaintServer); teardownFillPaintServer(); + m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphFill; m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object); if (m_fillPaintServer) { m_fillPaintServer->setup(m_paintInfo.context, object, ApplyToFillTargetType, true); @@ -462,6 +469,7 @@ struct SVGRootInlineBoxPaintWalker { teardownFillPaintServer(); teardownStrokePaintServer(); + m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphStroke; m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object); if (m_strokePaintServer) { @@ -473,6 +481,32 @@ struct SVGRootInlineBoxPaintWalker { return false; } + bool chunkSetupForegroundCallback(InlineBox* /*box*/) + { + teardownFillPaintServer(); + teardownStrokePaintServer(); + + m_textPaintInfo.subphase = SVGTextPaintSubphaseForeground; + + return true; + } + + SVGPaintServer* activePaintServer() const + { + switch (m_textPaintInfo.subphase) { + case SVGTextPaintSubphaseGlyphFill: + ASSERT(m_fillPaintServer); + return m_fillPaintServer; + case SVGTextPaintSubphaseGlyphStroke: + ASSERT(m_strokePaintServer); + return m_strokePaintServer; + case SVGTextPaintSubphaseBackground: + case SVGTextPaintSubphaseForeground: + default: + return 0; + } + } + void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { @@ -523,12 +557,8 @@ struct SVGRootInlineBoxPaintWalker { textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); // Paint text - SVGPaintServer* activePaintServer = m_fillPaintServer; - if (!activePaintServer) - activePaintServer = m_strokePaintServer; - - ASSERT(activePaintServer); - textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer); + m_textPaintInfo.activePaintServer = activePaintServer(); + textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, m_textPaintInfo); // Paint decorations, that have to be drawn afterwards if (textDecorations & LINE_THROUGH && textWidth != 0.0f) @@ -561,6 +591,8 @@ private: int m_tx; int m_ty; + + SVGTextPaintInfo m_textPaintInfo; }; void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) @@ -575,18 +607,20 @@ void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) FloatRect boundingBox(tx + x(), ty + y(), width(), height()); // Initialize text rendering - SVGRenderBase::prepareToRenderSVGContent(renderer(), paintInfo, boundingBox, filter); - - // Render text, chunk-by-chunk - SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty); - SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback, - &SVGRootInlineBoxPaintWalker::chunkPortionCallback, - &SVGRootInlineBoxPaintWalker::chunkStartCallback, - &SVGRootInlineBoxPaintWalker::chunkEndCallback, - &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback, - &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback); - - walkTextChunks(&walker); + if (SVGRenderBase::prepareToRenderSVGContent(renderer(), paintInfo, boundingBox, filter)) { + // Render text, chunk-by-chunk + SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty); + SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback, + &SVGRootInlineBoxPaintWalker::chunkPortionCallback, + &SVGRootInlineBoxPaintWalker::chunkStartCallback, + &SVGRootInlineBoxPaintWalker::chunkEndCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupBackgroundCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupForegroundCallback); + + walkTextChunks(&walker); + } // Finalize text rendering SVGRenderBase::finishRenderSVGContent(renderer(), paintInfo, filter, savedInfo.context); @@ -1388,7 +1422,7 @@ void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* } else ASSERT(!info.chunk.boxes.isEmpty()); - // Walk string to find out new chunk positions, if existant + // Walk string to find out new chunk positions, if existent for (unsigned i = 0; i < length; ++i) { ASSERT(info.it != svgChars.end()); @@ -1680,11 +1714,18 @@ void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGI if (textBox) (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); else { + if (walker->setupBackground(range.box)) + (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + if (walker->setupFill(range.box)) (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); if (walker->setupStroke(range.box)) (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + + if (walker->setupForeground(range.box)) + (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + } chunkOffset += length; diff --git a/WebCore/rendering/SVGRootInlineBox.h b/WebCore/rendering/SVGRootInlineBox.h index 65bade0..d2dab98 100644 --- a/WebCore/rendering/SVGRootInlineBox.h +++ b/WebCore/rendering/SVGRootInlineBox.h @@ -28,6 +28,7 @@ #if ENABLE(SVG) #include "RootInlineBox.h" #include "SVGCharacterLayoutInfo.h" +#include "SVGRenderSupport.h" namespace WebCore { @@ -43,13 +44,14 @@ struct LastGlyphInfo { bool isValid; }; -class SVGRootInlineBox : public RootInlineBox { +class SVGRootInlineBox : public RootInlineBox, SVGRenderBase { public: SVGRootInlineBox(RenderObject* obj) : RootInlineBox(obj) , m_height(0) { } + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } virtual bool isSVGRootInlineBox() { return true; } @@ -63,6 +65,9 @@ public: virtual void computePerCharacterLayoutInformation(); + virtual FloatRect objectBoundingBox() const { return FloatRect(); } + virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); } + // Used by SVGInlineTextBox const Vector<SVGTextChunk>& svgTextChunks() const; diff --git a/WebCore/rendering/SVGShadowTreeElements.cpp b/WebCore/rendering/SVGShadowTreeElements.cpp new file mode 100644 index 0000000..d9ce640 --- /dev/null +++ b/WebCore/rendering/SVGShadowTreeElements.cpp @@ -0,0 +1,80 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGShadowTreeElements.h" + +#include "Document.h" +#include "FloatSize.h" +#include "RenderObject.h" +#include "SVGNames.h" + +namespace WebCore { + +// SVGShadowTreeContainerElement +SVGShadowTreeContainerElement::SVGShadowTreeContainerElement(Document* document) + : SVGGElement(SVGNames::gTag, document) +{ +} + +SVGShadowTreeContainerElement::~SVGShadowTreeContainerElement() +{ +} + +FloatSize SVGShadowTreeContainerElement::containerTranslation() const +{ + return FloatSize(m_xOffset.value(this), m_yOffset.value(this)); +} + +// SVGShadowTreeRootElement +SVGShadowTreeRootElement::SVGShadowTreeRootElement(Document* document, Node* shadowParent) + : SVGShadowTreeContainerElement(document) + , m_shadowParent(shadowParent) +{ + setInDocument(true); +} + +SVGShadowTreeRootElement::~SVGShadowTreeRootElement() +{ +} + +void SVGShadowTreeRootElement::attachElement(PassRefPtr<RenderStyle> style, RenderArena* arena) +{ + ASSERT(m_shadowParent); + + // Create the renderer with the specified style + RenderObject* renderer = createRenderer(arena, style.get()); + if (renderer) { + setRenderer(renderer); + renderer->setStyle(style); + } + + // Set these explicitly since this normally happens during an attach() + setAttached(); + + // Add the renderer to the render tree + if (renderer) + m_shadowParent->renderer()->addChild(renderer); +} + +} + +#endif diff --git a/WebCore/rendering/SVGShadowTreeElements.h b/WebCore/rendering/SVGShadowTreeElements.h new file mode 100644 index 0000000..ed42e89 --- /dev/null +++ b/WebCore/rendering/SVGShadowTreeElements.h @@ -0,0 +1,67 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SVGShadowTreeElements_h +#define SVGShadowTreeElements_h + +#if ENABLE(SVG) +#include "SVGGElement.h" +#include "SVGLength.h" + +namespace WebCore { + +class FloatSize; + +class SVGShadowTreeContainerElement : public SVGGElement { +public: + SVGShadowTreeContainerElement(Document*); + virtual ~SVGShadowTreeContainerElement(); + + virtual bool isShadowTreeContainerElement() const { return true; } + + FloatSize containerTranslation() const; + void setContainerOffset(const SVGLength& x, const SVGLength& y) + { + m_xOffset = x; + m_yOffset = y; + } + +private: + SVGLength m_xOffset; + SVGLength m_yOffset; +}; + +class SVGShadowTreeRootElement : public SVGShadowTreeContainerElement { +public: + SVGShadowTreeRootElement(Document*, Node* shadowParent); + virtual ~SVGShadowTreeRootElement(); + + virtual bool isShadowNode() const { return m_shadowParent; } + virtual Node* shadowParentNode() { return m_shadowParent; } + + void attachElement(PassRefPtr<RenderStyle>, RenderArena*); + +private: + Node* m_shadowParent; +}; + +} + +#endif +#endif diff --git a/WebCore/rendering/TrailingFloatsRootInlineBox.h b/WebCore/rendering/TrailingFloatsRootInlineBox.h new file mode 100644 index 0000000..68bf637 --- /dev/null +++ b/WebCore/rendering/TrailingFloatsRootInlineBox.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TrailingFloatsRootInlineBox_h +#define TrailingFloatsRootInlineBox_h + +#include "RootInlineBox.h" + +namespace WebCore { + +class TrailingFloatsRootInlineBox : public RootInlineBox { +public: + TrailingFloatsRootInlineBox(RenderObject* object) : RootInlineBox(object) + { +#if ENABLE(SVG) + setHasVirtualHeight(); +#endif + } + +private: + virtual int virtualHeight() const { return 0; } +}; + +} // namespace WebCore + +#endif // TrailingFloatsRootInlineBox_h diff --git a/WebCore/rendering/TransformState.cpp b/WebCore/rendering/TransformState.cpp index a9e68f4..700831b 100644 --- a/WebCore/rendering/TransformState.cpp +++ b/WebCore/rendering/TransformState.cpp @@ -115,7 +115,7 @@ void TransformState::flattenWithTransform(const TransformationMatrix& t) } // We could throw away m_accumulatedTransform if we wanted to here, but that - // would cause thrash when traversing hierarachies with alternating + // would cause thrash when traversing hierarchies with alternating // preserve-3d and flat elements. if (m_accumulatedTransform) m_accumulatedTransform->makeIdentity(); diff --git a/WebCore/rendering/break_lines.cpp b/WebCore/rendering/break_lines.cpp index be460c8..bbd86eb 100644 --- a/WebCore/rendering/break_lines.cpp +++ b/WebCore/rendering/break_lines.cpp @@ -1,21 +1,26 @@ /* - * Copyright (C) 2005, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2007, 2010 Apple Inc. All rights reserved. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -44,17 +49,37 @@ static inline bool isBreakableSpace(UChar ch, bool treatNoBreakSpaceAsBreak) } } -static inline bool shouldBreakAfter(UChar ch) +// This differs from the Unicode algorithm only in that Unicode does not break +// between a question mark and a vertical line (U+007C). +static const unsigned char internetExplorerLineBreaksAfterQuestionMarkTable[0x80] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, // \t + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, // ! " ' ) , . / + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, // : ; ? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, // ] + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1 // } +}; + +static const size_t internetExplorerLineBreaksAfterQuestionMarkTableSize = sizeof(internetExplorerLineBreaksAfterQuestionMarkTable) / sizeof(*internetExplorerLineBreaksAfterQuestionMarkTable); + +static inline bool shouldBreakAfter(UChar ch, UChar nextCh) { - // Match WinIE's breaking strategy, which is to always allow breaks after hyphens and question marks. - // FIXME: it appears that IE behavior is more complex, see <http://bugs.webkit.org/show_bug.cgi?id=17475>. switch (ch) { - case '-': + // For a question mark preceding a non-ASCII characters, defer to the Unicode algorithm by returning false. + // For ASCII characters, use a lookup table for enhanced speed and for compatibility with Internet Explorer. case '?': +<<<<<<< HEAD #ifdef ANDROID_LAYOUT // as '/' is used in uri which is always long, we would like to break it case '/': #endif +======= + return nextCh < internetExplorerLineBreaksAfterQuestionMarkTableSize && internetExplorerLineBreaksAfterQuestionMarkTable[nextCh]; + // Internet Explorer always allows breaking after a hyphen. + case '-': +>>>>>>> webkit.org at r54127 case softHyphen: // FIXME: cases for ideographicComma and ideographicFullStop are a workaround for an issue in Unicode 5.0 // which is likely to be resolved in Unicode 5.1 <http://bugs.webkit.org/show_bug.cgi?id=17411>. @@ -92,7 +117,7 @@ int nextBreakablePosition(const UChar* str, int pos, int len, bool treatNoBreakS for (int i = pos; i < len; i++) { UChar ch = str[i]; - if (isBreakableSpace(ch, treatNoBreakSpaceAsBreak) || shouldBreakAfter(lastCh)) + if (isBreakableSpace(ch, treatNoBreakSpaceAsBreak) || shouldBreakAfter(lastCh, ch)) return i; if (needsLineBreakIterator(ch) || needsLineBreakIterator(lastCh)) { diff --git a/WebCore/rendering/style/FillLayer.cpp b/WebCore/rendering/style/FillLayer.cpp index ec910c9..597e919 100644 --- a/WebCore/rendering/style/FillLayer.cpp +++ b/WebCore/rendering/style/FillLayer.cpp @@ -270,4 +270,15 @@ bool FillLayer::containsImage(StyleImage* s) const return false; } +bool FillLayer::imagesAreLoaded() const +{ + const FillLayer* curr; + for (curr = this; curr; curr = curr->next()) { + if (curr->m_image && !curr->m_image->isLoaded()) + return false; + } + + return true; +} + } // namespace WebCore diff --git a/WebCore/rendering/style/FillLayer.h b/WebCore/rendering/style/FillLayer.h index 9c615b4..cef6b19 100644 --- a/WebCore/rendering/style/FillLayer.h +++ b/WebCore/rendering/style/FillLayer.h @@ -126,6 +126,7 @@ public: } bool containsImage(StyleImage*) const; + bool imagesAreLoaded() const; bool hasImage() const { diff --git a/WebCore/rendering/style/RenderStyle.cpp b/WebCore/rendering/style/RenderStyle.cpp index 59d40b4..0952557 100644 --- a/WebCore/rendering/style/RenderStyle.cpp +++ b/WebCore/rendering/style/RenderStyle.cpp @@ -702,7 +702,7 @@ void RenderStyle::addBindingURI(StringImpl* uri) void RenderStyle::setTextShadow(ShadowData* val, bool add) { - ASSERT(!val || !val->spread && val->style == Normal); + ASSERT(!val || (!val->spread && val->style == Normal)); StyleRareInheritedData* rareData = rareInheritedData.access(); if (!add) { diff --git a/WebCore/rendering/style/RenderStyle.h b/WebCore/rendering/style/RenderStyle.h index a72b66d..c59d953 100644 --- a/WebCore/rendering/style/RenderStyle.h +++ b/WebCore/rendering/style/RenderStyle.h @@ -89,7 +89,11 @@ #include "BindingURI.h" #endif +#if COMPILER(WINSCW) +#define compareEqual(t, u) ((t) == (u)) +#else template<typename T, typename U> inline bool compareEqual(const T& t, const U& u) { return t == static_cast<T>(u); } +#endif #define SET_VAR(group, variable, value) \ if (!compareEqual(group->variable, value)) \ @@ -179,7 +183,7 @@ protected: unsigned _empty_cells : 1; // EEmptyCell unsigned _caption_side : 2; // ECaptionSide - unsigned _list_style_type : 5 ; // EListStyleType + unsigned _list_style_type : 6; // EListStyleType unsigned _list_style_position : 1; // EListStylePosition unsigned _visibility : 2; // EVisibility unsigned _text_align : 3; // ETextAlign @@ -190,37 +194,38 @@ protected: bool _border_collapse : 1 ; unsigned _white_space : 3; // EWhiteSpace unsigned _box_direction : 1; // EBoxDirection (CSS3 box_direction property, flexible box layout module) - // 32 bits + // 33 bits // non CSS2 inherited bool _visuallyOrdered : 1; bool _htmlHacks : 1; bool _force_backgrounds_to_white : 1; unsigned _pointerEvents : 4; // EPointerEvents - // 39 bits + // 40 bits } inherited_flags; // don't inherit struct NonInheritedFlags { bool operator==(const NonInheritedFlags& other) const { - return (_effectiveDisplay == other._effectiveDisplay) && - (_originalDisplay == other._originalDisplay) && - (_overflowX == other._overflowX) && - (_overflowY == other._overflowY) && - (_vertical_align == other._vertical_align) && - (_clear == other._clear) && - (_position == other._position) && - (_floating == other._floating) && - (_table_layout == other._table_layout) && - (_page_break_before == other._page_break_before) && - (_page_break_after == other._page_break_after) && - (_styleType == other._styleType) && - (_affectedByHover == other._affectedByHover) && - (_affectedByActive == other._affectedByActive) && - (_affectedByDrag == other._affectedByDrag) && - (_pseudoBits == other._pseudoBits) && - (_unicodeBidi == other._unicodeBidi); + return _effectiveDisplay == other._effectiveDisplay + && _originalDisplay == other._originalDisplay + && _overflowX == other._overflowX + && _overflowY == other._overflowY + && _vertical_align == other._vertical_align + && _clear == other._clear + && _position == other._position + && _floating == other._floating + && _table_layout == other._table_layout + && _page_break_before == other._page_break_before + && _page_break_after == other._page_break_after + && _page_break_inside == other._page_break_inside + && _styleType == other._styleType + && _affectedByHover == other._affectedByHover + && _affectedByActive == other._affectedByActive + && _affectedByDrag == other._affectedByDrag + && _pseudoBits == other._pseudoBits + && _unicodeBidi == other._unicodeBidi; } bool operator!=(const NonInheritedFlags& other) const { return !(*this == other); } @@ -237,6 +242,7 @@ protected: unsigned _page_break_before : 2; // EPageBreak unsigned _page_break_after : 2; // EPageBreak + unsigned _page_break_inside : 2; // EPageBreak unsigned _styleType : 5; // PseudoId bool _affectedByHover : 1; @@ -244,7 +250,7 @@ protected: bool _affectedByDrag : 1; unsigned _pseudoBits : 7; unsigned _unicodeBidi : 2; // EUnicodeBidi - // 48 bits + // 50 bits } noninherited_flags; // !END SYNC! @@ -280,6 +286,7 @@ protected: noninherited_flags._table_layout = initialTableLayout(); noninherited_flags._page_break_before = initialPageBreak(); noninherited_flags._page_break_after = initialPageBreak(); + noninherited_flags._page_break_inside = initialPageBreak(); noninherited_flags._styleType = NOPSEUDO; noninherited_flags._affectedByHover = false; noninherited_flags._affectedByActive = false; @@ -460,7 +467,7 @@ public: return font().lineSpacing(); if (lh.isPercent()) - return lh.calcMinValue(fontSize(), true); + return lh.calcMinValue(fontSize()); return lh.value(); } @@ -581,7 +588,7 @@ public: short widows() const { return inherited->widows; } short orphans() const { return inherited->orphans; } - EPageBreak pageBreakInside() const { return static_cast<EPageBreak>(inherited->page_break_inside); } + EPageBreak pageBreakInside() const { return static_cast<EPageBreak>(noninherited_flags._page_break_inside); } EPageBreak pageBreakBefore() const { return static_cast<EPageBreak>(noninherited_flags._page_break_before); } EPageBreak pageBreakAfter() const { return static_cast<EPageBreak>(noninherited_flags._page_break_after); } @@ -921,7 +928,7 @@ public: void setWidows(short w) { SET_VAR(inherited, widows, w); } void setOrphans(short o) { SET_VAR(inherited, orphans, o); } - void setPageBreakInside(EPageBreak b) { SET_VAR(inherited, page_break_inside, b); } + void setPageBreakInside(EPageBreak b) { noninherited_flags._page_break_inside = b; } void setPageBreakBefore(EPageBreak b) { noninherited_flags._page_break_before = b; } void setPageBreakAfter(EPageBreak b) { noninherited_flags._page_break_after = b; } @@ -1114,7 +1121,7 @@ public: static EEmptyCell initialEmptyCells() { return SHOW; } static EFloat initialFloating() { return FNONE; } static EListStylePosition initialListStylePosition() { return OUTSIDE; } - static EListStyleType initialListStyleType() { return DISC; } + static EListStyleType initialListStyleType() { return Disc; } static EOverflow initialOverflowX() { return OVISIBLE; } static EOverflow initialOverflowY() { return OVISIBLE; } static EPageBreak initialPageBreak() { return PBAUTO; } diff --git a/WebCore/rendering/style/RenderStyleConstants.h b/WebCore/rendering/style/RenderStyleConstants.h index 92cd3d5..d2c80ca 100644 --- a/WebCore/rendering/style/RenderStyleConstants.h +++ b/WebCore/rendering/style/RenderStyleConstants.h @@ -73,7 +73,7 @@ enum PseudoId { MEDIA_CONTROLS_SEEK_BACK_BUTTON, MEDIA_CONTROLS_SEEK_FORWARD_BUTTON, MEDIA_CONTROLS_FULLSCREEN_BUTTON, MEDIA_CONTROLS_REWIND_BUTTON, MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON, MEDIA_CONTROLS_STATUS_DISPLAY, SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER, RESIZER, - INPUT_LIST_BUTTON, + INPUT_LIST_BUTTON, INNER_SPIN_BUTTON, OUTER_SPIN_BUTTON, FIRST_INTERNAL_PSEUDOID = FILE_UPLOAD_BUTTON }; @@ -203,12 +203,63 @@ enum EResize { RESIZE_NONE, RESIZE_BOTH, RESIZE_HORIZONTAL, RESIZE_VERTICAL }; +// The order of this enum must match the order of the list style types in CSSValueKeywords.in. enum EListStyleType { - DISC, CIRCLE, SQUARE, LDECIMAL, DECIMAL_LEADING_ZERO, - LOWER_ROMAN, UPPER_ROMAN, LOWER_GREEK, - LOWER_ALPHA, LOWER_LATIN, UPPER_ALPHA, UPPER_LATIN, - HEBREW, ARMENIAN, GEORGIAN, CJK_IDEOGRAPHIC, - HIRAGANA, KATAKANA, HIRAGANA_IROHA, KATAKANA_IROHA, LNONE + Disc, + Circle, + Square, + DecimalListStyle, + DecimalLeadingZero, + LowerRoman, + UpperRoman, + LowerGreek, + LowerAlpha, + LowerLatin, + UpperAlpha, + UpperLatin, + Afar, + EthiopicHalehameAaEt, + EthiopicHalehameAaEr, + Amharic, + EthiopicHalehameAmEt, + AmharicAbegede, + EthiopicAbegedeAmEt, + CjkEarthlyBranch, + CjkHeavenlyStem, + Ethiopic, + EthiopicHalehameGez, + EthiopicAbegede, + EthiopicAbegedeGez, + HangulConsonant, + Hangul, + LowerNorwegian, + Oromo, + EthiopicHalehameOmEt, + Sidama, + EthiopicHalehameSidEt, + Somali, + EthiopicHalehameSoEt, + Tigre, + EthiopicHalehameTig, + TigrinyaEr, + EthiopicHalehameTiEr, + TigrinyaErAbegede, + EthiopicAbegedeTiEr, + TigrinyaEt, + EthiopicHalehameTiEt, + TigrinyaEtAbegede, + EthiopicAbegedeTiEt, + UpperGreek, + UpperNorwegian, + Hebrew, + Armenian, + Georgian, + CJKIdeographic, + Hiragana, + Katakana, + HiraganaIroha, + KatakanaIroha, + NoneListStyle }; enum StyleContentType { diff --git a/WebCore/rendering/style/SVGRenderStyle.cpp b/WebCore/rendering/style/SVGRenderStyle.cpp index 728738b..7958088 100644 --- a/WebCore/rendering/style/SVGRenderStyle.cpp +++ b/WebCore/rendering/style/SVGRenderStyle.cpp @@ -30,11 +30,14 @@ #include "CSSPrimitiveValue.h" #include "CSSValueList.h" +#include "IntRect.h" #include "NodeRenderStyle.h" #include "RenderObject.h" #include "RenderStyle.h" #include "SVGStyledElement.h" +using namespace std; + namespace WebCore { SVGRenderStyle::SVGRenderStyle() @@ -141,6 +144,56 @@ float SVGRenderStyle::cssPrimitiveToLength(const RenderObject* item, CSSValue* v return primitive->computeLengthFloat(const_cast<RenderStyle*>(item->style()), item->document()->documentElement()->renderStyle()); } + +static void getSVGShadowExtent(ShadowData* shadow, int& top, int& right, int& bottom, int& left) +{ + top = 0; + right = 0; + bottom = 0; + left = 0; + + int blurAndSpread = shadow->blur + shadow->spread; + + top = min(top, shadow->y - blurAndSpread); + right = max(right, shadow->x + blurAndSpread); + bottom = max(bottom, shadow->y + blurAndSpread); + left = min(left, shadow->x - blurAndSpread); +} + +void SVGRenderStyle::inflateForShadow(IntRect& repaintRect) const +{ + ShadowData* svgShadow = shadow(); + if (!svgShadow) + return; + + FloatRect repaintFloatRect = FloatRect(repaintRect); + inflateForShadow(repaintFloatRect); + repaintRect = enclosingIntRect(repaintFloatRect); +} + +void SVGRenderStyle::inflateForShadow(FloatRect& repaintRect) const +{ + ShadowData* svgShadow = shadow(); + if (!svgShadow) + return; + + int shadowTop; + int shadowRight; + int shadowBottom; + int shadowLeft; + getSVGShadowExtent(svgShadow, shadowTop, shadowRight, shadowBottom, shadowLeft); + + int overflowLeft = repaintRect.x() + shadowLeft; + int overflowRight = repaintRect.right() + shadowRight; + int overflowTop = repaintRect.y() + shadowTop; + int overflowBottom = repaintRect.bottom() + shadowBottom; + + repaintRect.setX(overflowLeft); + repaintRect.setY(overflowTop); + repaintRect.setWidth(overflowRight - overflowLeft); + repaintRect.setHeight(overflowBottom - overflowTop); +} + } #endif // ENABLE(SVG) diff --git a/WebCore/rendering/style/SVGRenderStyle.h b/WebCore/rendering/style/SVGRenderStyle.h index c65be97..c7f85db 100644 --- a/WebCore/rendering/style/SVGRenderStyle.h +++ b/WebCore/rendering/style/SVGRenderStyle.h @@ -34,176 +34,182 @@ namespace WebCore { - class RenderObject; - class RenderStyle; - - class SVGRenderStyle : public RefCounted<SVGRenderStyle> { - public: - static PassRefPtr<SVGRenderStyle> create() { return adoptRef(new SVGRenderStyle); } - PassRefPtr<SVGRenderStyle> copy() const { return adoptRef(new SVGRenderStyle(*this));} - ~SVGRenderStyle(); +class FloatRect; +class IntRect; +class RenderObject; +class RenderStyle; + +class SVGRenderStyle : public RefCounted<SVGRenderStyle> { +public: + static PassRefPtr<SVGRenderStyle> create() { return adoptRef(new SVGRenderStyle); } + PassRefPtr<SVGRenderStyle> copy() const { return adoptRef(new SVGRenderStyle(*this));} + ~SVGRenderStyle(); + + bool inheritedNotEqual(const SVGRenderStyle*) const; + void inheritFrom(const SVGRenderStyle*); + + // FIXME: These functions should move to ShadowData. + void inflateForShadow(IntRect&) const; + void inflateForShadow(FloatRect&) const; + + bool operator==(const SVGRenderStyle&) const; + bool operator!=(const SVGRenderStyle& o) const { return !(*this == o); } + + // SVG CSS Properties + SVG_RS_DEFINE_ATTRIBUTE(EAlignmentBaseline, AlignmentBaseline, alignmentBaseline, AB_AUTO) + SVG_RS_DEFINE_ATTRIBUTE(EDominantBaseline, DominantBaseline, dominantBaseline, DB_AUTO) + SVG_RS_DEFINE_ATTRIBUTE(EBaselineShift, BaselineShift, baselineShift, BS_BASELINE) + + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineCap, CapStyle, capStyle, ButtCap) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, ClipRule, clipRule, RULE_NONZERO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolation, colorInterpolation, CI_SRGB) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolationFilters, colorInterpolationFilters, CI_LINEARRGB) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorRendering, ColorRendering, colorRendering, CR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, FillRule, fillRule, RULE_NONZERO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EImageRendering, ImageRendering, imageRendering, IR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineJoin, JoinStyle, joinStyle, MiterJoin) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EShapeRendering, ShapeRendering, shapeRendering, SR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(ETextAnchor, TextAnchor, textAnchor, TA_START) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EWritingMode, WritingMode, writingMode, WM_LRTB) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationHorizontal, glyphOrientationHorizontal, GO_0DEG) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationVertical, glyphOrientationVertical, GO_AUTO) + + // SVG CSS Properties (using DataRef's) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, fill, opacity, FillOpacity, fillOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, fill, paint, FillPaint, fillPaint, SVGPaint::defaultFill()) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, opacity, StrokeOpacity, strokeOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, stroke, paint, StrokePaint, strokePaint, SVGPaint::defaultStroke()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValueList, stroke, dashArray, StrokeDashArray, strokeDashArray, 0) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, miterLimit, StrokeMiterLimit, strokeMiterLimit, 4.0f) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, width, StrokeWidth, strokeWidth, 0) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, dashOffset, StrokeDashOffset, strokeDashOffset, 0); + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, text, kerning, Kerning, kerning, 0) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stops, opacity, StopOpacity, stopOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, stops, color, StopColor, stopColor, Color(0, 0, 0)) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, clip, clipPath, ClipPath, clipPath, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, mask, maskElement, MaskElement, maskElement, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, startMarker, StartMarker, startMarker, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, midMarker, MidMarker, midMarker, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, endMarker, EndMarker, endMarker, String()) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, misc, filter, Filter, filter, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, misc, floodOpacity, FloodOpacity, floodOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, floodColor, FloodColor, floodColor, Color(0, 0, 0)) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, lightingColor, LightingColor, lightingColor, Color(255, 255, 255)) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, misc, baselineShiftValue, BaselineShiftValue, baselineShiftValue, 0) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_OWNPTR(ShadowData, shadowSVG, shadow, Shadow, shadow, 0) + + // convenience + bool hasStroke() const { return (strokePaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } + bool hasFill() const { return (fillPaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } + + static float cssPrimitiveToLength(const RenderObject*, CSSValue*, float defaultValue = 0.0f); + +protected: + // inherit + struct InheritedFlags { + bool operator==(const InheritedFlags& other) const + { + return (_colorRendering == other._colorRendering) + && (_imageRendering == other._imageRendering) + && (_shapeRendering == other._shapeRendering) + && (_clipRule == other._clipRule) + && (_fillRule == other._fillRule) + && (_capStyle == other._capStyle) + && (_joinStyle == other._joinStyle) + && (_textAnchor == other._textAnchor) + && (_colorInterpolation == other._colorInterpolation) + && (_colorInterpolationFilters == other._colorInterpolationFilters) + && (_writingMode == other._writingMode) + && (_glyphOrientationHorizontal == other._glyphOrientationHorizontal) + && (_glyphOrientationVertical == other._glyphOrientationVertical); + } - bool inheritedNotEqual(const SVGRenderStyle*) const; - void inheritFrom(const SVGRenderStyle*); - - bool operator==(const SVGRenderStyle&) const; - bool operator!=(const SVGRenderStyle& o) const { return !(*this == o); } - - // SVG CSS Properties - SVG_RS_DEFINE_ATTRIBUTE(EAlignmentBaseline, AlignmentBaseline, alignmentBaseline, AB_AUTO) - SVG_RS_DEFINE_ATTRIBUTE(EDominantBaseline, DominantBaseline, dominantBaseline, DB_AUTO) - SVG_RS_DEFINE_ATTRIBUTE(EBaselineShift, BaselineShift, baselineShift, BS_BASELINE) - - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineCap, CapStyle, capStyle, ButtCap) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, ClipRule, clipRule, RULE_NONZERO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolation, colorInterpolation, CI_SRGB) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolationFilters, colorInterpolationFilters, CI_LINEARRGB) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorRendering, ColorRendering, colorRendering, CR_AUTO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, FillRule, fillRule, RULE_NONZERO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EImageRendering, ImageRendering, imageRendering, IR_AUTO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineJoin, JoinStyle, joinStyle, MiterJoin) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EShapeRendering, ShapeRendering, shapeRendering, SR_AUTO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(ETextAnchor, TextAnchor, textAnchor, TA_START) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EWritingMode, WritingMode, writingMode, WM_LRTB) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationHorizontal, glyphOrientationHorizontal, GO_0DEG) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationVertical, glyphOrientationVertical, GO_AUTO) - - // SVG CSS Properties (using DataRef's) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, fill, opacity, FillOpacity, fillOpacity, 1.0f) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, fill, paint, FillPaint, fillPaint, SVGPaint::defaultFill()) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, opacity, StrokeOpacity, strokeOpacity, 1.0f) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, stroke, paint, StrokePaint, strokePaint, SVGPaint::defaultStroke()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValueList, stroke, dashArray, StrokeDashArray, strokeDashArray, 0) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, miterLimit, StrokeMiterLimit, strokeMiterLimit, 4.0f) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, width, StrokeWidth, strokeWidth, 0) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, dashOffset, StrokeDashOffset, strokeDashOffset, 0); - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, text, kerning, Kerning, kerning, 0) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stops, opacity, StopOpacity, stopOpacity, 1.0f) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, stops, color, StopColor, stopColor, Color(0, 0, 0)) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, clip, clipPath, ClipPath, clipPath, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, mask, maskElement, MaskElement, maskElement, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, startMarker, StartMarker, startMarker, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, midMarker, MidMarker, midMarker, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, endMarker, EndMarker, endMarker, String()) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, misc, filter, Filter, filter, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, misc, floodOpacity, FloodOpacity, floodOpacity, 1.0f) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, floodColor, FloodColor, floodColor, Color(0, 0, 0)) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, lightingColor, LightingColor, lightingColor, Color(255, 255, 255)) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, misc, baselineShiftValue, BaselineShiftValue, baselineShiftValue, 0) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_OWNPTR(ShadowData, shadowSVG, shadow, Shadow, shadow, 0) - - // convenience - bool hasStroke() const { return (strokePaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } - bool hasFill() const { return (fillPaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } - - static float cssPrimitiveToLength(const RenderObject*, CSSValue*, float defaultValue = 0.0f); - - protected: - // inherit - struct InheritedFlags { - bool operator==(const InheritedFlags& other) const - { - return (_colorRendering == other._colorRendering) && - (_imageRendering == other._imageRendering) && - (_shapeRendering == other._shapeRendering) && - (_clipRule == other._clipRule) && - (_fillRule == other._fillRule) && - (_capStyle == other._capStyle) && - (_joinStyle == other._joinStyle) && - (_textAnchor == other._textAnchor) && - (_colorInterpolation == other._colorInterpolation) && - (_colorInterpolationFilters == other._colorInterpolationFilters) && - (_writingMode == other._writingMode) && - (_glyphOrientationHorizontal == other._glyphOrientationHorizontal) && - (_glyphOrientationVertical == other._glyphOrientationVertical); - } - - bool operator!=(const InheritedFlags& other) const - { - return !(*this == other); - } - - unsigned _colorRendering : 2; // EColorRendering - unsigned _imageRendering : 2; // EImageRendering - unsigned _shapeRendering : 2; // EShapeRendering - unsigned _clipRule : 1; // WindRule - unsigned _fillRule : 1; // WindRule - unsigned _capStyle : 2; // LineCap - unsigned _joinStyle : 2; // LineJoin - unsigned _textAnchor : 2; // ETextAnchor - unsigned _colorInterpolation : 2; // EColorInterpolation - unsigned _colorInterpolationFilters : 2; // EColorInterpolation - unsigned _writingMode : 3; // EWritingMode - unsigned _glyphOrientationHorizontal : 3; // EGlyphOrientation - unsigned _glyphOrientationVertical : 3; // EGlyphOrientation - } svg_inherited_flags; - - // don't inherit - struct NonInheritedFlags { - // 32 bit non-inherited, don't add to the struct, or the operator will break. - bool operator==(const NonInheritedFlags &other) const { return _niflags == other._niflags; } - bool operator!=(const NonInheritedFlags &other) const { return _niflags != other._niflags; } - - union { - struct { - unsigned _alignmentBaseline : 4; // EAlignmentBaseline - unsigned _dominantBaseline : 4; // EDominantBaseline - unsigned _baselineShift : 2; // EBaselineShift - // 22 bits unused - } f; - uint32_t _niflags; - }; - } svg_noninherited_flags; - - // inherited attributes - DataRef<StyleFillData> fill; - DataRef<StyleStrokeData> stroke; - DataRef<StyleMarkerData> markers; - DataRef<StyleTextData> text; - - // non-inherited attributes - DataRef<StyleStopData> stops; - DataRef<StyleClipData> clip; - DataRef<StyleMaskData> mask; - DataRef<StyleMiscData> misc; - DataRef<StyleShadowSVGData> shadowSVG; - - private: - enum CreateDefaultType { CreateDefault }; - - SVGRenderStyle(); - SVGRenderStyle(const SVGRenderStyle&); - SVGRenderStyle(CreateDefaultType); // Used to create the default style. - - void setBitDefaults() + bool operator!=(const InheritedFlags& other) const { - svg_inherited_flags._clipRule = initialClipRule(); - svg_inherited_flags._colorRendering = initialColorRendering(); - svg_inherited_flags._fillRule = initialFillRule(); - svg_inherited_flags._imageRendering = initialImageRendering(); - svg_inherited_flags._shapeRendering = initialShapeRendering(); - svg_inherited_flags._textAnchor = initialTextAnchor(); - svg_inherited_flags._capStyle = initialCapStyle(); - svg_inherited_flags._joinStyle = initialJoinStyle(); - svg_inherited_flags._colorInterpolation = initialColorInterpolation(); - svg_inherited_flags._colorInterpolationFilters = initialColorInterpolationFilters(); - svg_inherited_flags._writingMode = initialWritingMode(); - svg_inherited_flags._glyphOrientationHorizontal = initialGlyphOrientationHorizontal(); - svg_inherited_flags._glyphOrientationVertical = initialGlyphOrientationVertical(); - - svg_noninherited_flags._niflags = 0; - svg_noninherited_flags.f._alignmentBaseline = initialAlignmentBaseline(); - svg_noninherited_flags.f._dominantBaseline = initialDominantBaseline(); - svg_noninherited_flags.f._baselineShift = initialBaselineShift(); + return !(*this == other); } - }; + + unsigned _colorRendering : 2; // EColorRendering + unsigned _imageRendering : 2; // EImageRendering + unsigned _shapeRendering : 2; // EShapeRendering + unsigned _clipRule : 1; // WindRule + unsigned _fillRule : 1; // WindRule + unsigned _capStyle : 2; // LineCap + unsigned _joinStyle : 2; // LineJoin + unsigned _textAnchor : 2; // ETextAnchor + unsigned _colorInterpolation : 2; // EColorInterpolation + unsigned _colorInterpolationFilters : 2; // EColorInterpolation + unsigned _writingMode : 3; // EWritingMode + unsigned _glyphOrientationHorizontal : 3; // EGlyphOrientation + unsigned _glyphOrientationVertical : 3; // EGlyphOrientation + } svg_inherited_flags; + + // don't inherit + struct NonInheritedFlags { + // 32 bit non-inherited, don't add to the struct, or the operator will break. + bool operator==(const NonInheritedFlags &other) const { return _niflags == other._niflags; } + bool operator!=(const NonInheritedFlags &other) const { return _niflags != other._niflags; } + + union { + struct { + unsigned _alignmentBaseline : 4; // EAlignmentBaseline + unsigned _dominantBaseline : 4; // EDominantBaseline + unsigned _baselineShift : 2; // EBaselineShift + // 22 bits unused + } f; + uint32_t _niflags; + }; + } svg_noninherited_flags; + + // inherited attributes + DataRef<StyleFillData> fill; + DataRef<StyleStrokeData> stroke; + DataRef<StyleMarkerData> markers; + DataRef<StyleTextData> text; + + // non-inherited attributes + DataRef<StyleStopData> stops; + DataRef<StyleClipData> clip; + DataRef<StyleMaskData> mask; + DataRef<StyleMiscData> misc; + DataRef<StyleShadowSVGData> shadowSVG; + +private: + enum CreateDefaultType { CreateDefault }; + + SVGRenderStyle(); + SVGRenderStyle(const SVGRenderStyle&); + SVGRenderStyle(CreateDefaultType); // Used to create the default style. + + void setBitDefaults() + { + svg_inherited_flags._clipRule = initialClipRule(); + svg_inherited_flags._colorRendering = initialColorRendering(); + svg_inherited_flags._fillRule = initialFillRule(); + svg_inherited_flags._imageRendering = initialImageRendering(); + svg_inherited_flags._shapeRendering = initialShapeRendering(); + svg_inherited_flags._textAnchor = initialTextAnchor(); + svg_inherited_flags._capStyle = initialCapStyle(); + svg_inherited_flags._joinStyle = initialJoinStyle(); + svg_inherited_flags._colorInterpolation = initialColorInterpolation(); + svg_inherited_flags._colorInterpolationFilters = initialColorInterpolationFilters(); + svg_inherited_flags._writingMode = initialWritingMode(); + svg_inherited_flags._glyphOrientationHorizontal = initialGlyphOrientationHorizontal(); + svg_inherited_flags._glyphOrientationVertical = initialGlyphOrientationVertical(); + + svg_noninherited_flags._niflags = 0; + svg_noninherited_flags.f._alignmentBaseline = initialAlignmentBaseline(); + svg_noninherited_flags.f._dominantBaseline = initialDominantBaseline(); + svg_noninherited_flags.f._baselineShift = initialBaselineShift(); + } +}; } // namespace WebCore diff --git a/WebCore/rendering/style/StyleInheritedData.cpp b/WebCore/rendering/style/StyleInheritedData.cpp index f59c0c2..c73497f 100644 --- a/WebCore/rendering/style/StyleInheritedData.cpp +++ b/WebCore/rendering/style/StyleInheritedData.cpp @@ -37,7 +37,6 @@ StyleInheritedData::StyleInheritedData() , vertical_border_spacing(RenderStyle::initialVerticalBorderSpacing()) , widows(RenderStyle::initialWidows()) , orphans(RenderStyle::initialOrphans()) - , page_break_inside(RenderStyle::initialPageBreak()) { } @@ -58,7 +57,6 @@ StyleInheritedData::StyleInheritedData(const StyleInheritedData& o) , vertical_border_spacing(o.vertical_border_spacing) , widows(o.widows) , orphans(o.orphans) - , page_break_inside(o.page_break_inside) { } @@ -84,8 +82,7 @@ bool StyleInheritedData::operator==(const StyleInheritedData& o) const horizontal_border_spacing == o.horizontal_border_spacing && vertical_border_spacing == o.vertical_border_spacing && widows == o.widows && - orphans == o.orphans && - page_break_inside == o.page_break_inside; + orphans == o.orphans; } } // namespace WebCore diff --git a/WebCore/rendering/style/StyleInheritedData.h b/WebCore/rendering/style/StyleInheritedData.h index 548ca72..3b30b8f 100644 --- a/WebCore/rendering/style/StyleInheritedData.h +++ b/WebCore/rendering/style/StyleInheritedData.h @@ -68,7 +68,6 @@ public: // Paged media properties. short widows; short orphans; - unsigned page_break_inside : 2; // EPageBreak private: StyleInheritedData(); |