diff options
Diffstat (limited to 'WebCore/rendering')
176 files changed, 12941 insertions, 8265 deletions
diff --git a/WebCore/rendering/EllipsisBox.cpp b/WebCore/rendering/EllipsisBox.cpp index fcce87d..db9a101 100644 --- a/WebCore/rendering/EllipsisBox.cpp +++ b/WebCore/rendering/EllipsisBox.cpp @@ -31,7 +31,7 @@ namespace WebCore { void EllipsisBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) { GraphicsContext* context = paintInfo.context; - RenderStyle* style = m_object->style(m_firstLine); + RenderStyle* style = m_renderer->style(m_firstLine); Color textColor = style->color(); if (textColor != context->fillColor()) context->setFillColor(textColor); @@ -43,15 +43,15 @@ void EllipsisBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) } 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 + m_baseline)); + 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())); if (setShadow) context->clearShadow(); if (m_markupBox) { // Paint the markup box - tx += m_x + m_width - m_markupBox->xPos(); - ty += m_y + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline()); + tx += m_x + m_width - m_markupBox->x(); + ty += m_y + style->font().ascent() - (m_markupBox->y() + m_markupBox->renderer()->style(m_firstLine)->font().ascent()); m_markupBox->paint(paintInfo, tx, ty); } } @@ -63,16 +63,17 @@ bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu // Hit test the markup box. if (m_markupBox) { - int mtx = tx + m_width - m_markupBox->xPos(); - int mty = ty + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline()); + RenderStyle* style = m_renderer->style(m_firstLine); + int mtx = tx + m_width - m_markupBox->x(); + int mty = ty + style->font().ascent() - (m_markupBox->y() + m_markupBox->renderer()->style(m_firstLine)->font().ascent()); if (m_markupBox->nodeAtPoint(request, result, x, y, mtx, mty)) { - object()->updateHitTestResult(result, IntPoint(x - mtx, y - mty)); + renderer()->updateHitTestResult(result, IntPoint(x - mtx, y - mty)); return true; } } if (visibleToHitTesting() && IntRect(tx, ty, m_width, m_height).contains(x, y)) { - object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } diff --git a/WebCore/rendering/EllipsisBox.h b/WebCore/rendering/EllipsisBox.h index dbb30cd..9dbd27f 100644 --- a/WebCore/rendering/EllipsisBox.h +++ b/WebCore/rendering/EllipsisBox.h @@ -26,15 +26,15 @@ namespace WebCore { +class HitTestRequest; class HitTestResult; -struct HitTestRequest; - class EllipsisBox : public InlineBox { public: EllipsisBox(RenderObject* obj, const AtomicString& ellipsisStr, InlineFlowBox* parent, - int width, int y, int height, int baseline, bool firstLine, InlineBox* markupBox) - : InlineBox(obj, 0, y, width, height, baseline, firstLine, true, false, false, 0, 0, parent) + int width, int height, int y, bool firstLine, InlineBox* markupBox) + : InlineBox(obj, 0, y, width, firstLine, true, false, false, 0, 0, parent) + , m_height(height) , m_str(ellipsisStr) , m_markupBox(markupBox) { @@ -44,6 +44,9 @@ public: virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); private: + virtual int height() const { return m_height; } + + int m_height; AtomicString m_str; InlineBox* m_markupBox; }; diff --git a/WebCore/rendering/HitTestRequest.h b/WebCore/rendering/HitTestRequest.h index 11dca4b..46dd7b8 100644 --- a/WebCore/rendering/HitTestRequest.h +++ b/WebCore/rendering/HitTestRequest.h @@ -2,6 +2,7 @@ * This file is part of the HTML rendering engine for KDE. * * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -19,24 +20,35 @@ * Boston, MA 02110-1301, USA. * */ + #ifndef HitTestRequest_h #define HitTestRequest_h namespace WebCore { -struct HitTestRequest { - HitTestRequest(bool r, bool a, bool m = false, bool u = false) - : readonly(r) - , active(a) - , mouseMove(m) - , mouseUp(u) - { +class HitTestRequest { +public: + enum RequestType { + ReadOnly = 0x1, + Active = 0x2, + MouseMove = 0x4, + MouseUp = 0x8, + IgnoreClipping = 0x10 + }; + + HitTestRequest(int requestType) + : m_requestType(requestType) + { } - bool readonly; - bool active; - bool mouseMove; - bool mouseUp; + bool readOnly() const { return m_requestType & ReadOnly; } + bool active() const { return m_requestType & Active; } + bool mouseMove() const { return m_requestType & MouseMove; } + bool mouseUp() const { return m_requestType & MouseUp; } + bool ignoreClipping() const { return m_requestType & IgnoreClipping; } + +private: + int m_requestType; }; } // namespace WebCore diff --git a/WebCore/rendering/InlineBox.cpp b/WebCore/rendering/InlineBox.cpp index 6f7324a..89cd4b8 100644 --- a/WebCore/rendering/InlineBox.cpp +++ b/WebCore/rendering/InlineBox.cpp @@ -80,19 +80,24 @@ void InlineBox::operator delete(void* ptr, size_t sz) #ifndef NDEBUG void InlineBox::showTreeForThis() const { - if (m_object) - m_object->showTreeForThis(); + if (m_renderer) + m_renderer->showTreeForThis(); } #endif +int InlineBox::height() const +{ + return toRenderBox(m_renderer)->height(); +} + int InlineBox::caretMinOffset() const { - return m_object->caretMinOffset(); + return m_renderer->caretMinOffset(); } int InlineBox::caretMaxOffset() const { - return m_object->caretMaxOffset(); + return m_renderer->caretMaxOffset(); } unsigned InlineBox::caretMaxRenderedOffset() const @@ -109,36 +114,38 @@ void InlineBox::dirtyLineBoxes() void InlineBox::deleteLine(RenderArena* arena) { - if (!m_extracted) - m_object->setInlineBoxWrapper(0); + if (!m_extracted && m_renderer->isBox()) + toRenderBox(m_renderer)->setInlineBoxWrapper(0); destroy(arena); } void InlineBox::extractLine() { m_extracted = true; - m_object->setInlineBoxWrapper(0); + if (m_renderer->isBox()) + toRenderBox(m_renderer)->setInlineBoxWrapper(0); } void InlineBox::attachLine() { m_extracted = false; - m_object->setInlineBoxWrapper(this); + if (m_renderer->isBox()) + toRenderBox(m_renderer)->setInlineBoxWrapper(this); } void InlineBox::adjustPosition(int dx, int dy) { m_x += dx; m_y += dy; - if (m_object->isReplaced()) { - RenderBox* box = toRenderBox(m_object); + if (m_renderer->isReplaced()) { + RenderBox* box = toRenderBox(m_renderer); box->move(dx, dy); } } void InlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) { - if (!object()->shouldPaintWithinRoot(paintInfo) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)) + if (!renderer()->shouldPaintWithinRoot(paintInfo) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)) return; // Paint all phases of replaced elements atomically, as though the replaced element established its @@ -147,16 +154,16 @@ void InlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip; RenderObject::PaintInfo info(paintInfo); info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; - object()->paint(info, tx, ty); + renderer()->paint(info, tx, ty); if (!preservePhase) { info.phase = PaintPhaseChildBlockBackgrounds; - object()->paint(info, tx, ty); + renderer()->paint(info, tx, ty); info.phase = PaintPhaseFloat; - object()->paint(info, tx, ty); + renderer()->paint(info, tx, ty); info.phase = PaintPhaseForeground; - object()->paint(info, tx, ty); + renderer()->paint(info, tx, ty); info.phase = PaintPhaseOutline; - object()->paint(info, tx, ty); + renderer()->paint(info, tx, ty); } } @@ -165,7 +172,15 @@ bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result // Hit test all phases of replaced elements atomically, as though the replaced element established its // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 // specification.) - return object()->hitTest(request, result, IntPoint(x, y), tx, ty); + return renderer()->hitTest(request, result, IntPoint(x, y), tx, ty); +} + +const RootInlineBox* InlineBox::root() const +{ + if (m_parent) + return m_parent->root(); + ASSERT(isRootInlineBox()); + return static_cast<const RootInlineBox*>(this); } RootInlineBox* InlineBox::root() @@ -228,13 +243,13 @@ InlineBox* InlineBox::prevLeafChild() RenderObject::SelectionState InlineBox::selectionState() { - return object()->selectionState(); + return renderer()->selectionState(); } bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) { // Non-replaced elements can always accommodate an ellipsis. - if (!m_object || !m_object->isReplaced()) + if (!m_renderer || !m_renderer->isReplaced()) return true; IntRect boxRect(m_x, 0, m_width, 10); diff --git a/WebCore/rendering/InlineBox.h b/WebCore/rendering/InlineBox.h index e17b6e8..e66574a 100644 --- a/WebCore/rendering/InlineBox.h +++ b/WebCore/rendering/InlineBox.h @@ -21,27 +21,25 @@ #ifndef InlineBox_h #define InlineBox_h -#include "RenderBox.h" +#include "RenderBoxModelObject.h" #include "TextDirection.h" namespace WebCore { +class HitTestRequest; class HitTestResult; class RootInlineBox; -struct HitTestRequest; // InlineBox represents a rectangle that occurs on a line. It corresponds to // some RenderObject (i.e., it represents a portion of that RenderObject). class InlineBox { public: InlineBox(RenderObject* obj) - : m_object(obj) + : m_renderer(obj) , m_x(0) , m_y(0) , m_width(0) - , m_height(0) - , m_baseline(0) , m_next(0) , m_prev(0) , m_parent(0) @@ -50,9 +48,6 @@ public: , m_bidiEmbeddingLevel(0) , m_dirty(false) , m_extracted(false) - , m_includeLeftEdge(false) - , m_includeRightEdge(false) - , m_hasTextChildren(true) , m_endsWithBreak(false) , m_hasSelectedChildren(false) , m_hasEllipsisBox(false) @@ -69,14 +64,12 @@ public: { } - InlineBox(RenderObject* obj, int x, int y, int width, int height, int baseline, bool firstLine, bool constructed, + InlineBox(RenderObject* obj, int x, int y, int width, bool firstLine, bool constructed, bool dirty, bool extracted, InlineBox* next, InlineBox* prev, InlineFlowBox* parent) - : m_object(obj) + : m_renderer(obj) , m_x(x) , m_y(y) , m_width(width) - , m_height(height) - , m_baseline(baseline) , m_next(next) , m_prev(prev) , m_parent(parent) @@ -85,9 +78,6 @@ public: , m_bidiEmbeddingLevel(0) , m_dirty(dirty) , m_extracted(extracted) - , m_includeLeftEdge(false) - , m_includeRightEdge(false) - , m_hasTextChildren(true) , m_endsWithBreak(false) , m_hasSelectedChildren(false) , m_hasEllipsisBox(false) @@ -134,10 +124,9 @@ public: void showTreeForThis() const; #endif virtual bool isInlineBox() { return false; } - virtual bool isInlineFlowBox() { return false; } - virtual bool isContainer() { return false; } + virtual bool isInlineFlowBox() const { return false; } virtual bool isInlineTextBox() { return false; } - virtual bool isRootInlineBox() { return false; } + virtual bool isRootInlineBox() const { return false; } #if ENABLE(SVG) virtual bool isSVGRootInlineBox() { return false; } #endif @@ -178,7 +167,7 @@ public: InlineBox* nextLeafChild(); InlineBox* prevLeafChild(); - RenderObject* object() const { return m_object; } + RenderObject* renderer() const { return m_renderer; } InlineFlowBox* parent() const { @@ -187,29 +176,24 @@ public: } void setParent(InlineFlowBox* par) { m_parent = par; } + const RootInlineBox* root() const; RootInlineBox* root(); - + void setWidth(int w) { m_width = w; } int width() const { return m_width; } - void setXPos(int x) { m_x = x; } - int xPos() const { return m_x; } - - void setYPos(int y) { m_y = y; } - int yPos() const { return m_y; } + void setX(int x) { m_x = x; } + int x() const { return m_x; } - void setHeight(int h) { m_height = h; } - int height() const { return m_height; } - - void setBaseline(int b) { m_baseline = b; } - int baseline() const { return m_baseline; } + void setY(int y) { m_y = y; } + int y() const { return m_y; } - bool hasTextChildren() const { return m_hasTextChildren; } + virtual int height() const; - virtual int topOverflow() { return yPos(); } - virtual int bottomOverflow() { return yPos() + height(); } - virtual int leftOverflow() { return xPos(); } - virtual int rightOverflow() { return xPos() + width(); } + virtual int topOverflow() const { return y(); } + virtual int bottomOverflow() const { return y() + height(); } + virtual int leftOverflow() const { return x(); } + virtual int rightOverflow() const { return x() + width(); } virtual int caretMinOffset() const; virtual int caretMaxOffset() const; @@ -237,19 +221,22 @@ public: int toAdd() const { return m_toAdd; } - bool visibleToHitTesting() const { return object()->style()->visibility() == VISIBLE && object()->style()->pointerEvents() != PE_NONE; } + bool visibleToHitTesting() const { return renderer()->style()->visibility() == VISIBLE && renderer()->style()->pointerEvents() != PE_NONE; } // Use with caution! The type is not checked! - RenderBox* renderBox() const { return toRenderBox(m_object); } + RenderBoxModelObject* boxModelObject() const + { + if (!m_renderer->isText()) + return static_cast<RenderBoxModelObject*>(m_renderer); + return 0; + } public: - RenderObject* m_object; + RenderObject* m_renderer; int m_x; int m_y; int m_width; - int m_height; - int m_baseline; private: InlineBox* m_next; // The next element on the same line as us. @@ -269,11 +256,6 @@ protected: bool m_dirty : 1; bool m_extracted : 1; - // for InlineFlowBox - bool m_includeLeftEdge : 1; - bool m_includeRightEdge : 1; - bool m_hasTextChildren : 1; - // for RootInlineBox bool m_endsWithBreak : 1; // Whether the line ends with a <br>. bool m_hasSelectedChildren : 1; // Whether we have any children selected (this bit will also be set if the <br> that terminates our line is selected). diff --git a/WebCore/rendering/InlineFlowBox.cpp b/WebCore/rendering/InlineFlowBox.cpp index 74f4151..2e23bb6 100644 --- a/WebCore/rendering/InlineFlowBox.cpp +++ b/WebCore/rendering/InlineFlowBox.cpp @@ -28,7 +28,7 @@ #include "HitTestResult.h" #include "RootInlineBox.h" #include "RenderBlock.h" -#include "RenderFlow.h" +#include "RenderInline.h" #include "RenderListMarker.h" #include "RenderTableCell.h" #include "RootInlineBox.h" @@ -51,45 +51,19 @@ InlineFlowBox::~InlineFlowBox() #endif -RenderFlow* InlineFlowBox::flowObject() +int InlineFlowBox::height() const { - return static_cast<RenderFlow*>(m_object); -} - -int InlineFlowBox::marginLeft() -{ - if (!includeLeftEdge()) - return 0; - - Length margin = object()->style()->marginLeft(); - if (margin.isAuto()) - return 0; - if (margin.isFixed()) - return margin.value(); - return renderBox()->marginLeft(); -} - -int InlineFlowBox::marginRight() -{ - if (!includeRightEdge()) - return 0; - - Length margin = object()->style()->marginRight(); - if (margin.isAuto()) - return 0; - if (margin.isFixed()) - return margin.value(); - return renderBox()->marginRight(); -} - -int InlineFlowBox::marginBorderPaddingLeft() -{ - return marginLeft() + borderLeft() + paddingLeft(); -} - -int InlineFlowBox::marginBorderPaddingRight() -{ - return marginRight() + borderRight() + paddingRight(); + const Font& font = renderer()->style(m_firstLine)->font(); + int result = font.height(); + bool strictMode = renderer()->document()->inStrictMode(); + RenderBoxModelObject* box = boxModelObject(); + result += box->borderTop() + box->paddingTop() + box->borderBottom() + box->paddingBottom(); + if (!strictMode && !hasTextChildren() && !box->hasHorizontalBordersOrPadding()) { + int bottomOverflow = root()->bottomOverflow(); + if (y() + result > bottomOverflow) + result = bottomOverflow - y(); + } + return result; } int InlineFlowBox::getFlowSpacingWidth() @@ -121,7 +95,7 @@ void InlineFlowBox::addToLine(InlineBox* child) child->setFirstLineStyleBit(m_firstLine); if (child->isText()) m_hasTextChildren = true; - if (child->object()->selectionState() != RenderObject::SelectionNone) + if (child->renderer()->selectionState() != RenderObject::SelectionNone) root()->setHasSelectedChildren(true); checkConsistency(); @@ -168,26 +142,41 @@ void InlineFlowBox::deleteLine(RenderArena* arena) m_lastChild = 0; #endif - static_cast<RenderFlow*>(m_object)->removeLineBox(this); + removeLineBoxFromRenderObject(); destroy(arena); } +void InlineFlowBox::removeLineBoxFromRenderObject() +{ + toRenderInline(renderer())->lineBoxes()->removeLineBox(this); +} + void InlineFlowBox::extractLine() { if (!m_extracted) - static_cast<RenderFlow*>(m_object)->extractLineBox(this); + extractLineBoxFromRenderObject(); for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) child->extractLine(); } +void InlineFlowBox::extractLineBoxFromRenderObject() +{ + toRenderInline(renderer())->lineBoxes()->extractLineBox(this); +} + void InlineFlowBox::attachLine() { if (m_extracted) - static_cast<RenderFlow*>(m_object)->attachLineBox(this); + attachLineBoxToRenderObject(); for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) child->attachLine(); } +void InlineFlowBox::attachLineBoxToRenderObject() +{ + toRenderInline(renderer())->lineBoxes()->attachLineBox(this); +} + void InlineFlowBox::adjustPosition(int dx, int dy) { InlineRunBox::adjustPosition(dx, dy); @@ -195,18 +184,23 @@ void InlineFlowBox::adjustPosition(int dx, int dy) child->adjustPosition(dx, dy); } +RenderLineBoxList* InlineFlowBox::rendererLineBoxes() const +{ + return toRenderInline(renderer())->lineBoxes(); +} + bool InlineFlowBox::onEndChain(RenderObject* endObject) { if (!endObject) return false; - if (endObject == object()) + if (endObject == renderer()) return true; RenderObject* curr = endObject; RenderObject* parent = curr->parent(); while (parent && !parent->isRenderBlock()) { - if (parent->lastChild() != curr || parent == object()) + if (parent->lastChild() != curr || parent == renderer()) return false; curr = parent; @@ -223,19 +217,17 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* en bool includeLeftEdge = false; bool includeRightEdge = false; - RenderFlow* flow = static_cast<RenderFlow*>(object()); - - if (!flow->firstChild()) - includeLeftEdge = includeRightEdge = true; // Empty inlines never split across lines. - else if (parent()) { // The root inline box never has borders/margins/padding. - bool ltr = flow->style()->direction() == LTR; - + // The root inline box never has borders/margins/padding. + if (parent()) { + bool ltr = renderer()->style()->direction() == LTR; + // Check to see if all initial lines are unconstructed. If so, then - // we know the inline began on this line. - if (!flow->firstLineBox()->isConstructed()) { - if (ltr && flow->firstLineBox() == this) + // we know the inline began on this line (unless we are a continuation). + RenderLineBoxList* lineBoxList = rendererLineBoxes(); + if (!lineBoxList->firstLineBox()->isConstructed() && !renderer()->isInlineContinuation()) { + if (ltr && lineBoxList->firstLineBox() == this) includeLeftEdge = true; - else if (!ltr && flow->lastLineBox() == this) + else if (!ltr && lineBoxList->lastLineBox() == this) includeRightEdge = true; } @@ -246,14 +238,15 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* en // reverse for rtl), then the inline has closed. // (3) The line may end on the inline. If we are the last child (climbing up // the end object's chain), then we just closed as well. - if (!flow->lastLineBox()->isConstructed()) { + if (!lineBoxList->lastLineBox()->isConstructed()) { + RenderInline* inlineFlow = toRenderInline(renderer()); if (ltr) { if (!nextLineBox() && - ((lastLine && !flow->continuation()) || nextOnLineExists() || onEndChain(endObject))) + ((lastLine && !inlineFlow->continuation()) || nextOnLineExists() || onEndChain(endObject))) includeRightEdge = true; } else { if ((!prevLineBox() || prevLineBox()->isConstructed()) && - ((lastLine && !flow->continuation()) || prevOnLineExists() || onEndChain(endObject))) + ((lastLine && !inlineFlow->continuation()) || prevOnLineExists() || onEndChain(endObject))) includeLeftEdge = true; } } @@ -270,32 +263,32 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* en } } -int InlineFlowBox::placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing) +int InlineFlowBox::placeBoxesHorizontally(int xPos, int& leftPosition, int& rightPosition, bool& needsWordSpacing) { // Set our x position. - setXPos(x); + setX(xPos); int boxShadowLeft = 0; int boxShadowRight = 0; - for (ShadowData* boxShadow = object()->style(m_firstLine)->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + for (ShadowData* boxShadow = renderer()->style(m_firstLine)->boxShadow(); boxShadow; boxShadow = boxShadow->next) { boxShadowLeft = min(boxShadow->x - boxShadow->blur, boxShadowLeft); boxShadowRight = max(boxShadow->x + boxShadow->blur, boxShadowRight); } - leftPosition = min(x + boxShadowLeft, leftPosition); + leftPosition = min(xPos + boxShadowLeft, leftPosition); - int startX = x; - x += borderLeft() + paddingLeft(); + int startX = xPos; + xPos += borderLeft() + paddingLeft(); for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->object()->isText()) { + if (curr->renderer()->isText()) { InlineTextBox* text = static_cast<InlineTextBox*>(curr); - RenderText* rt = toRenderText(text->object()); + RenderText* rt = toRenderText(text->renderer()); if (rt->textLength()) { if (needsWordSpacing && isSpaceOrNewline(rt->characters()[text->start()])) - x += rt->style(m_firstLine)->font().wordSpacing(); + xPos += rt->style(m_firstLine)->font().wordSpacing(); needsWordSpacing = !isSpaceOrNewline(rt->characters()[text->end()]); } - text->setXPos(x); + text->setX(xPos); int strokeOverflow = static_cast<int>(ceilf(rt->style()->textStrokeWidth() / 2.0f)); @@ -313,41 +306,41 @@ int InlineFlowBox::placeBoxesHorizontally(int x, int& leftPosition, int& rightPo visualOverflowRight = max(visualOverflowRight, shadow->x + shadow->blur + rightGlyphOverflow); } - leftPosition = min(x + visualOverflowLeft, leftPosition); - rightPosition = max(x + text->width() + visualOverflowRight, rightPosition); - m_maxHorizontalVisualOverflow = max(max(visualOverflowRight, -visualOverflowLeft), m_maxHorizontalVisualOverflow); - x += text->width(); + leftPosition = min(xPos + visualOverflowLeft, leftPosition); + rightPosition = max(xPos + text->width() + visualOverflowRight, rightPosition); + m_maxHorizontalVisualOverflow = max(max(visualOverflowRight, -visualOverflowLeft), (int)m_maxHorizontalVisualOverflow); + xPos += text->width(); } else { - if (curr->object()->isPositioned()) { - if (curr->object()->parent()->style()->direction() == LTR) - curr->setXPos(x); + if (curr->renderer()->isPositioned()) { + if (curr->renderer()->parent()->style()->direction() == LTR) + curr->setX(xPos); else // Our offset that we cache needs to be from the edge of the right border box and // not the left border box. We have to subtract |x| from the width of the block // (which can be obtained from the root line box). - curr->setXPos(root()->block()->width()-x); + curr->setX(root()->block()->width() - xPos); continue; // The positioned object has no effect on the width. } - if (curr->object()->isRenderInline()) { + if (curr->renderer()->isRenderInline()) { InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr); - x += flow->marginLeft(); - x = flow->placeBoxesHorizontally(x, leftPosition, rightPosition, needsWordSpacing); - x += flow->marginRight(); - } else if (!curr->object()->isListMarker() || static_cast<RenderListMarker*>(curr->object())->isInside()) { - x += curr->renderBox()->marginLeft(); - curr->setXPos(x); - leftPosition = min(x + curr->renderBox()->overflowLeft(false), leftPosition); - rightPosition = max(x + curr->renderBox()->overflowWidth(false), rightPosition); - x += curr->width() + curr->renderBox()->marginRight(); + xPos += flow->marginLeft(); + xPos = flow->placeBoxesHorizontally(xPos, leftPosition, rightPosition, needsWordSpacing); + xPos += flow->marginRight(); + } else if (!curr->renderer()->isListMarker() || static_cast<RenderListMarker*>(curr->renderer())->isInside()) { + xPos += curr->boxModelObject()->marginLeft(); + curr->setX(xPos); + leftPosition = min(xPos + toRenderBox(curr->renderer())->overflowLeft(false), leftPosition); + rightPosition = max(xPos + toRenderBox(curr->renderer())->overflowWidth(false), rightPosition); + xPos += curr->width() + curr->boxModelObject()->marginRight(); } } } - x += borderRight() + paddingRight(); - setWidth(x - startX); - rightPosition = max(xPos() + width() + boxShadowRight, rightPosition); + xPos += borderRight() + paddingRight(); + setWidth(xPos - startX); + rightPosition = max(x() + width() + boxShadowRight, rightPosition); - return x; + return xPos; } int InlineFlowBox::verticallyAlignBoxes(int heightOfBlock) @@ -359,8 +352,8 @@ int InlineFlowBox::verticallyAlignBoxes(int heightOfBlock) // Figure out if we're in strict mode. Note that we can't simply use !style()->htmlHacks(), // because that would match almost strict mode as well. - RenderObject* curr = object(); - while (curr && !curr->element()) + RenderObject* curr = renderer(); + while (curr && !curr->node()) curr = curr->container(); bool strictMode = (curr && curr->document()->inStrictMode()); @@ -378,10 +371,6 @@ int InlineFlowBox::verticallyAlignBoxes(int heightOfBlock) setVerticalOverflowPositions(topPosition, bottomPosition); setVerticalSelectionPositions(selectionTop, selectionBottom); - - // Shrink boxes with no text children in quirks and almost strict mode. - if (!strictMode) - shrinkBoxesWithNoTextChildren(topPosition, bottomPosition); heightOfBlock += maxHeight; @@ -394,16 +383,17 @@ void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { // The computed lineheight needs to be extended for the // positioned elements - if (curr->object()->isPositioned()) + if (curr->renderer()->isPositioned()) continue; // Positioned placeholders don't affect calculations. - if (curr->yPos() == PositionTop || curr->yPos() == PositionBottom) { - if (curr->yPos() == PositionTop) { - if (maxAscent + maxDescent < curr->height()) - maxDescent = curr->height() - maxAscent; + if (curr->y() == PositionTop || curr->y() == PositionBottom) { + int lineHeight = curr->renderer()->lineHeight(m_firstLine); + if (curr->y() == PositionTop) { + if (maxAscent + maxDescent < lineHeight) + maxDescent = lineHeight - maxAscent; } else { - if (maxAscent + maxDescent < curr->height()) - maxAscent = curr->height() - maxDescent; + if (maxAscent + maxDescent < lineHeight) + maxAscent = lineHeight - maxDescent; } if (maxAscent + maxDescent >= max(maxPositionTop, maxPositionBottom)) @@ -415,22 +405,25 @@ void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, } } +static int verticalPositionForBox(InlineBox* curr, bool firstLine) +{ + if (curr->renderer()->isText()) + return curr->parent()->y(); + if (curr->renderer()->isBox()) + return toRenderBox(curr->renderer())->verticalPosition(firstLine); + return toRenderInline(curr->renderer())->verticalPositionFromCache(firstLine); +} + void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, int& maxAscent, int& maxDescent, bool strictMode) { if (isRootInlineBox()) { // Examine our root box. - setHeight(object()->lineHeight(m_firstLine, true)); - bool isTableCell = object()->isTableCell(); - if (isTableCell) { - RenderTableCell* tableCell = static_cast<RenderTableCell*>(object()); - setBaseline(tableCell->RenderBlock::baselinePosition(m_firstLine, true)); - } - else - setBaseline(object()->baselinePosition(m_firstLine, true)); + int lineHeight = renderer()->lineHeight(m_firstLine, true); + int baseline = renderer()->baselinePosition(m_firstLine, true); if (hasTextChildren() || strictMode) { - int ascent = baseline(); - int descent = height() - ascent; + int ascent = baseline; + int descent = lineHeight - ascent; if (maxAscent < ascent) maxAscent = ascent; if (maxDescent < descent) @@ -439,23 +432,25 @@ void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositi } for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->object()->isPositioned()) + if (curr->renderer()->isPositioned()) continue; // Positioned placeholders don't affect calculations. - curr->setHeight(curr->object()->lineHeight(m_firstLine)); - curr->setBaseline(curr->object()->baselinePosition(m_firstLine)); - curr->setYPos(curr->object()->verticalPositionHint(m_firstLine)); - if (curr->yPos() == PositionTop) { - if (maxPositionTop < curr->height()) - maxPositionTop = curr->height(); + bool isInlineFlow = curr->isInlineFlowBox(); + + int lineHeight = curr->renderer()->lineHeight(m_firstLine); + int baseline = curr->renderer()->baselinePosition(m_firstLine); + curr->setY(verticalPositionForBox(curr, m_firstLine)); + if (curr->y() == PositionTop) { + if (maxPositionTop < lineHeight) + maxPositionTop = lineHeight; } - else if (curr->yPos() == PositionBottom) { - if (maxPositionBottom < curr->height()) - maxPositionBottom = curr->height(); + else if (curr->y() == PositionBottom) { + if (maxPositionBottom < lineHeight) + maxPositionBottom = lineHeight; } - else if (curr->hasTextChildren() || curr->renderBox()->hasHorizontalBordersOrPadding() || strictMode) { - int ascent = curr->baseline() - curr->yPos(); - int descent = curr->height() - ascent; + else if ((!isInlineFlow || static_cast<InlineFlowBox*>(curr)->hasTextChildren()) || curr->boxModelObject()->hasHorizontalBordersOrPadding() || strictMode) { + int ascent = baseline - curr->y(); + int descent = lineHeight - ascent; if (maxAscent < ascent) maxAscent = ascent; if (maxDescent < descent) @@ -467,135 +462,106 @@ void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositi } } -void InlineFlowBox::placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode, +void InlineFlowBox::placeBoxesVertically(int yPos, int maxHeight, int maxAscent, bool strictMode, int& topPosition, int& bottomPosition, int& selectionTop, int& selectionBottom) { if (isRootInlineBox()) - setYPos(y + maxAscent - baseline());// Place our root box. + setY(yPos + max(0, maxAscent - renderer()->baselinePosition(m_firstLine, true))); // Place our root box. for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->object()->isPositioned()) + if (curr->renderer()->isPositioned()) continue; // Positioned placeholders don't affect calculations. // Adjust boxes to use their real box y/height and not the logical height (as dictated by // line-height). - if (curr->isInlineFlowBox()) - static_cast<InlineFlowBox*>(curr)->placeBoxesVertically(y, maxHeight, maxAscent, strictMode, topPosition, bottomPosition, selectionTop, selectionBottom); + bool isInlineFlow = curr->isInlineFlowBox(); + if (isInlineFlow) + static_cast<InlineFlowBox*>(curr)->placeBoxesVertically(yPos, maxHeight, maxAscent, strictMode, topPosition, bottomPosition, selectionTop, selectionBottom); bool childAffectsTopBottomPos = true; - if (curr->yPos() == PositionTop) - curr->setYPos(y); - else if (curr->yPos() == PositionBottom) - curr->setYPos(y + maxHeight - curr->height()); + if (curr->y() == PositionTop) + curr->setY(yPos); + else if (curr->y() == PositionBottom) + curr->setY(yPos + maxHeight - curr->renderer()->lineHeight(m_firstLine)); else { - if (!curr->hasTextChildren() && !curr->renderBox()->hasHorizontalBordersOrPadding() && !strictMode) + if ((isInlineFlow && !static_cast<InlineFlowBox*>(curr)->hasTextChildren()) && !curr->boxModelObject()->hasHorizontalBordersOrPadding() && !strictMode) childAffectsTopBottomPos = false; - curr->setYPos(curr->yPos() + y + maxAscent - curr->baseline()); + int posAdjust = maxAscent - curr->renderer()->baselinePosition(m_firstLine); + if (!childAffectsTopBottomPos) + posAdjust = max(0, posAdjust); + curr->setY(curr->y() + yPos + posAdjust); } - int newY = curr->yPos(); - int newHeight = curr->height(); - int newBaseline = curr->baseline(); + int newY = curr->y(); int overflowTop = 0; int overflowBottom = 0; if (curr->isText() || curr->isInlineFlowBox()) { - const Font& font = curr->object()->style(m_firstLine)->font(); - newBaseline = font.ascent(); - newY += curr->baseline() - newBaseline; - newHeight = newBaseline + font.descent(); - for (ShadowData* shadow = curr->object()->style()->textShadow(); shadow; shadow = shadow->next) { + const Font& font = curr->renderer()->style(m_firstLine)->font(); + newY += curr->renderer()->baselinePosition(m_firstLine) - font.ascent(); + for (ShadowData* shadow = curr->renderer()->style()->textShadow(); shadow; shadow = shadow->next) { overflowTop = min(overflowTop, shadow->y - shadow->blur); overflowBottom = max(overflowBottom, shadow->y + shadow->blur); } - for (ShadowData* boxShadow = curr->object()->style(m_firstLine)->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + for (ShadowData* boxShadow = curr->renderer()->style(m_firstLine)->boxShadow(); boxShadow; boxShadow = boxShadow->next) { overflowTop = min(overflowTop, boxShadow->y - boxShadow->blur); overflowBottom = max(overflowBottom, boxShadow->y + boxShadow->blur); } - for (ShadowData* textShadow = curr->object()->style(m_firstLine)->textShadow(); textShadow; textShadow = textShadow->next) { + for (ShadowData* textShadow = curr->renderer()->style(m_firstLine)->textShadow(); textShadow; textShadow = textShadow->next) { overflowTop = min(overflowTop, textShadow->y - textShadow->blur); overflowBottom = max(overflowBottom, textShadow->y + textShadow->blur); } - if (curr->object()->hasReflection()) { - overflowTop = min(overflowTop, curr->renderBox()->reflectionBox().y()); - overflowBottom = max(overflowBottom, curr->renderBox()->reflectionBox().bottom()); + if (curr->renderer()->hasReflection()) { + RenderBox* box = toRenderBox(curr->renderer()); + overflowTop = min(overflowTop, box->reflectionBox().y()); + overflowBottom = max(overflowBottom, box->reflectionBox().bottom()); } - if (curr->isInlineFlowBox()) { - newHeight += curr->renderBox()->borderTop() + curr->renderBox()->paddingTop() + - curr->renderBox()->borderBottom() + curr->renderBox()->paddingBottom(); - newY -= curr->renderBox()->borderTop() + curr->renderBox()->paddingTop(); - newBaseline += curr->renderBox()->borderTop() + curr->renderBox()->paddingTop(); - } - } else if (!curr->object()->isBR()) { - newY += curr->renderBox()->marginTop(); - newHeight = curr->height() - (curr->renderBox()->marginTop() + curr->renderBox()->marginBottom()); - overflowTop = curr->renderBox()->overflowTop(false); - overflowBottom = curr->renderBox()->overflowHeight(false) - newHeight; + if (curr->isInlineFlowBox()) + newY -= curr->boxModelObject()->borderTop() + curr->boxModelObject()->paddingTop(); + } else if (!curr->renderer()->isBR()) { + RenderBox* box = toRenderBox(curr->renderer()); + newY += box->marginTop(); + overflowTop = box->overflowTop(false); + overflowBottom = box->overflowHeight(false) - box->height(); } - curr->setYPos(newY); - curr->setHeight(newHeight); - curr->setBaseline(newBaseline); + curr->setY(newY); if (childAffectsTopBottomPos) { selectionTop = min(selectionTop, newY); - selectionBottom = max(selectionBottom, newY + newHeight); + selectionBottom = max(selectionBottom, newY + curr->height()); topPosition = min(topPosition, newY + overflowTop); - bottomPosition = max(bottomPosition, newY + newHeight + overflowBottom); + bottomPosition = max(bottomPosition, newY + curr->height() + overflowBottom); } } if (isRootInlineBox()) { - const Font& font = object()->style(m_firstLine)->font(); - setHeight(font.ascent() + font.descent()); - setYPos(yPos() + baseline() - font.ascent()); - setBaseline(font.ascent()); + const Font& font = renderer()->style(m_firstLine)->font(); + setY(y() + renderer()->baselinePosition(m_firstLine, true) - font.ascent()); if (hasTextChildren() || strictMode) { - selectionTop = min(selectionTop, yPos()); - selectionBottom = max(selectionBottom, yPos() + height()); + selectionTop = min(selectionTop, y()); + selectionBottom = max(selectionBottom, y() + height()); } } } -void InlineFlowBox::shrinkBoxesWithNoTextChildren(int topPos, int bottomPos) -{ - // First shrink our kids. - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->object()->isPositioned()) - continue; // Positioned placeholders don't affect calculations. - - if (curr->isInlineFlowBox()) - static_cast<InlineFlowBox*>(curr)->shrinkBoxesWithNoTextChildren(topPos, bottomPos); - } - - // See if we have text children. If not, then we need to shrink ourselves to fit on the line. - if (!hasTextChildren() && !renderBox()->hasHorizontalBordersOrPadding()) { - if (yPos() < topPos) - setYPos(topPos); - if (yPos() + height() > bottomPos) - setHeight(bottomPos - yPos()); - if (baseline() > height()) - setBaseline(height()); - } -} - bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) { // Check children first. for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) { - if (!curr->object()->hasLayer() && curr->nodeAtPoint(request, result, x, y, tx, ty)) { - object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + if ((curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) && curr->nodeAtPoint(request, result, x, y, tx, ty)) { + renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } } // Now check ourselves. - IntRect rect(tx + m_x, ty + m_y, m_width, m_height); + IntRect rect(tx + m_x, ty + m_y, m_width, height()); if (visibleToHitTesting() && rect.contains(x, y)) { - object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); // Don't add in m_x or m_y here, we want coords in the containing block's space. + renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); // Don't add in m_x or m_y here, we want coords in the containing block's space. return true; } @@ -604,15 +570,15 @@ bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re void InlineFlowBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) { - int xPos = tx + m_x - object()->maximalOutlineSize(paintInfo.phase); - int w = width() + 2 * object()->maximalOutlineSize(paintInfo.phase); + int xPos = tx + m_x - renderer()->maximalOutlineSize(paintInfo.phase); + int w = width() + 2 * renderer()->maximalOutlineSize(paintInfo.phase); int shadowLeft = 0; int shadowRight = 0; - for (ShadowData* boxShadow = object()->style(m_firstLine)->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + for (ShadowData* boxShadow = renderer()->style(m_firstLine)->boxShadow(); boxShadow; boxShadow = boxShadow->next) { shadowLeft = min(boxShadow->x - boxShadow->blur, shadowLeft); shadowRight = max(boxShadow->x + boxShadow->blur, shadowRight); } - for (ShadowData* textShadow = object()->style(m_firstLine)->textShadow(); textShadow; textShadow = textShadow->next) { + for (ShadowData* textShadow = renderer()->style(m_firstLine)->textShadow(); textShadow; textShadow = textShadow->next) { shadowLeft = min(textShadow->x - textShadow->blur, shadowLeft); shadowRight = max(textShadow->x + textShadow->blur, shadowRight); } @@ -624,14 +590,15 @@ void InlineFlowBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) { // Add ourselves to the paint info struct's list of inlines that need to paint their // outlines. - if (object()->style()->visibility() == VISIBLE && object()->hasOutline() && !isRootInlineBox()) { - if ((flowObject()->continuation() || object()->isInlineContinuation()) && !object()->hasLayer()) { + if (renderer()->style()->visibility() == VISIBLE && renderer()->hasOutline() && !isRootInlineBox()) { + RenderInline* inlineFlow = toRenderInline(renderer()); + if ((inlineFlow->continuation() || inlineFlow->isInlineContinuation()) && !boxModelObject()->hasSelfPaintingLayer()) { // Add ourselves to the containing block of the entire continuation so that it can // paint us atomically. - RenderBlock* block = object()->containingBlock()->containingBlock(); - block->addContinuationWithOutline(static_cast<RenderFlow*>(object()->element()->renderer())); - } else if (!object()->isInlineContinuation()) - paintInfo.outlineObjects->add(flowObject()); + RenderBlock* block = renderer()->containingBlock()->containingBlock(); + block->addContinuationWithOutline(toRenderInline(renderer()->node()->renderer())); + } else if (!inlineFlow->isInlineContinuation()) + paintInfo.outlineObjects->add(inlineFlow); } } else if (paintInfo.phase == PaintPhaseMask) { paintMask(paintInfo, tx, ty); @@ -651,12 +618,12 @@ void InlineFlowBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) PaintPhase paintPhase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase; RenderObject::PaintInfo childInfo(paintInfo); childInfo.phase = paintPhase; - childInfo.paintingRoot = object()->paintingRootForChildren(paintInfo); + childInfo.paintingRoot = renderer()->paintingRootForChildren(paintInfo); // 3. Paint our children. if (paintPhase != PaintPhaseSelfOutline) { for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (!curr->object()->hasLayer()) + if (curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) curr->paint(childInfo, tx, ty); } } @@ -679,9 +646,9 @@ void InlineFlowBox::paintFillLayer(const RenderObject::PaintInfo& paintInfo, con int my, int mh, int tx, int ty, int w, int h, CompositeOperator op) { StyleImage* img = fillLayer->image(); - bool hasFillImage = img && img->canRender(object()->style()->effectiveZoom()); - if ((!hasFillImage && !object()->style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent()) - object()->paintFillLayerExtended(paintInfo, c, fillLayer, my, mh, tx, ty, w, h, this, op); + bool hasFillImage = img && img->canRender(renderer()->style()->effectiveZoom()); + if ((!hasFillImage && !renderer()->style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent()) + boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, my, mh, tx, ty, w, h, this, op); else { // We have a fill image that spans multiple lines. // We need to adjust _tx and _ty by the width of all previous lines. @@ -700,7 +667,7 @@ void InlineFlowBox::paintFillLayer(const RenderObject::PaintInfo& paintInfo, con totalWidth += curr->width(); paintInfo.context->save(); paintInfo.context->clip(IntRect(tx, ty, width(), height())); - object()->paintFillLayerExtended(paintInfo, c, fillLayer, my, mh, startX, ty, totalWidth, h, this, op); + boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, my, mh, startX, ty, totalWidth, h, this, op); paintInfo.context->restore(); } } @@ -708,17 +675,17 @@ void InlineFlowBox::paintFillLayer(const RenderObject::PaintInfo& paintInfo, con void InlineFlowBox::paintBoxShadow(GraphicsContext* context, RenderStyle* s, int tx, int ty, int w, int h) { if ((!prevLineBox() && !nextLineBox()) || !parent()) - object()->paintBoxShadow(context, tx, ty, w, h, s); + boxModelObject()->paintBoxShadow(context, tx, ty, w, h, s); else { // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines - object()->paintBoxShadow(context, tx, ty, w, h, s, includeLeftEdge(), includeRightEdge()); + boxModelObject()->paintBoxShadow(context, tx, ty, w, h, s, includeLeftEdge(), includeRightEdge()); } } void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int tx, int ty) { - if (!object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) + if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) return; // Move x/y to our coordinates. @@ -739,8 +706,8 @@ void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int // You can use p::first-line to specify a background. If so, the root line boxes for // a line may actually have to paint a background. - RenderStyle* styleToUse = object()->style(m_firstLine); - if ((!parent() && m_firstLine && styleToUse != object()->style()) || (parent() && object()->hasBoxDecorations())) { + RenderStyle* styleToUse = renderer()->style(m_firstLine); + if ((!parent() && m_firstLine && styleToUse != renderer()->style()) || (parent() && renderer()->hasBoxDecorations())) { // Shadow comes first and is behind the background and border. if (styleToUse->boxShadow()) paintBoxShadow(context, styleToUse, tx, ty, w, h); @@ -750,8 +717,8 @@ void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int // :first-line cannot be used to put borders on a line. Always paint borders with our // non-first-line style. - if (parent() && object()->style()->hasBorder()) { - StyleImage* borderImage = object()->style()->borderImage().image(); + if (parent() && renderer()->style()->hasBorder()) { + StyleImage* borderImage = renderer()->style()->borderImage().image(); bool hasBorderImage = borderImage && borderImage->canRender(styleToUse->effectiveZoom()); if (hasBorderImage && !borderImage->isLoaded()) return; // Don't paint anything while we wait for the image to load. @@ -759,7 +726,7 @@ void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int // The simple case is where we either have no border image or we are the only box for this object. In those // cases only a single call to draw is required. if (!hasBorderImage || (!prevLineBox() && !nextLineBox())) - object()->paintBorder(context, tx, ty, w, h, object()->style(), includeLeftEdge(), includeRightEdge()); + boxModelObject()->paintBorder(context, tx, ty, w, h, renderer()->style(), includeLeftEdge(), includeRightEdge()); else { // We have a border image that spans multiple lines. // We need to adjust _tx and _ty by the width of all previous lines. @@ -778,7 +745,7 @@ void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int totalWidth += curr->width(); context->save(); context->clip(IntRect(tx, ty, width(), height())); - object()->paintBorder(context, startX, ty, totalWidth, h, object()->style()); + boxModelObject()->paintBorder(context, startX, ty, totalWidth, h, renderer()->style()); context->restore(); } } @@ -787,7 +754,7 @@ void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int void InlineFlowBox::paintMask(RenderObject::PaintInfo& paintInfo, int tx, int ty) { - if (!object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; // Move x/y to our coordinates. @@ -807,9 +774,9 @@ void InlineFlowBox::paintMask(RenderObject::PaintInfo& paintInfo, int tx, int ty // Figure out if we need to push a transparency layer to render our mask. bool pushTransparencyLayer = false; - const NinePieceImage& maskNinePieceImage = object()->style()->maskBoxImage(); - StyleImage* maskBoxImage = object()->style()->maskBoxImage().image(); - if ((maskBoxImage && object()->style()->maskLayers()->hasImage()) || object()->style()->maskLayers()->next()) + const NinePieceImage& maskNinePieceImage = renderer()->style()->maskBoxImage(); + StyleImage* maskBoxImage = renderer()->style()->maskBoxImage().image(); + if ((maskBoxImage && renderer()->style()->maskLayers()->hasImage()) || renderer()->style()->maskLayers()->next()) pushTransparencyLayer = true; CompositeOperator compositeOp = CompositeDestinationIn; @@ -819,16 +786,16 @@ void InlineFlowBox::paintMask(RenderObject::PaintInfo& paintInfo, int tx, int ty compositeOp = CompositeSourceOver; } - paintFillLayers(paintInfo, Color(), object()->style()->maskLayers(), my, mh, tx, ty, w, h, compositeOp); + paintFillLayers(paintInfo, Color(), renderer()->style()->maskLayers(), my, mh, tx, ty, w, h, compositeOp); - bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(object()->style()->effectiveZoom()); + bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(renderer()->style()->effectiveZoom()); if (!hasBoxImage || !maskBoxImage->isLoaded()) return; // Don't paint anything while we wait for the image to load. // The simple case is where we are the only box for this object. In those // cases only a single call to draw is required. if (!prevLineBox() && !nextLineBox()) { - object()->paintNinePieceImage(paintInfo.context, tx, ty, w, h, object()->style(), maskNinePieceImage, compositeOp); + boxModelObject()->paintNinePieceImage(paintInfo.context, tx, ty, w, h, renderer()->style(), maskNinePieceImage, compositeOp); } else { // We have a mask image that spans multiple lines. // We need to adjust _tx and _ty by the width of all previous lines. @@ -841,7 +808,7 @@ void InlineFlowBox::paintMask(RenderObject::PaintInfo& paintInfo, int tx, int ty totalWidth += curr->width(); paintInfo.context->save(); paintInfo.context->clip(IntRect(tx, ty, width(), height())); - object()->paintNinePieceImage(paintInfo.context, startX, ty, totalWidth, h, object()->style(), maskNinePieceImage, compositeOp); + boxModelObject()->paintNinePieceImage(paintInfo.context, startX, ty, totalWidth, h, renderer()->style(), maskNinePieceImage, compositeOp); paintInfo.context->restore(); } @@ -857,7 +824,7 @@ static bool shouldDrawTextDecoration(RenderObject* obj) if (curr->isText() && !curr->isBR()) { if (!curr->style()->collapseWhiteSpace()) return true; - Node* currElement = curr->element(); + Node* currElement = curr->node(); if (!currElement) return true; if (!currElement->isTextNode()) @@ -873,8 +840,8 @@ void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int { // Paint text decorations like underlines/overlines. We only do this if we aren't in quirks mode (i.e., in // almost-strict mode or strict mode). - if (object()->style()->htmlHacks() || !object()->shouldPaintWithinRoot(paintInfo) || - object()->style()->visibility() != VISIBLE) + if (renderer()->style()->htmlHacks() || !renderer()->shouldPaintWithinRoot(paintInfo) || + renderer()->style()->visibility() != VISIBLE) return; // We don't want underlines or other decorations when we're trying to draw nothing but the selection as white text. @@ -884,16 +851,16 @@ void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int GraphicsContext* context = paintInfo.context; tx += m_x; ty += m_y; - RenderStyle* styleToUse = object()->style(m_firstLine); + RenderStyle* styleToUse = renderer()->style(m_firstLine); int deco = parent() ? styleToUse->textDecoration() : styleToUse->textDecorationsInEffect(); if (deco != TDNONE && ((!paintedChildren && ((deco & UNDERLINE) || (deco & OVERLINE))) || (paintedChildren && (deco & LINE_THROUGH))) && - shouldDrawTextDecoration(object())) { + shouldDrawTextDecoration(renderer())) { int x = m_x + borderLeft() + paddingLeft(); int w = m_width - (borderLeft() + paddingLeft() + borderRight() + paddingRight()); RootInlineBox* rootLine = root(); if (rootLine->ellipsisBox()) { - int ellipsisX = rootLine->ellipsisBox()->xPos(); + int ellipsisX = rootLine->ellipsisBox()->x(); int ellipsisWidth = rootLine->ellipsisBox()->width(); // FIXME: Will need to work with RTL @@ -914,9 +881,9 @@ void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int Color underline, overline, linethrough; underline = overline = linethrough = styleToUse->color(); if (!parent()) - object()->getTextDecorationColors(deco, underline, overline, linethrough); + renderer()->getTextDecorationColors(deco, underline, overline, linethrough); - bool isPrinting = object()->document()->printing(); + bool isPrinting = renderer()->document()->printing(); context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1. bool paintUnderline = deco & UNDERLINE && !paintedChildren; @@ -925,13 +892,17 @@ void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int bool linesAreOpaque = !isPrinting && (!paintUnderline || underline.alpha() == 255) && (!paintOverline || overline.alpha() == 255) && (!paintLineThrough || linethrough.alpha() == 255); + int baselinePos = renderer()->style(m_firstLine)->font().ascent(); + if (!isRootInlineBox()) + baselinePos += borderTop() + paddingTop(); + bool setClip = false; int extraOffset = 0; ShadowData* shadow = styleToUse->textShadow(); if (!linesAreOpaque && shadow && shadow->next) { - IntRect clipRect(tx, ty, w, m_baseline + 2); + IntRect clipRect(tx, ty, w, baselinePos + 2); for (ShadowData* s = shadow; s; s = s->next) { - IntRect shadowRect(tx, ty, w, m_baseline + 2); + IntRect shadowRect(tx, ty, w, baselinePos + 2); shadowRect.inflate(s->blur); shadowRect.move(s->x, s->y); clipRect.unite(shadowRect); @@ -939,7 +910,7 @@ void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int } context->save(); context->clip(clipRect); - extraOffset += m_baseline + 2; + extraOffset += baselinePos + 2; ty += extraOffset; setClip = true; } @@ -959,16 +930,19 @@ void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int if (paintUnderline) { context->setStrokeColor(underline); + context->setStrokeStyle(SolidStroke); // Leave one pixel of white between the baseline and the underline. - context->drawLineForText(IntPoint(tx, ty + m_baseline + 1), w, isPrinting); + context->drawLineForText(IntPoint(tx, ty + baselinePos + 1), w, isPrinting); } if (paintOverline) { context->setStrokeColor(overline); + context->setStrokeStyle(SolidStroke); context->drawLineForText(IntPoint(tx, ty), w, isPrinting); } if (paintLineThrough) { context->setStrokeColor(linethrough); - context->drawLineForText(IntPoint(tx, ty + 2 * m_baseline / 3), w, isPrinting); + context->setStrokeStyle(SolidStroke); + context->drawLineForText(IntPoint(tx, ty + 2 * baselinePos / 3), w, isPrinting); } } while (shadow); diff --git a/WebCore/rendering/InlineFlowBox.h b/WebCore/rendering/InlineFlowBox.h index 62f8f69..df75d06 100644 --- a/WebCore/rendering/InlineFlowBox.h +++ b/WebCore/rendering/InlineFlowBox.h @@ -25,9 +25,9 @@ namespace WebCore { +class HitTestRequest; class HitTestResult; - -struct HitTestRequest; +class RenderLineBoxList; class InlineFlowBox : public InlineRunBox { public: @@ -36,6 +36,9 @@ public: , m_firstChild(0) , m_lastChild(0) , m_maxHorizontalVisualOverflow(0) + , m_includeLeftEdge(false) + , m_includeRightEdge(false) + , m_hasTextChildren(true) #ifndef NDEBUG , m_hasBadChildList(false) #endif @@ -52,9 +55,7 @@ public: virtual ~InlineFlowBox(); #endif - RenderFlow* flowObject(); - - virtual bool isInlineFlowBox() { return true; } + virtual int height() const; InlineFlowBox* prevFlowBox() const { return static_cast<InlineFlowBox*>(m_prevLine); } InlineFlowBox* nextFlowBox() const { return static_cast<InlineFlowBox*>(m_nextLine); } @@ -80,6 +81,10 @@ public: virtual void attachLine(); virtual void adjustPosition(int dx, int dy); + virtual void extractLineBoxFromRenderObject(); + virtual void attachLineBoxToRenderObject(); + virtual void removeLineBoxFromRenderObject(); + virtual void clearTruncation(); virtual void paintBoxDecorations(RenderObject::PaintInfo&, int tx, int ty); @@ -93,17 +98,23 @@ public: virtual void paint(RenderObject::PaintInfo&, int tx, int ty); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); - int marginBorderPaddingLeft(); - int marginBorderPaddingRight(); - int marginLeft(); - int marginRight(); - int borderLeft() { if (includeLeftEdge()) return renderBox()->borderLeft(); return 0; } - int borderRight() { if (includeRightEdge()) return renderBox()->borderRight(); return 0; } - int paddingLeft() { if (includeLeftEdge()) return renderBox()->paddingLeft(); return 0; } - int paddingRight() { if (includeRightEdge()) return renderBox()->paddingRight(); return 0; } - - bool includeLeftEdge() { return m_includeLeftEdge; } - bool includeRightEdge() { return m_includeRightEdge; } + virtual RenderLineBoxList* rendererLineBoxes() const; + + int marginBorderPaddingLeft() const { return marginLeft() + borderLeft() + paddingLeft(); } + int marginBorderPaddingRight() const { return marginRight() + borderRight() + paddingRight(); } + int marginLeft() const { if (includeLeftEdge()) return boxModelObject()->marginLeft(); return 0; } + int marginRight() const { if (includeRightEdge()) return boxModelObject()->marginRight(); return 0; } + int borderLeft() const { if (includeLeftEdge()) return renderer()->style()->borderLeftWidth(); return 0; } + int borderRight() const { if (includeRightEdge()) return renderer()->style()->borderRightWidth(); return 0; } + int borderTop() const { return renderer()->style()->borderTopWidth(); } + int borderBottom() const { return renderer()->style()->borderBottomWidth(); } + int paddingLeft() const { if (includeLeftEdge()) return boxModelObject()->paddingLeft(); return 0; } + int paddingRight() const { if (includeRightEdge()) return boxModelObject()->paddingRight(); return 0; } + int paddingTop() const { return boxModelObject()->paddingTop(); } + int paddingBottom() const { return boxModelObject()->paddingBottom(); } + + bool includeLeftEdge() const { return m_includeLeftEdge; } + bool includeRightEdge() const { return m_includeRightEdge; } void setEdges(bool includeLeft, bool includeRight) { m_includeLeftEdge = includeLeft; @@ -122,11 +133,10 @@ public: int maxPositionTop, int maxPositionBottom); void placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode, int& topPosition, int& bottomPosition, int& selectionTop, int& selectionBottom); - void shrinkBoxesWithNoTextChildren(int topPosition, int bottomPosition); virtual void setVerticalOverflowPositions(int /*top*/, int /*bottom*/) { } virtual void setVerticalSelectionPositions(int /*top*/, int /*bottom*/) { } - int maxHorizontalVisualOverflow() const { return m_maxHorizontalVisualOverflow; } + short maxHorizontalVisualOverflow() const { return m_maxHorizontalVisualOverflow; } void removeChild(InlineBox* child); @@ -135,13 +145,21 @@ public: virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth); virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool&); + bool hasTextChildren() const { return m_hasTextChildren; } + void checkConsistency() const; void setHasBadChildList(); private: + virtual bool isInlineFlowBox() const { return true; } + InlineBox* m_firstChild; InlineBox* m_lastChild; - int m_maxHorizontalVisualOverflow; + short m_maxHorizontalVisualOverflow; + + bool m_includeLeftEdge : 1; + bool m_includeRightEdge : 1; + bool m_hasTextChildren : 1; #ifndef NDEBUG bool m_hasBadChildList; diff --git a/WebCore/rendering/InlineTextBox.cpp b/WebCore/rendering/InlineTextBox.cpp index ca965e9..809c071 100644 --- a/WebCore/rendering/InlineTextBox.cpp +++ b/WebCore/rendering/InlineTextBox.cpp @@ -41,6 +41,11 @@ using namespace std; namespace WebCore { +int InlineTextBox::height() const +{ + return m_treatAsText ? renderer()->style(m_firstLine)->font().height() : 0; +} + int InlineTextBox::selectionTop() { return root()->selectionTop(); @@ -60,10 +65,10 @@ bool InlineTextBox::isSelected(int startPos, int endPos) const RenderObject::SelectionState InlineTextBox::selectionState() { - RenderObject::SelectionState state = object()->selectionState(); + RenderObject::SelectionState state = renderer()->selectionState(); if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) { int startPos, endPos; - object()->selectionStartEnd(startPos, endPos); + renderer()->selectionStartEnd(startPos, endPos); // The position after a hard line break is considered to be past its end. int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0); @@ -92,7 +97,7 @@ IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos) if (sPos >= ePos) return IntRect(); - RenderText* textObj = textObject(); + RenderText* textObj = textRenderer(); int selTop = selectionTop(); int selHeight = selectionHeight(); const Font& f = textObj->style(m_firstLine)->font(); @@ -108,7 +113,7 @@ IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos) void InlineTextBox::deleteLine(RenderArena* arena) { - toRenderText(m_object)->removeTextBox(this); + toRenderText(renderer())->removeTextBox(this); destroy(arena); } @@ -117,7 +122,7 @@ void InlineTextBox::extractLine() if (m_extracted) return; - toRenderText(m_object)->extractTextBox(this); + toRenderText(renderer())->extractTextBox(this); } void InlineTextBox::attachLine() @@ -125,7 +130,7 @@ void InlineTextBox::attachLine() if (!m_extracted) return; - toRenderText(m_object)->attachTextBox(this); + toRenderText(renderer())->attachTextBox(this); } int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox) @@ -137,36 +142,40 @@ int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, int ellipsisX = ltr ? blockEdge - ellipsisWidth : blockEdge + ellipsisWidth; - // For LTR, if the left edge of the ellipsis is to the left of our text run, then we are the run that will get truncated. - if (ltr) { - if (ellipsisX <= m_x) { - // Too far. Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box. - m_truncation = cFullTruncation; - foundBox = true; - return -1; - } + // Criteria for full truncation: + // LTR: the left edge of the ellipsis is to the left of our text run. + // RTL: the right edge of the ellipsis is to the right of our text run. + bool ltrFullTruncation = ltr && ellipsisX <= m_x; + bool rtlFullTruncation = !ltr && ellipsisX >= (m_x + m_width); + if (ltrFullTruncation || rtlFullTruncation) { + // Too far. Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box. + m_truncation = cFullTruncation; + foundBox = true; + return -1; + } - if (ellipsisX < m_x + m_width) { - if (direction() == RTL) - return -1; // FIXME: Support LTR truncation when the last run is RTL someday. + bool ltrEllipsisWithinBox = ltr && (ellipsisX < m_x + m_width); + bool rtlEllipsisWithinBox = !ltr && (ellipsisX > m_x); + if (ltrEllipsisWithinBox || rtlEllipsisWithinBox) { + if ((ltr && direction() == RTL) || (!ltr && direction() == LTR)) + return -1; // FIXME: Support cases in which the last run's directionality differs from the context. - foundBox = true; + foundBox = true; - int offset = offsetForPosition(ellipsisX, false); - if (offset == 0) { - // No characters should be rendered. Set ourselves to full truncation and place the ellipsis at the min of our start - // and the ellipsis edge. - m_truncation = cFullTruncation; - return min(ellipsisX, m_x); - } - - // Set the truncation index on the text run. The ellipsis needs to be placed just after the last visible character. - m_truncation = offset; - return m_x + toRenderText(m_object)->width(m_start, offset, textPos(), m_firstLine); + int offset = offsetForPosition(ellipsisX, false); + if (offset == 0) { + // No characters should be rendered. Set ourselves to full truncation and place the ellipsis at the min of our start + // and the ellipsis edge. + m_truncation = cFullTruncation; + return min(ellipsisX, m_x); } - } - else { - // FIXME: Support RTL truncation someday, including both modes (when the leftmost run on the line is either RTL or LTR) + + // Set the truncation index on the text run. The ellipsis needs to be placed just after the last visible character. + m_truncation = offset; + if (ltr) + return m_x + toRenderText(renderer())->width(m_start, offset, textPos(), m_firstLine); + else + return m_x + (m_width - toRenderText(renderer())->width(m_start, offset, textPos(), m_firstLine)) - ellipsisWidth; } return -1; } @@ -216,7 +225,7 @@ void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, con bool InlineTextBox::isLineBreak() const { - return object()->isBR() || (object()->style()->preserveNewline() && len() == 1 && (*textObject()->text())[start()] == '\n'); + return renderer()->isBR() || (renderer()->style()->preserveNewline() && len() == 1 && (*textRenderer()->text())[start()] == '\n'); } bool InlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int x, int y, int tx, int ty) @@ -224,9 +233,9 @@ bool InlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, in if (isLineBreak()) return false; - IntRect rect(tx + m_x, ty + m_y, m_width, m_height); + IntRect rect(tx + m_x, ty + m_y, m_width, height()); if (m_truncation != cFullTruncation && visibleToHitTesting() && rect.contains(x, y)) { - object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } return false; @@ -278,7 +287,7 @@ static void paintTextWithShadows(GraphicsContext* context, const Font& font, con void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) { - if (isLineBreak() || !object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || + if (isLineBreak() || !renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline) return; @@ -289,7 +298,7 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) if (xPos >= paintInfo.rect.right() || xPos + w <= paintInfo.rect.x()) return; - bool isPrinting = textObject()->document()->printing(); + bool isPrinting = textRenderer()->document()->printing(); // Determine whether or not we're selected. bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone; @@ -300,11 +309,11 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) GraphicsContext* context = paintInfo.context; // Determine whether or not we have composition underlines to draw. - bool containsComposition = object()->document()->frame()->editor()->compositionNode() == object()->node(); - bool useCustomUnderlines = containsComposition && object()->document()->frame()->editor()->compositionUsesCustomUnderlines(); + bool containsComposition = renderer()->node() && renderer()->document()->frame()->editor()->compositionNode() == renderer()->node(); + bool useCustomUnderlines = containsComposition && renderer()->document()->frame()->editor()->compositionUsesCustomUnderlines(); // Set our font. - RenderStyle* styleToUse = object()->style(m_firstLine); + RenderStyle* styleToUse = renderer()->style(m_firstLine); int d = styleToUse->textDecorationsInEffect(); const Font& font = styleToUse->font(); @@ -319,8 +328,8 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) if (containsComposition && !useCustomUnderlines) paintCompositionBackground(context, tx, ty, styleToUse, font, - object()->document()->frame()->editor()->compositionStart(), - object()->document()->frame()->editor()->compositionEnd()); + renderer()->document()->frame()->editor()->compositionStart(), + renderer()->document()->frame()->editor()->compositionEnd()); paintDocumentMarkers(context, tx, ty, styleToUse, font, true); @@ -367,14 +376,14 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) ShadowData* selectionShadow = textShadow; if (haveSelection) { // Check foreground color first. - Color foreground = paintInfo.forceBlackText ? Color::black : object()->selectionForegroundColor(); + Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor(); if (foreground.isValid() && foreground != selectionFillColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionFillColor = foreground; } - if (RenderStyle* pseudoStyle = object()->getCachedPseudoStyle(RenderStyle::SELECTION)) { + if (RenderStyle* pseudoStyle = renderer()->getCachedPseudoStyle(SELECTION)) { ShadowData* shadow = paintInfo.forceBlackText ? 0 : pseudoStyle->textShadow(); if (shadow != selectionShadow) { if (!paintSelectedTextOnly) @@ -400,8 +409,9 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) } } - IntPoint textOrigin(m_x + tx, m_y + ty + m_baseline); - TextRun textRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()); + int baseline = renderer()->style(m_firstLine)->font().ascent(); + IntPoint textOrigin(m_x + tx, m_y + ty + baseline); + TextRun textRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()); int sPos = 0; int ePos = 0; @@ -447,7 +457,7 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) paintDocumentMarkers(context, tx, ty, styleToUse, font, false); if (useCustomUnderlines) { - const Vector<CompositionUnderline>& underlines = object()->document()->frame()->editor()->customCompositionUnderlines(); + const Vector<CompositionUnderline>& underlines = renderer()->document()->frame()->editor()->customCompositionUnderlines(); size_t numUnderlines = underlines.size(); for (size_t index = 0; index < numUnderlines; ++index) { @@ -476,14 +486,14 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) void InlineTextBox::selectionStartEnd(int& sPos, int& ePos) { int startPos, endPos; - if (object()->selectionState() == RenderObject::SelectionInside) { + if (renderer()->selectionState() == RenderObject::SelectionInside) { startPos = 0; - endPos = textObject()->textLength(); + endPos = textRenderer()->textLength(); } else { - textObject()->selectionStartEnd(startPos, endPos); - if (object()->selectionState() == RenderObject::SelectionStart) - endPos = textObject()->textLength(); - else if (object()->selectionState() == RenderObject::SelectionEnd) + textRenderer()->selectionStartEnd(startPos, endPos); + if (renderer()->selectionState() == RenderObject::SelectionStart) + endPos = textRenderer()->textLength(); + else if (renderer()->selectionState() == RenderObject::SelectionEnd) startPos = 0; } @@ -500,7 +510,7 @@ void InlineTextBox::paintSelection(GraphicsContext* context, int tx, int ty, Ren return; Color textColor = style->color(); - Color c = object()->selectionBackgroundColor(); + Color c = renderer()->selectionBackgroundColor(); if (!c.isValid() || c.alpha() == 0) return; @@ -514,7 +524,7 @@ void InlineTextBox::paintSelection(GraphicsContext* context, int tx, int ty, Ren int y = selectionTop(); int h = selectionHeight(); context->clip(IntRect(m_x + tx, y + ty, m_width, h)); - context->drawHighlightForText(font, TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, + context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), IntPoint(m_x + tx, y + ty), h, c, sPos, ePos); context->restore(); @@ -537,7 +547,7 @@ void InlineTextBox::paintCompositionBackground(GraphicsContext* context, int tx, int y = selectionTop(); int h = selectionHeight(); - context->drawHighlightForText(font, TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, + context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), IntPoint(m_x + tx, y + ty), h, c, sPos, ePos); context->restore(); @@ -547,7 +557,7 @@ void InlineTextBox::paintCompositionBackground(GraphicsContext* context, int tx, void InlineTextBox::paintCustomHighlight(int tx, int ty, const AtomicString& type) { - Frame* frame = object()->document()->frame(); + Frame* frame = renderer()->document()->frame(); if (!frame) return; Page* page = frame->page(); @@ -555,10 +565,10 @@ void InlineTextBox::paintCustomHighlight(int tx, int ty, const AtomicString& typ return; RootInlineBox* r = root(); - FloatRect rootRect(tx + r->xPos(), ty + selectionTop(), r->width(), selectionHeight()); - FloatRect textRect(tx + xPos(), rootRect.y(), width(), rootRect.height()); + FloatRect rootRect(tx + r->x(), ty + selectionTop(), r->width(), selectionHeight()); + FloatRect textRect(tx + x(), rootRect.y(), width(), rootRect.height()); - page->chrome()->client()->paintCustomHighlight(object()->node(), type, textRect, rootRect, true, false); + page->chrome()->client()->paintCustomHighlight(renderer()->node(), type, textRect, rootRect, true, false); } #endif @@ -570,27 +580,33 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, in if (m_truncation == cFullTruncation) return; - - int width = (m_truncation == cNoTruncation) ? m_width - : toRenderText(m_object)->width(m_start, m_truncation, textPos(), m_firstLine); + + int width = m_width; + if (m_truncation != cNoTruncation) { + width = toRenderText(renderer())->width(m_start, m_truncation, textPos(), m_firstLine); + if (direction() == RTL) + tx += (m_width - width); + } // Get the text decoration colors. Color underline, overline, linethrough; - object()->getTextDecorationColors(deco, underline, overline, linethrough, true); + renderer()->getTextDecorationColors(deco, underline, overline, linethrough, true); // Use a special function for underlines to get the positioning exactly right. - bool isPrinting = textObject()->document()->printing(); + bool isPrinting = textRenderer()->document()->printing(); context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1. bool linesAreOpaque = !isPrinting && (!(deco & UNDERLINE) || underline.alpha() == 255) && (!(deco & OVERLINE) || overline.alpha() == 255) && (!(deco & LINE_THROUGH) || linethrough.alpha() == 255); + int baseline = renderer()->style(m_firstLine)->font().ascent(); + bool setClip = false; int extraOffset = 0; if (!linesAreOpaque && shadow && shadow->next) { context->save(); - IntRect clipRect(tx, ty, width, m_baseline + 2); + IntRect clipRect(tx, ty, width, baseline + 2); for (ShadowData* s = shadow; s; s = s->next) { - IntRect shadowRect(tx, ty, width, m_baseline + 2); + IntRect shadowRect(tx, ty, width, baseline + 2); shadowRect.inflate(s->blur); shadowRect.move(s->x, s->y); clipRect.unite(shadowRect); @@ -598,12 +614,13 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, in } context->save(); context->clip(clipRect); - extraOffset += m_baseline + 2; + extraOffset += baseline + 2; ty += extraOffset; setClip = true; } bool setShadow = false; + do { if (shadow) { if (!shadow->next) { @@ -618,16 +635,19 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, in if (deco & UNDERLINE) { context->setStrokeColor(underline); + context->setStrokeStyle(SolidStroke); // Leave one pixel of white between the baseline and the underline. - context->drawLineForText(IntPoint(tx, ty + m_baseline + 1), width, isPrinting); + context->drawLineForText(IntPoint(tx, ty + baseline + 1), width, isPrinting); } if (deco & OVERLINE) { context->setStrokeColor(overline); + context->setStrokeStyle(SolidStroke); context->drawLineForText(IntPoint(tx, ty), width, isPrinting); } if (deco & LINE_THROUGH) { context->setStrokeColor(linethrough); - context->drawLineForText(IntPoint(tx, ty + 2 * m_baseline / 3), width, isPrinting); + context->setStrokeStyle(SolidStroke); + context->drawLineForText(IntPoint(tx, ty + 2 * baseline / 3), width, isPrinting); } } while (shadow); @@ -640,7 +660,7 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, in void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font& font, bool grammar) { // Never print spelling/grammar markers (5327887) - if (textObject()->document()->printing()) + if (textRenderer()->document()->printing()) return; if (m_truncation == cFullTruncation) @@ -667,7 +687,7 @@ void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, in // Calculate start & width IntPoint startPoint(tx + m_x, ty + selectionTop()); - TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); int h = selectionHeight(); IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, startPosition, endPosition)); @@ -677,7 +697,7 @@ void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, in // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to // display a toolTip. We don't do this for misspelling markers. if (grammar) - object()->document()->setRenderedRectForMarker(object()->node(), marker, markerRect); + renderer()->document()->setRenderedRectForMarker(renderer()->node(), marker, markerRect); } // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to @@ -687,14 +707,15 @@ void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, in // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so // we pin to two pixels under the baseline. int lineThickness = cMisspellingLineThickness; - int descent = m_height - m_baseline; + int baseline = renderer()->style(m_firstLine)->font().ascent(); + int descent = height() - baseline; int underlineOffset; if (descent <= (2 + lineThickness)) { - // place the underline at the very bottom of the text in small/medium fonts - underlineOffset = m_height - lineThickness; + // Place the underline at the very bottom of the text in small/medium fonts. + underlineOffset = height() - lineThickness; } else { - // in larger fonts, tho, place the underline up near the baseline to prevent big gap - underlineOffset = m_baseline + 2; + // In larger fonts, though, place the underline up near the baseline to prevent a big gap. + underlineOffset = baseline + 2; } pt->drawLineForMisspellingOrBadGrammar(IntPoint(tx + m_x + start, ty + m_y + underlineOffset), width, grammar); } @@ -708,15 +729,15 @@ void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, Do int sPos = max(marker.startOffset - m_start, (unsigned)0); int ePos = min(marker.endOffset - m_start, (unsigned)m_len); - TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); IntPoint startPoint = IntPoint(m_x + tx, y + ty); // Always compute and store the rect associated with this marker IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos)); - object()->document()->setRenderedRectForMarker(object()->node(), marker, markerRect); + renderer()->document()->setRenderedRectForMarker(renderer()->node(), marker, markerRect); // Optionally highlight the text - if (object()->document()->frame()->markedTextMatchesAreHighlighted()) { + if (renderer()->document()->frame()->markedTextMatchesAreHighlighted()) { Color color = theme()->platformTextSearchHighlightColor(); pt->save(); updateGraphicsContext(pt, color, color, 0); // Don't draw text at all! @@ -728,7 +749,10 @@ void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, Do void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, int tx, int ty, RenderStyle* style, const Font& font, bool background) { - Vector<DocumentMarker> markers = object()->document()->markersForNode(object()->node()); + if (!renderer()->node()) + return; + + Vector<DocumentMarker> markers = renderer()->document()->markersForNode(renderer()->node()); Vector<DocumentMarker>::iterator markerIt = markers.begin(); // Give any document markers that touch this run a chance to draw before the text has been drawn. @@ -797,7 +821,7 @@ void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, int tx, int if (paintStart <= underline.startOffset) { paintStart = underline.startOffset; useWholeWidth = false; - start = toRenderText(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine); + start = toRenderText(renderer())->width(m_start, paintStart - m_start, textPos(), m_firstLine); } if (paintEnd != underline.endOffset) { // end points at the last char, not past it paintEnd = min(paintEnd, (unsigned)underline.endOffset); @@ -808,14 +832,15 @@ void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, int tx, int useWholeWidth = false; } if (!useWholeWidth) { - width = toRenderText(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine); + width = toRenderText(renderer())->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine); } // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline. // All other marked text underlines are 1px thick. // If there's not enough space the underline will touch or overlap characters. int lineThickness = 1; - if (underline.thick && m_height - m_baseline >= 2) + int baseline = renderer()->style(m_firstLine)->font().ascent(); + if (underline.thick && height() - baseline >= 2) lineThickness = 2; // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those. @@ -825,7 +850,7 @@ void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, int tx, int ctx->setStrokeColor(underline.color); ctx->setStrokeThickness(lineThickness); - ctx->drawLineForText(IntPoint(tx + start, ty + m_height - lineThickness), width, textObject()->document()->printing()); + ctx->drawLineForText(IntPoint(tx + start, ty + height() - lineThickness), width, textRenderer()->document()->printing()); } int InlineTextBox::caretMinOffset() const @@ -845,12 +870,12 @@ unsigned InlineTextBox::caretMaxRenderedOffset() const int InlineTextBox::textPos() const { - if (xPos() == 0) + if (x() == 0) return 0; - RenderBlock *blockElement = object()->containingBlock(); - return direction() == RTL ? xPos() - blockElement->borderRight() - blockElement->paddingRight() - : xPos() - blockElement->borderLeft() - blockElement->paddingLeft(); + RenderBlock *blockElement = renderer()->containingBlock(); + return direction() == RTL ? x() - blockElement->borderRight() - blockElement->paddingRight() + : x() - blockElement->borderLeft() - blockElement->paddingLeft(); } int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const @@ -858,10 +883,10 @@ int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const if (isLineBreak()) return 0; - RenderText* text = toRenderText(m_object); + RenderText* text = toRenderText(renderer()); RenderStyle *style = text->style(m_firstLine); const Font* f = &style->font(); - return f->offsetForPosition(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), + return f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), _x - m_x, includePartialGlyphs); } @@ -873,12 +898,12 @@ int InlineTextBox::positionForOffset(int offset) const if (isLineBreak()) return m_x; - RenderText* text = toRenderText(m_object); + RenderText* text = toRenderText(renderer()); const Font& f = text->style(m_firstLine)->font(); int from = direction() == RTL ? offset - m_start : 0; int to = direction() == RTL ? m_len : offset - m_start; // FIXME: Do we need to add rightBearing here? - return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride), + return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride), IntPoint(m_x, 0), 0, from, to)).right(); } diff --git a/WebCore/rendering/InlineTextBox.h b/WebCore/rendering/InlineTextBox.h index 0c2dd8a..222e973 100644 --- a/WebCore/rendering/InlineTextBox.h +++ b/WebCore/rendering/InlineTextBox.h @@ -24,7 +24,7 @@ #define InlineTextBox_h #include "InlineRunBox.h" -#include "RenderText.h" // so textObject() can be inline +#include "RenderText.h" // so textRenderer() can be inline namespace WebCore { @@ -50,6 +50,8 @@ public: InlineTextBox* nextTextBox() const { return static_cast<InlineTextBox*>(nextLineBox()); } InlineTextBox* prevTextBox() const { return static_cast<InlineTextBox*>(prevLineBox()); } + virtual int height() const; + unsigned start() const { return m_start; } unsigned end() const { return m_len ? m_start + m_len - 1 : m_start; } unsigned len() const { return m_len; } @@ -73,7 +75,7 @@ private: virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); public: - RenderText* textObject() const; + RenderText* textRenderer() const; private: virtual void deleteLine(RenderArena*); @@ -135,9 +137,9 @@ private: void paintTextMatchMarker(GraphicsContext*, int tx, int ty, DocumentMarker, RenderStyle*, const Font&); }; -inline RenderText* InlineTextBox::textObject() const +inline RenderText* InlineTextBox::textRenderer() const { - return toRenderText(m_object); + return toRenderText(renderer()); } } // namespace WebCore diff --git a/WebCore/rendering/LayoutState.cpp b/WebCore/rendering/LayoutState.cpp index 6c196ac..883f74d 100644 --- a/WebCore/rendering/LayoutState.cpp +++ b/WebCore/rendering/LayoutState.cpp @@ -27,6 +27,7 @@ #include "LayoutState.h" #include "RenderArena.h" +#include "RenderInline.h" #include "RenderLayer.h" #include "RenderView.h" @@ -52,8 +53,10 @@ LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const IntSize& if (renderer->hasLayer()) m_offset += renderer->layer()->relativePositionOffset(); } else if (renderer->isPositioned() && !fixed) { - if (RenderObject* container = renderer->container()) - m_offset += renderer->offsetForPositionedInContainer(container); + if (RenderObject* container = renderer->container()) { + if (container->isRelPositioned() && container->isRenderInline()) + m_offset += toRenderInline(container)->relativePositionedInlineOffset(renderer); + } } m_clipped = !fixed && prev->m_clipped; diff --git a/WebCore/rendering/ListMarkerBox.cpp b/WebCore/rendering/ListMarkerBox.cpp index 0455eae..6089ebd 100644 --- a/WebCore/rendering/ListMarkerBox.cpp +++ b/WebCore/rendering/ListMarkerBox.cpp @@ -39,7 +39,7 @@ ListMarkerBox::ListMarkerBox(RenderObject* obj) bool ListMarkerBox::isText() const { - return static_cast<RenderListMarker*>(object())->isText(); + return static_cast<RenderListMarker*>(renderer())->isText(); } } // namespace WebCore diff --git a/WebCore/rendering/MediaControlElements.cpp b/WebCore/rendering/MediaControlElements.cpp index 8ad2bd6..d84e9ad 100644 --- a/WebCore/rendering/MediaControlElements.cpp +++ b/WebCore/rendering/MediaControlElements.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -68,13 +68,22 @@ MediaControlShadowRootElement::MediaControlShadowRootElement(Document* doc, HTML setInDocument(true); } +void MediaControlShadowRootElement::updateStyle() +{ + if (renderer()) { + RenderStyle* timelineContainerStyle = m_mediaElement->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_TIMELINE_CONTAINER); + renderer()->setStyle(timelineContainerStyle); + } +} + // ---------------------------- -MediaTextDisplayElement::MediaTextDisplayElement(Document* doc, RenderStyle::PseudoId pseudo, HTMLMediaElement* mediaElement) +MediaTextDisplayElement::MediaTextDisplayElement(Document* doc, PseudoId pseudo, HTMLMediaElement* mediaElement) : HTMLDivElement(divTag, doc) , m_mediaElement(mediaElement) + , m_pseudoStyleId(pseudo) { - RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(pseudo); + RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId); RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style); if (renderer) { setRenderer(renderer); @@ -97,19 +106,28 @@ void MediaTextDisplayElement::update() renderer()->updateFromElement(); } +void MediaTextDisplayElement::updateStyle() +{ + if (renderer() && m_mediaElement->renderer()) { + RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId); + renderer()->setStyle(style); + } +} + MediaTimeDisplayElement::MediaTimeDisplayElement(Document* doc, HTMLMediaElement* element, bool currentTime) - : MediaTextDisplayElement(doc, currentTime ? RenderStyle::MEDIA_CONTROLS_CURRENT_TIME_DISPLAY : RenderStyle::MEDIA_CONTROLS_TIME_REMAINING_DISPLAY, element) + : MediaTextDisplayElement(doc, currentTime ? MEDIA_CONTROLS_CURRENT_TIME_DISPLAY : MEDIA_CONTROLS_TIME_REMAINING_DISPLAY, element) { } // ---------------------------- -MediaControlInputElement::MediaControlInputElement(Document* doc, RenderStyle::PseudoId pseudo, const String& type, HTMLMediaElement* mediaElement) +MediaControlInputElement::MediaControlInputElement(Document* doc, PseudoId pseudo, const String& type, HTMLMediaElement* mediaElement) : HTMLInputElement(inputTag, doc) , m_mediaElement(mediaElement) + , m_pseudoStyleId(pseudo) { setInputType(type); - RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(pseudo); + RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId); RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style); if (renderer) { setRenderer(renderer); @@ -131,6 +149,14 @@ void MediaControlInputElement::update() renderer()->updateFromElement(); } +void MediaControlInputElement::updateStyle() +{ + if (renderer() && m_mediaElement->renderer()) { + RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId); + renderer()->setStyle(style); + } +} + bool MediaControlInputElement::hitTest(const IntPoint& absPoint) { if (renderer() && renderer()->style()->hasAppearance()) @@ -142,7 +168,7 @@ bool MediaControlInputElement::hitTest(const IntPoint& absPoint) // ---------------------------- MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* doc, HTMLMediaElement* element) - : MediaControlInputElement(doc, RenderStyle::MEDIA_CONTROLS_MUTE_BUTTON, "button", element) + : MediaControlInputElement(doc, MEDIA_CONTROLS_MUTE_BUTTON, "button", element) { } @@ -158,18 +184,14 @@ void MediaControlMuteButtonElement::defaultEventHandler(Event* event) // ---------------------------- MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* doc, HTMLMediaElement* element) - : MediaControlInputElement(doc, RenderStyle::MEDIA_CONTROLS_PLAY_BUTTON, "button", element) + : MediaControlInputElement(doc, MEDIA_CONTROLS_PLAY_BUTTON, "button", element) { } void MediaControlPlayButtonElement::defaultEventHandler(Event* event) { if (event->type() == eventNames().clickEvent) { - ExceptionCode ec; - if (m_mediaElement->canPlay()) - m_mediaElement->play(ec); - else - m_mediaElement->pause(ec); + m_mediaElement->togglePlayState(); event->setDefaultHandled(); } HTMLInputElement::defaultEventHandler(event); @@ -178,7 +200,7 @@ void MediaControlPlayButtonElement::defaultEventHandler(Event* event) // ---------------------------- MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* doc, HTMLMediaElement* element, bool forward) - : MediaControlInputElement(doc, forward ? RenderStyle::MEDIA_CONTROLS_SEEK_FORWARD_BUTTON : RenderStyle::MEDIA_CONTROLS_SEEK_BACK_BUTTON, "button", element) + : MediaControlInputElement(doc, forward ? MEDIA_CONTROLS_SEEK_FORWARD_BUTTON : MEDIA_CONTROLS_SEEK_BACK_BUTTON, "button", element) , m_forward(forward) , m_seeking(false) , m_capturing(false) @@ -193,8 +215,7 @@ void MediaControlSeekButtonElement::defaultEventHandler(Event* event) m_capturing = true; frame->eventHandler()->setCapturingMouseEventsNode(this); } - ExceptionCode ec; - m_mediaElement->pause(ec); + m_mediaElement->pause(); m_seekTimer.startRepeating(cSeekRepeatDelay); event->setDefaultHandled(); } else if (event->type() == eventNames().mouseupEvent) { @@ -228,40 +249,34 @@ void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonE // ---------------------------- MediaControlTimelineElement::MediaControlTimelineElement(Document* doc, HTMLMediaElement* element) - : MediaControlInputElement(doc, RenderStyle::MEDIA_CONTROLS_TIMELINE, "range", element) + : MediaControlInputElement(doc, MEDIA_CONTROLS_TIMELINE, "range", element) { setAttribute(precisionAttr, "float"); } void MediaControlTimelineElement::defaultEventHandler(Event* event) { - RenderSlider* slider = static_cast<RenderSlider*>(renderer()); - bool oldInDragMode = slider && slider->inDragMode(); - float oldTime = narrowPrecisionToFloat(value().toDouble()); - bool oldEnded = m_mediaElement->ended(); + if (event->type() == eventNames().mousedownEvent) + m_mediaElement->beginScrubbing(); HTMLInputElement::defaultEventHandler(event); - float time = narrowPrecisionToFloat(value().toDouble()); - if (oldTime != time || event->type() == eventNames().inputEvent) { - ExceptionCode ec; - m_mediaElement->setCurrentTime(time, ec); + if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent ) { + return; } - // Media element stays in non-paused state when it reaches end. If the slider is now dragged - // to some other position the playback resumes which does not match usual media player UIs. - // Get the expected behavior by pausing explicitly in this case. - if (oldEnded && !m_mediaElement->ended() && !m_mediaElement->paused()) { + float time = narrowPrecisionToFloat(value().toDouble()); + if (time != m_mediaElement->currentTime()) { ExceptionCode ec; - m_mediaElement->pause(ec); + m_mediaElement->setCurrentTime(time, ec); } - // Pause playback during drag, but do it without using DOM API which would generate events - bool inDragMode = slider && slider->inDragMode(); - if (inDragMode != oldInDragMode) - m_mediaElement->setPausedInternal(inDragMode); - if (inDragMode) + RenderSlider* slider = static_cast<RenderSlider*>(renderer()); + if (slider && slider->inDragMode()) static_cast<RenderMedia*>(m_mediaElement->renderer())->updateTimeDisplay(); + + if (event->type() == eventNames().mouseupEvent) + m_mediaElement->endScrubbing(); } void MediaControlTimelineElement::update(bool updateDuration) @@ -276,7 +291,7 @@ void MediaControlTimelineElement::update(bool updateDuration) // ---------------------------- MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* doc, HTMLMediaElement* element) - : MediaControlInputElement(doc, RenderStyle::MEDIA_CONTROLS_FULLSCREEN_BUTTON, "button", element) + : MediaControlInputElement(doc, MEDIA_CONTROLS_FULLSCREEN_BUTTON, "button", element) { } diff --git a/WebCore/rendering/MediaControlElements.h b/WebCore/rendering/MediaControlElements.h index c1c9574..fa0fb30 100644 --- a/WebCore/rendering/MediaControlElements.h +++ b/WebCore/rendering/MediaControlElements.h @@ -56,6 +56,8 @@ public: virtual bool isShadowNode() const { return true; } virtual Node* shadowParentNode() { return m_mediaElement; } + + void updateStyle(); private: HTMLMediaElement* m_mediaElement; @@ -66,11 +68,13 @@ private: class MediaTextDisplayElement : public HTMLDivElement { public: - MediaTextDisplayElement(Document*, RenderStyle::PseudoId, HTMLMediaElement*); + MediaTextDisplayElement(Document*, PseudoId, HTMLMediaElement*); void attachToParent(Element*); void update(); + void updateStyle(); protected: HTMLMediaElement* m_mediaElement; + PseudoId m_pseudoStyleId; }; // ---------------------------- @@ -84,12 +88,14 @@ public: class MediaControlInputElement : public HTMLInputElement { public: - MediaControlInputElement(Document*, RenderStyle::PseudoId, const String& type, HTMLMediaElement*); + MediaControlInputElement(Document*, PseudoId, const String& type, HTMLMediaElement*); void attachToParent(Element*); void update(); + void updateStyle(); bool hitTest(const IntPoint& absPoint); protected: HTMLMediaElement* m_mediaElement; + PseudoId m_pseudoStyleId; }; // ---------------------------- diff --git a/WebCore/rendering/PointerEventsHitRules.h b/WebCore/rendering/PointerEventsHitRules.h index e2dae3b..3d8939a 100644 --- a/WebCore/rendering/PointerEventsHitRules.h +++ b/WebCore/rendering/PointerEventsHitRules.h @@ -22,7 +22,7 @@ #ifndef PointerEventsHitRules_h #define PointerEventsHitRules_h -#include "RenderStyle.h" +#include "RenderStyleConstants.h" namespace WebCore { diff --git a/WebCore/rendering/RenderBR.cpp b/WebCore/rendering/RenderBR.cpp index 2532c5b..f407099 100644 --- a/WebCore/rendering/RenderBR.cpp +++ b/WebCore/rendering/RenderBR.cpp @@ -40,15 +40,6 @@ RenderBR::~RenderBR() { } -InlineBox* RenderBR::createInlineBox(bool makePlaceholder, bool isRootLineBox, bool isOnlyRun) -{ - // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode - // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.) - InlineTextBox* box = static_cast<InlineTextBox*>(RenderText::createInlineBox(makePlaceholder, isRootLineBox, isOnlyRun)); - box->setIsText(isOnlyRun || document()->inStrictMode()); - return box; -} - int RenderBR::baselinePosition(bool firstLine, bool isRootLineBox) const { if (firstTextBox() && !firstTextBox()->isText()) @@ -82,7 +73,7 @@ int RenderBR::lineHeight(bool firstLine, bool /*isRootLineBox*/) const return m_lineHeight; } -void RenderBR::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderBR::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderText::styleDidChange(diff, oldStyle); m_lineHeight = -1; @@ -103,9 +94,9 @@ unsigned RenderBR::caretMaxRenderedOffset() const return 1; } -VisiblePosition RenderBR::positionForCoordinates(int /*x*/, int /*y*/) +VisiblePosition RenderBR::positionForPoint(const IntPoint&) { - return VisiblePosition(element(), 0, DOWNSTREAM); + return createVisiblePosition(0, DOWNSTREAM); } } // namespace WebCore diff --git a/WebCore/rendering/RenderBR.h b/WebCore/rendering/RenderBR.h index b65bb58..7eae8ea 100644 --- a/WebCore/rendering/RenderBR.h +++ b/WebCore/rendering/RenderBR.h @@ -40,7 +40,7 @@ public: virtual const char* renderName() const { return "RenderBR"; } - virtual IntRect selectionRect(bool) { return IntRect(); } + virtual IntRect selectionRectForRepaint(RenderBoxModelObject* /*repaintContainer*/, bool /*clipToVisibleContent*/) { return IntRect(); } virtual unsigned width(unsigned /*from*/, unsigned /*len*/, const Font&, int /*xpos*/) const { return 0; } virtual unsigned width(unsigned /*from*/, unsigned /*len*/, int /*xpos*/, bool /*firstLine = false*/) const { return 0; } @@ -49,18 +49,16 @@ public: virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; // overrides - virtual InlineBox* createInlineBox(bool, bool, bool isOnlyRun = false); - virtual bool isBR() const { return true; } virtual int caretMinOffset() const; virtual int caretMaxOffset() const; virtual unsigned caretMaxRenderedOffset() const; - virtual VisiblePosition positionForCoordinates(int x, int y); + virtual VisiblePosition positionForPoint(const IntPoint&); protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); private: mutable int m_lineHeight; diff --git a/WebCore/rendering/RenderBlock.cpp b/WebCore/rendering/RenderBlock.cpp index 725bdb6..5b45501 100644 --- a/WebCore/rendering/RenderBlock.cpp +++ b/WebCore/rendering/RenderBlock.cpp @@ -25,6 +25,7 @@ #include "Document.h" #include "Element.h" +#include "FloatQuad.h" #include "Frame.h" #include "FrameView.h" #include "GraphicsContext.h" @@ -58,6 +59,12 @@ const int verticalLineClickFudgeFactor= 3; using namespace HTMLNames; +static void moveChild(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* from, RenderObjectChildList* fromChildList, RenderObject* child) +{ + ASSERT(from == child->parent()); + toChildList->appendChildNode(to, fromChildList->removeChildNode(from, child, false), false); +} + struct ColumnInfo { ColumnInfo() : m_desiredColumnWidth(0) @@ -77,7 +84,7 @@ static PercentHeightDescendantsMap* gPercentHeightDescendantsMap = 0; typedef WTF::HashMap<const RenderBox*, HashSet<RenderBlock*>*> PercentHeightContainerMap; static PercentHeightContainerMap* gPercentHeightContainerMap = 0; -typedef WTF::HashMap<RenderBlock*, ListHashSet<RenderFlow*>*> ContinuationOutlineTableMap; +typedef WTF::HashMap<RenderBlock*, ListHashSet<RenderInline*>*> ContinuationOutlineTableMap; // Our MarginInfo state used when laying out block children. RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int top, int bottom) @@ -114,15 +121,18 @@ RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int top, int bottom) // ------------------------------------------------------------------------------------------------------- RenderBlock::RenderBlock(Node* node) - : RenderFlow(node) + : RenderBox(node) , m_floatingObjects(0) , m_positionedObjects(0) + , m_inlineContinuation(0) , m_maxMargin(0) , m_overflowHeight(0) , m_overflowWidth(0) , m_overflowLeft(0) , m_overflowTop(0) + , m_lineHeight(-1) { + setChildrenInline(true); } RenderBlock::~RenderBlock() @@ -131,7 +141,7 @@ RenderBlock::~RenderBlock() delete m_positionedObjects; delete m_maxMargin; - if (m_hasColumns) + if (hasColumns()) delete gColumnInfoMap->take(this); if (gPercentHeightDescendantsMap) { @@ -154,11 +164,49 @@ RenderBlock::~RenderBlock() } } -void RenderBlock::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +void RenderBlock::destroy() +{ + // Detach our continuation first. + if (m_inlineContinuation) + m_inlineContinuation->destroy(); + m_inlineContinuation = 0; + + // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will + // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. + children()->destroyLeftoverChildren(); + + if (!documentBeingDestroyed()) { + if (firstLineBox()) { + // We can't wait for RenderBox::destroy to clear the selection, + // because by then we will have nuked the line boxes. + // FIXME: The SelectionController should be responsible for this when it + // is notified of DOM mutations. + if (isSelectionBorder()) + view()->clearSelection(); + + // If we are an anonymous block, then our line boxes might have children + // that will outlast this block. In the non-anonymous block case those + // children will be destroyed by the time we return from this function. + if (isAnonymousBlock()) { + for (InlineFlowBox* box = firstLineBox(); box; box = box->nextFlowBox()) { + while (InlineBox* childBox = box->firstChild()) + childBox->remove(); + } + } + } else if (isInline() && parent()) + parent()->dirtyLinesFromChangedChild(this); + } + + m_lineBoxes.deleteLineBoxes(renderArena()); + + RenderBox::destroy(); +} + +void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { setReplaced(newStyle->isDisplayReplacedType()); - if (style() && parent() && diff == RenderStyle::Layout && style()->position() != newStyle->position()) { + if (style() && parent() && diff == StyleDifferenceLayout && style()->position() != newStyle->position()) { if (newStyle->position() == StaticPosition) // Clear our positioned objects list. Our absolutely positioned descendants will be // inserted into our containing block's positioned objects list during layout. @@ -176,16 +224,16 @@ void RenderBlock::styleWillChange(RenderStyle::Diff diff, const RenderStyle* new } if (cb->isRenderBlock()) - static_cast<RenderBlock*>(cb)->removePositionedObjects(this); + toRenderBlock(cb)->removePositionedObjects(this); } } - RenderFlow::styleWillChange(diff, newStyle); + RenderBox::styleWillChange(diff, newStyle); } -void RenderBlock::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { - RenderFlow::styleDidChange(diff, oldStyle); + RenderBox::styleDidChange(diff, oldStyle); // FIXME: We could save this call when the change only affected non-inherited properties for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { @@ -201,13 +249,21 @@ void RenderBlock::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldS // Update pseudos for :before and :after now. if (!isAnonymous() && document()->usesBeforeAfterRules() && canHaveChildren()) { - updateBeforeAfterContent(RenderStyle::BEFORE); - updateBeforeAfterContent(RenderStyle::AFTER); + updateBeforeAfterContent(BEFORE); + updateBeforeAfterContent(AFTER); } updateFirstLetter(); } -void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild) +void RenderBlock::updateBeforeAfterContent(PseudoId pseudoId) +{ + // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it. + if (parent() && parent()->createsAnonymousWrapper()) + return; + return children()->updateBeforeAfterContent(this, pseudoId); +} + +void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) { // Make sure we don't append things after :after-generated content if we have it. if (!beforeChild && isAfterContent(lastChild())) @@ -231,13 +287,13 @@ void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChi if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild) beforeChild->parent()->addChild(newChild, beforeChild); else - addChildToFlow(newChild, beforeChild->parent()); + addChild(newChild, beforeChild->parent()); return; } ASSERT(anonymousChild->isTable()); - if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP - || newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION + if ((newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) + || (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) || newChild->isTableSection() || newChild->isTableRow() || newChild->isTableCell()) { @@ -253,7 +309,7 @@ void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChi // A block has to either have all of its children inline, or all of its children as blocks. // So, if our children are currently inline and a block child has to be inserted, we move all our // inline children into anonymous block boxes. - if (m_childrenInline && !newChild->isInline() && !newChild->isFloatingOrPositioned()) { + if (childrenInline() && !newChild->isInline() && !newChild->isFloatingOrPositioned()) { // This is a block with inline content. Wrap the inline content in anonymous blocks. makeChildrenNonInline(beforeChild); madeBoxesNonInline = true; @@ -263,7 +319,7 @@ void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChi ASSERT(beforeChild->isAnonymousBlock()); ASSERT(beforeChild->parent() == this); } - } else if (!m_childrenInline && (newChild->isFloatingOrPositioned() || newChild->isInline())) { + } else if (!childrenInline() && (newChild->isFloatingOrPositioned() || newChild->isInline())) { // If we're inserting an inline child but all of our children are blocks, then we have to make sure // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise // a new one is created and inserted into our list of children in the appropriate position. @@ -277,17 +333,16 @@ void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChi if (newChild->isInline()) { // No suitable existing anonymous box - create a new one. RenderBlock* newBox = createAnonymousBlock(); - RenderContainer::addChild(newBox, beforeChild); + RenderBox::addChild(newBox, beforeChild); newBox->addChild(newChild); return; } } - RenderContainer::addChild(newChild, beforeChild); - // ### care about aligned stuff + RenderBox::addChild(newChild, beforeChild); - if (madeBoxesNonInline && parent() && isAnonymousBlock()) - parent()->removeLeftoverAnonymousBlock(this); + if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) + toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); // this object may be dead here } @@ -333,14 +388,19 @@ static void getInlineRun(RenderObject* start, RenderObject* boundary, void RenderBlock::deleteLineBoxTree() { - InlineFlowBox* line = m_firstLineBox; - InlineFlowBox* nextLine; - while (line) { - nextLine = line->nextFlowBox(); - line->deleteLine(renderArena()); - line = nextLine; - } - m_firstLineBox = m_lastLineBox = 0; + m_lineBoxes.deleteLineBoxTree(renderArena()); +} + +RootInlineBox* RenderBlock::createRootBox() +{ + return new (renderArena()) RootInlineBox(this); +} + +RootInlineBox* RenderBlock::createRootInlineBox() +{ + RootInlineBox* rootBox = createRootBox(); + m_lineBoxes.appendLineBox(rootBox); + return rootBox; } void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) @@ -355,7 +415,7 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) ASSERT(isInlineBlockOrInlineTable() || !isInline()); ASSERT(!insertionPoint || insertionPoint->parent() == this); - m_childrenInline = false; + setChildrenInline(false); RenderObject *child = firstChild(); if (!child) @@ -372,16 +432,16 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) child = inlineRunEnd->nextSibling(); - RenderBlock* box = createAnonymousBlock(); - insertChildNode(box, inlineRunStart); + RenderBlock* block = createAnonymousBlock(); + children()->insertChildNode(this, block, inlineRunStart); RenderObject* o = inlineRunStart; - while(o != inlineRunEnd) - { + while (o != inlineRunEnd) { RenderObject* no = o; o = no->nextSibling(); - box->moveChildNode(no); + + moveChild(block, block->children(), this, children(), no); } - box->moveChildNode(inlineRunEnd); + moveChild(block, block->children(), this, children(), inlineRunEnd); } #ifndef NDEBUG @@ -392,7 +452,49 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) repaint(); } -void RenderBlock::removeChild(RenderObject *oldChild) +void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) +{ + ASSERT(child->isAnonymousBlock()); + ASSERT(!child->childrenInline()); + + if (child->inlineContinuation()) + return; + + RenderObject* firstAnChild = child->m_children.firstChild(); + RenderObject* lastAnChild = child->m_children.lastChild(); + if (firstAnChild) { + RenderObject* o = firstAnChild; + while (o) { + o->setParent(this); + o = o->nextSibling(); + } + firstAnChild->setPreviousSibling(child->previousSibling()); + lastAnChild->setNextSibling(child->nextSibling()); + if (child->previousSibling()) + child->previousSibling()->setNextSibling(firstAnChild); + if (child->nextSibling()) + child->nextSibling()->setPreviousSibling(lastAnChild); + } else { + if (child->previousSibling()) + child->previousSibling()->setNextSibling(child->nextSibling()); + if (child->nextSibling()) + child->nextSibling()->setPreviousSibling(child->previousSibling()); + } + if (child == m_children.firstChild()) + m_children.setFirstChild(firstAnChild); + if (child == m_children.lastChild()) + m_children.setLastChild(lastAnChild); + child->setParent(0); + child->setPreviousSibling(0); + child->setNextSibling(0); + + child->children()->setFirstChild(0); + child->m_next = 0; + + child->destroy(); +} + +void RenderBlock::removeChild(RenderObject* oldChild) { // If this child is a block, and if our previous and next siblings are // both anonymous blocks with inline content, then we can go ahead and @@ -400,7 +502,7 @@ void RenderBlock::removeChild(RenderObject *oldChild) RenderObject* prev = oldChild->previousSibling(); RenderObject* next = oldChild->nextSibling(); bool canDeleteAnonymousBlocks = !documentBeingDestroyed() && !isInline() && !oldChild->isInline() && - !oldChild->virtualContinuation() && + (!oldChild->isRenderBlock() || !toRenderBlock(oldChild)->inlineContinuation()) && (!prev || (prev->isAnonymousBlock() && prev->childrenInline())) && (!next || (next->isAnonymousBlock() && next->childrenInline())); if (canDeleteAnonymousBlocks && prev && next) { @@ -408,20 +510,22 @@ void RenderBlock::removeChild(RenderObject *oldChild) // 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(); - prev->moveChildNode(no); + moveChild(prevBlock, prevBlock->children(), nextBlock, nextBlock->children(), no); } - RenderBlock* nextBlock = static_cast<RenderBlock*>(next); nextBlock->deleteLineBoxTree(); // Nuke the now-empty block. next->destroy(); } - RenderFlow::removeChild(oldChild); + RenderBox::removeChild(oldChild); RenderObject* child = prev ? prev : next; if (canDeleteAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && !isFlexibleBox()) { @@ -429,13 +533,13 @@ void RenderBlock::removeChild(RenderObject *oldChild) // box. We can go ahead and pull the content right back up into our // box. setNeedsLayoutAndPrefWidthsRecalc(); - RenderBlock* anonBlock = static_cast<RenderBlock*>(removeChildNode(child, false)); - m_childrenInline = true; + RenderBlock* anonBlock = toRenderBlock(children()->removeChildNode(this, child, false)); + setChildrenInline(true); RenderObject* o = anonBlock->firstChild(); while (o) { RenderObject* no = o; o = no->nextSibling(); - moveChildNode(no); + moveChild(this, children(), anonBlock, anonBlock->children(), no); } // Delete the now-empty block's lines and nuke it. @@ -615,15 +719,8 @@ void RenderBlock::layoutBlock(bool relayoutChildren) if (!relayoutChildren && layoutOnlyPositionedObjects()) return; - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = m_everHadLayout && checkForRepaintDuringLayout(); - if (checkForRepaint) { - oldBounds = absoluteClippedOverflowRect(); - oldOutlineBox = absoluteOutlineBounds(); - } - - LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), m_hasColumns || hasTransform() || hasReflection()); + LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout()); + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection()); int oldWidth = width(); int oldColumnWidth = desiredColumnWidth(); @@ -670,11 +767,11 @@ void RenderBlock::layoutBlock(bool relayoutChildren) if (!isCell) { initMaxMarginValues(); - m_topMarginQuirk = style()->marginTop().quirk(); - m_bottomMarginQuirk = style()->marginBottom().quirk(); + setTopMarginQuirk(style()->marginTop().quirk()); + setBottomMarginQuirk(style()->marginBottom().quirk()); - Node* node = element(); - if (node && node->hasTagName(formTag) && static_cast<HTMLFormElement*>(node)->isMalformed()) { + Node* n = node(); + if (n && n->hasTagName(formTag) && static_cast<HTMLFormElement*>(n)->isMalformed()) { // See if this form is malformed (i.e., unclosed). If so, don't give the form // a bottom margin. setMaxBottomMargins(0, 0); @@ -684,9 +781,9 @@ void RenderBlock::layoutBlock(bool relayoutChildren) // For overflow:scroll blocks, ensure we have both scrollbars in place always. if (scrollsOverflow()) { if (style()->overflowX() == OSCROLL) - m_layer->setHasHorizontalScrollbar(true); + layer()->setHasHorizontalScrollbar(true); if (style()->overflowY() == OSCROLL) - m_layer->setHasVerticalScrollbar(true); + layer()->setHasVerticalScrollbar(true); } int repaintTop = 0; @@ -714,7 +811,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren) // One of our children's floats may have become an overhanging float for us. We need to look for it. for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (child->isBlockFlow() && !child->isFloatingOrPositioned()) { - RenderBlock* block = static_cast<RenderBlock*>(child); + RenderBlock* block = toRenderBlock(child); if (block->floatBottom() + block->y() > height()) addOverhangingFloats(block, -block->x(), -block->y(), false); } @@ -762,12 +859,10 @@ void RenderBlock::layoutBlock(bool relayoutChildren) // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if // we overflow or not. if (hasOverflowClip()) - m_layer->updateScrollInfoAfterLayout(); + layer()->updateScrollInfoAfterLayout(); // Repaint with our new bounds if they are different from our old bounds. - bool didFullRepaint = false; - if (checkForRepaint) - didFullRepaint = repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + bool didFullRepaint = repainter.repaintAfterLayout(); if (!didFullRepaint && repaintTop != repaintBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) { IntRect repaintRect(m_overflowLeft, repaintTop, m_overflowWidth - m_overflowLeft, repaintBottom - repaintTop); @@ -800,19 +895,19 @@ void RenderBlock::layoutBlock(bool relayoutChildren) bool RenderBlock::expandsToEncloseOverhangingFloats() const { - return isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBox()) || m_hasColumns || isTableCell() || isFieldset(); + return isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBox()) || hasColumns() || isTableCell() || isFieldset(); } void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marginInfo) { - if (child->hasStaticX()) { + if (child->style()->hasStaticX()) { if (style()->direction() == LTR) - child->setStaticX(borderLeft() + paddingLeft()); + child->layer()->setStaticX(borderLeft() + paddingLeft()); else - child->setStaticX(borderRight() + paddingRight()); + child->layer()->setStaticX(borderRight() + paddingRight()); } - if (child->hasStaticY()) { + if (child->style()->hasStaticY()) { int y = height(); if (!marginInfo.canCollapseWithTop()) { child->calcVerticalMargins(); @@ -828,7 +923,7 @@ void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marg } y += (collapsedTopPos - collapsedTopNeg) - marginTop; } - child->setStaticY(y); + child->layer()->setStaticY(y); } } @@ -892,24 +987,27 @@ RenderBox* RenderBlock::handleFloatingChild(RenderBox* child, const MarginInfo& return 0; } -RenderBox* RenderBlock::handleRunInChild(RenderBox* blockRunIn, bool& handled) +RenderBox* RenderBlock::handleRunInChild(RenderBox* child, bool& handled) { // See if we have a run-in element with inline children. If the // children aren't inline, then just treat the run-in as a normal // block. - if (blockRunIn->isRunIn() && (blockRunIn->childrenInline() || blockRunIn->isReplaced())) { + if (child->isRunIn() && (child->childrenInline() || child->isReplaced())) { + RenderBlock* blockRunIn = toRenderBlock(child); // Get the next non-positioned/non-floating RenderBlock. RenderObject* curr = blockRunIn->nextSibling(); while (curr && curr->isFloatingOrPositioned()) curr = curr->nextSibling(); if (curr && (curr->isRenderBlock() && curr->childrenInline() && !curr->isRunIn())) { + RenderBlock* currBlock = toRenderBlock(curr); + // The block acts like an inline, so just null out its // position. handled = true; // Remove the old child. RenderBox* next = blockRunIn->nextSiblingBox(); - removeChildNode(blockRunIn); + children()->removeChildNode(this, blockRunIn); // Create an inline. RenderInline* inlineRunIn = new (renderArena()) RenderInline(blockRunIn->node()); @@ -918,18 +1016,18 @@ RenderBox* RenderBlock::handleRunInChild(RenderBox* blockRunIn, bool& handled) // Move the nodes from the old child to the new child, but skip any :before/:after content. It has already // been regenerated by the new inline. for (RenderObject* runInChild = blockRunIn->firstChild(); runInChild; runInChild = runInChild->nextSibling()) { - if (runInChild->style()->styleType() != RenderStyle::BEFORE && runInChild->style()->styleType() != RenderStyle::AFTER) { - blockRunIn->removeChildNode(runInChild, false); + if (runInChild->style()->styleType() != BEFORE && runInChild->style()->styleType() != AFTER) { + blockRunIn->children()->removeChildNode(blockRunIn, runInChild, false); inlineRunIn->addChild(runInChild); // Use addChild instead of appendChildNode since it handles correct placement of the children relative to :after-generated content. } } - // Now insert the new child under |curr|. - curr->insertChildNode(inlineRunIn, curr->firstChild()); + // Now insert the new child under |currBlock|. + currBlock->children()->insertChildNode(currBlock, inlineRunIn, currBlock->firstChild()); // If the run-in had an element, we need to set the new renderer. - if (blockRunIn->element()) - blockRunIn->element()->setRenderer(inlineRunIn); + if (blockRunIn->node()) + blockRunIn->node()->setRenderer(inlineRunIn); // Destroy the block run-in. blockRunIn->destroy(); @@ -940,7 +1038,7 @@ RenderBox* RenderBlock::handleRunInChild(RenderBox* blockRunIn, bool& handled) return 0; } -void RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo, int yPosEstimate) +int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) { // Get our max pos and neg top margins. int posTop = child->maxTopMargin(true); @@ -969,7 +1067,7 @@ void RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo, int // has an example of this, a <dt> with 0.8em author-specified inside // a <dl> inside a <td>. if (!marginInfo.determinedTopQuirk() && !topQuirk && (posTop-negTop)) { - m_topMarginQuirk = false; + setTopMarginQuirk(false); marginInfo.setDeterminedTopQuirk(true); } @@ -979,7 +1077,7 @@ void RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo, int // This deals with the <td><div><p> case. // Don't do this for a block that split two inlines though. You do // still apply margins in this case. - m_topMarginQuirk = true; + setTopMarginQuirk(true); } if (marginInfo.quirkContainer() && marginInfo.atTopOfBlock() && (posTop - negTop)) @@ -1028,34 +1126,15 @@ void RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo, int marginInfo.setSelfCollapsingBlockClearedFloat(false); } - - view()->addLayoutDelta(IntSize(0, yPosEstimate - ypos)); - child->setLocation(child->x(), ypos); - if (ypos != yPosEstimate) { - if (child->shrinkToAvoidFloats()) - // The child's width depends on the line width. - // When the child shifts to clear an item, its width can - // change (because it has more available line width). - // So go ahead and mark the item as dirty. - child->setChildNeedsLayout(true, false); - - if (!child->avoidsFloats() && child->containsFloats()) - static_cast<RenderBlock*>(child)->markAllDescendantsWithFloatsForLayout(); - - // Our guess was wrong. Make the child lay itself out again. - child->layoutIfNeeded(); - } + + return ypos; } -void RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin) +int RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin, int yPos) { - int heightIncrease = getClearDelta(child); + int heightIncrease = getClearDelta(child, yPos); if (!heightIncrease) - return; - - // The child needs to be lowered. Move the child so that it just clears the float. - view()->addLayoutDelta(IntSize(0, -heightIncrease)); - child->setLocation(child->x(), child->y() + heightIncrease); + return yPos; if (child->isSelfCollapsingBlock()) { // For self-collapsing blocks that clear, they can still collapse their @@ -1084,19 +1163,8 @@ void RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, setMaxTopMargins(oldTopPosMargin, oldTopNegMargin); marginInfo.setAtTopOfBlock(false); } - - // If our value of clear caused us to be repositioned vertically to be - // underneath a float, we might have to do another layout to take into account - // the extra space we now have available. - if (child->shrinkToAvoidFloats()) - // The child's width depends on the line width. - // When the child shifts to clear an item, its width can - // change (because it has more available line width). - // So go ahead and mark the item as dirty. - child->setChildNeedsLayout(true, false); - if (!child->avoidsFloats() && child->containsFloats()) - static_cast<RenderBlock*>(child)->markAllDescendantsWithFloatsForLayout(); - child->layoutIfNeeded(); + + return yPos + heightIncrease; } int RenderBlock::estimateVerticalPosition(RenderBox* child, const MarginInfo& marginInfo) @@ -1108,6 +1176,7 @@ int RenderBlock::estimateVerticalPosition(RenderBox* child, const MarginInfo& ma int childMarginTop = child->selfNeedsLayout() ? child->marginTop() : child->collapsedMarginTop(); yPosEstimate += max(marginInfo.margin(), childMarginTop); } + yPosEstimate += getClearDelta(child, yPosEstimate); return yPosEstimate; } @@ -1122,7 +1191,7 @@ void RenderBlock::determineHorizontalPosition(RenderBox* child) // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need // to shift over as necessary to dodge any floats that might get in the way. if (child->avoidsFloats()) { - int leftOff = leftOffset(height()); + int leftOff = leftOffset(height(), false); if (style()->textAlign() != WEBKIT_CENTER && child->style()->marginLeft().type() != Auto) { if (child->marginLeft() < 0) leftOff += child->marginLeft(); @@ -1134,7 +1203,7 @@ void RenderBlock::determineHorizontalPosition(RenderBox* child) // width computation will take into account the delta between |leftOff| and |xPos| // so that we can just pass the content width in directly to the |calcHorizontalMargins| // function. - child->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->y())); + child->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->y(), false)); chPos = leftOff + child->marginLeft(); } } @@ -1144,7 +1213,7 @@ void RenderBlock::determineHorizontalPosition(RenderBox* child) int xPos = width() - borderRight() - paddingRight() - verticalScrollbarWidth(); int chPos = xPos - (child->width() + child->marginRight()); if (child->avoidsFloats()) { - int rightOff = rightOffset(height()); + int rightOff = rightOffset(height(), false); if (style()->textAlign() != WEBKIT_CENTER && child->style()->marginRight().type() != Auto) { if (child->marginRight() < 0) rightOff -= child->marginRight(); @@ -1155,7 +1224,7 @@ void RenderBlock::determineHorizontalPosition(RenderBox* child) // width computation will take into account the delta between |rightOff| and |xPos| // so that we can just pass the content width in directly to the |calcHorizontalMargins| // function. - toRenderBox(child)->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->y())); + child->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->y(), false)); chPos = rightOff - child->marginRight() - child->width(); } } @@ -1172,13 +1241,13 @@ void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) setMaxBottomMargins(max(maxBottomPosMargin(), marginInfo.posMargin()), max(maxBottomNegMargin(), marginInfo.negMargin())); if (!marginInfo.bottomQuirk()) - m_bottomMarginQuirk = false; + setBottomMarginQuirk(false); if (marginInfo.bottomQuirk() && marginBottom() == 0) // We have no bottom margin and our last child has a quirky margin. // We will pick up this quirky margin and pass it through. // This deals with the <td><div><p> case. - m_bottomMarginQuirk = true; + setBottomMarginQuirk(true); } } @@ -1304,21 +1373,22 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom child->setLocation(child->x(), yPosEstimate); bool markDescendantsWithFloats = false; - if (yPosEstimate != oldRect.y() && !child->avoidsFloats() && child->containsFloats()) + if (yPosEstimate != oldRect.y() && !child->avoidsFloats() && child->isBlockFlow() && toRenderBlock(child)->containsFloats()) markDescendantsWithFloats = true; else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) { // If an element might be affected by the presence of floats, then always mark it for // layout. int fb = max(previousFloatBottom, floatBottom()); - if (fb > height() || fb > yPosEstimate) + if (fb > yPosEstimate) markDescendantsWithFloats = true; } - if (markDescendantsWithFloats) - static_cast<RenderBlock*>(child)->markAllDescendantsWithFloatsForLayout(); + if (child->isRenderBlock()) { + if (markDescendantsWithFloats) + toRenderBlock(child)->markAllDescendantsWithFloatsForLayout(); - if (child->isRenderBlock()) - previousFloatBottom = max(previousFloatBottom, oldRect.y() + static_cast<RenderBlock*>(child)->floatBottom()); + previousFloatBottom = max(previousFloatBottom, oldRect.y() + toRenderBlock(child)->floatBottom()); + } bool childHadLayout = child->m_everHadLayout; bool childNeededLayout = child->needsLayout(); @@ -1327,10 +1397,28 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom // Now determine the correct ypos based off examination of collapsing margin // values. - collapseMargins(child, marginInfo, yPosEstimate); + int yBeforeClear = collapseMargins(child, marginInfo); // Now check for clear. - clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin); + int yAfterClear = clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin, yBeforeClear); + + view()->addLayoutDelta(IntSize(0, yPosEstimate - yAfterClear)); + child->setLocation(child->x(), yAfterClear); + + // Now we have a final y position. See if it really does end up being different from our estimate. + if (yAfterClear != yPosEstimate) { + if (child->shrinkToAvoidFloats()) { + // The child's width depends on the line width. + // When the child shifts to clear an item, its width can + // change (because it has more available line width). + // So go ahead and mark the item as dirty. + child->setChildNeedsLayout(true, false); + } + if (!child->avoidsFloats() && child->isBlockFlow() && toRenderBlock(child)->containsFloats()) + toRenderBlock(child)->markAllDescendantsWithFloatsForLayout(); + // Our guess was wrong. Make the child lay itself out again. + child->layoutIfNeeded(); + } // We are no longer at the top of the block if we encounter a non-empty child. // This has to be done after checking for clear, so that margins can be reset if a clear occurred. @@ -1348,7 +1436,8 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom } // If the child has overhanging floats that intrude into following siblings (or possibly out // of this block), then the parent gets notified of the floats now. - maxFloatBottom = max(maxFloatBottom, addOverhangingFloats(static_cast<RenderBlock*>(child), -child->x(), -child->y(), !childNeededLayout)); + if (child->isBlockFlow() && toRenderBlock(child)->containsFloats()) + maxFloatBottom = max(maxFloatBottom, addOverhangingFloats(toRenderBlock(child), -child->x(), -child->y(), !childNeededLayout)); // Update our overflow in case the child spills out the block. m_overflowTop = min(m_overflowTop, child->y() + child->overflowTop(false)); @@ -1384,7 +1473,7 @@ bool RenderBlock::layoutOnlyPositionedObjects() if (!posChildNeedsLayout() || normalChildNeedsLayout() || selfNeedsLayout()) return false; - LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), m_hasColumns || hasTransform() || hasReflection()); + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection()); if (needsPositionedMovementLayout()) { tryLayoutDoingPositionedMovementOnly(); @@ -1398,7 +1487,7 @@ bool RenderBlock::layoutOnlyPositionedObjects() statePusher.pop(); if (hasOverflowClip()) - m_layer->updateScrollInfoAfterLayout(); + layer()->updateScrollInfoAfterLayout(); #ifdef ANDROID_FIX // iframe flatten will call FrameView::layout() which calls performPostLayoutTasks, @@ -1422,11 +1511,11 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren) // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is // positioned explicitly) this should not incur a performance penalty. - if (relayoutChildren || (r->hasStaticY() && r->parent() != this && r->parent()->isBlockFlow())) + if (relayoutChildren || (r->style()->hasStaticY() && r->parent() != this && r->parent()->isBlockFlow())) r->setChildNeedsLayout(true, false); // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. - if (relayoutChildren && (r->style()->paddingLeft().isPercent() || r->style()->paddingRight().isPercent())) + //if (relayoutChildren && (r->style()->paddingLeft().isPercent() || r->style()->paddingRight().isPercent())) r->setPrefWidthsDirty(true, false); // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width @@ -1470,7 +1559,7 @@ void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) // Only repaint the object if it is overhanging, is not in its own layer, and // is our responsibility to paint (m_shouldPaint is set). When paintAllDescendants is true, the latter // condition is replaced with being a descendant of us. - if (r->m_bottom > height() && (paintAllDescendants && r->m_renderer->isDescendantOf(this) || r->m_shouldPaint) && !r->m_renderer->hasLayer()) { + if (r->m_bottom > height() && ((paintAllDescendants && r->m_renderer->isDescendantOf(this)) || r->m_shouldPaint) && !r->m_renderer->hasSelfPaintingLayer()) { r->m_renderer->repaint(); r->m_renderer->repaintOverhangingFloats(); } @@ -1478,7 +1567,7 @@ void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) view()->enableLayoutState(); } } - + void RenderBlock::paint(PaintInfo& paintInfo, int tx, int ty) { tx += x(); @@ -1497,52 +1586,69 @@ void RenderBlock::paint(PaintInfo& paintInfo, int tx, int ty) return; } - bool useControlClip = phase != PaintPhaseBlockBackground && phase != PaintPhaseSelfOutline && phase != PaintPhaseMask && hasControlClip(); + bool pushedClip = pushContentsClip(paintInfo, tx, ty); + paintObject(paintInfo, tx, ty); + if (pushedClip) + popContentsClip(paintInfo, phase, tx, ty); - // Push a clip. - if (useControlClip) { - if (phase == PaintPhaseOutline) - paintInfo.phase = PaintPhaseChildOutlines; - else if (phase == PaintPhaseChildBlockBackground) { - paintInfo.phase = PaintPhaseBlockBackground; - paintObject(paintInfo, tx, ty); - paintInfo.phase = PaintPhaseChildBlockBackgrounds; - } - IntRect clipRect(controlClipRect(tx, ty)); - if (clipRect.isEmpty()) - return; - paintInfo.context->save(); - paintInfo.context->clip(clipRect); - } + // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with + // z-index. We paint after we painted the background/border, so that the scrollbars will + // sit above the background/border. + if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground)) + layer()->paintOverflowControls(paintInfo.context, tx, ty, paintInfo.rect); +} - paintObject(paintInfo, tx, ty); - - // Pop the clip. - if (useControlClip) { - paintInfo.context->restore(); - if (phase == PaintPhaseOutline) { - paintInfo.phase = PaintPhaseSelfOutline; - paintObject(paintInfo, tx, ty); - paintInfo.phase = phase; - } else if (phase == PaintPhaseChildBlockBackground) - paintInfo.phase = phase; +void RenderBlock::paintColumnRules(PaintInfo& paintInfo, int tx, int ty) +{ + const Color& ruleColor = style()->columnRuleColor(); + bool ruleTransparent = style()->columnRuleIsTransparent(); + EBorderStyle ruleStyle = style()->columnRuleStyle(); + int ruleWidth = style()->columnRuleWidth(); + int colGap = columnGap(); + bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent && ruleWidth <= colGap; + if (!renderRule) + return; + + // We need to do multiple passes, breaking up our child painting into strips. + int currXOffset = 0; + int ruleAdd = borderLeft() + paddingLeft(); + int ruleX = 0; + Vector<IntRect>* colRects = columnRects(); + unsigned colCount = colRects->size(); + for (unsigned i = 0; i < colCount; i++) { + // For each rect, we clip to the rect, and then we adjust our coords. + IntRect colRect = colRects->at(i); + + // Move to the next position. + if (style()->direction() == LTR) { + ruleX += colRect.width() + colGap / 2; + currXOffset += colRect.width() + colGap; + } else { + ruleX -= (colRect.width() + colGap / 2); + currXOffset -= (colRect.width() + colGap); + } + + // Now paint the column rule. + if (i < colCount - 1) { + int ruleStart = tx + ruleX - ruleWidth / 2 + ruleAdd; + int ruleEnd = ruleStart + ruleWidth; + int ruleTop = ty + borderTop() + paddingTop(); + int ruleBottom = ruleTop + contentHeight(); + drawLineForBoxSide(paintInfo.context, ruleStart, ruleTop, ruleEnd, ruleBottom, + style()->direction() == LTR ? BSLeft : BSRight, ruleColor, style()->color(), ruleStyle, 0, 0); + } + + ruleX = currXOffset; } } -void RenderBlock::paintColumns(PaintInfo& paintInfo, int tx, int ty, bool paintingFloats) +void RenderBlock::paintColumnContents(PaintInfo& paintInfo, int tx, int ty, bool paintingFloats) { // We need to do multiple passes, breaking up our child painting into strips. GraphicsContext* context = paintInfo.context; int currXOffset = 0; int currYOffset = 0; - int ruleAdd = borderLeft() + paddingLeft(); - int ruleX = 0; int colGap = columnGap(); - const Color& ruleColor = style()->columnRuleColor(); - bool ruleTransparent = style()->columnRuleIsTransparent(); - EBorderStyle ruleStyle = style()->columnRuleStyle(); - int ruleWidth = style()->columnRuleWidth(); - bool renderRule = !paintingFloats && ruleStyle > BHIDDEN && !ruleTransparent && ruleWidth <= colGap; Vector<IntRect>* colRects = columnRects(); unsigned colCount = colRects->size(); for (unsigned i = 0; i < colCount; i++) { @@ -1568,27 +1674,14 @@ void RenderBlock::paintColumns(PaintInfo& paintInfo, int tx, int ty, bool painti paintContents(info, finalX, finalY); // Move to the next position. - if (style()->direction() == LTR) { - ruleX += colRect.width() + colGap / 2; + if (style()->direction() == LTR) currXOffset += colRect.width() + colGap; - } else { - ruleX -= (colRect.width() + colGap / 2); + else currXOffset -= (colRect.width() + colGap); - } - + currYOffset -= colRect.height(); context->restore(); - - // Now paint the column rule. - if (renderRule && paintInfo.phase == PaintPhaseForeground && i < colCount - 1) { - int ruleStart = ruleX - ruleWidth / 2 + ruleAdd; - int ruleEnd = ruleStart + ruleWidth; - drawBorder(paintInfo.context, tx + ruleStart, ty + borderTop() + paddingTop(), tx + ruleEnd, ty + borderTop() + paddingTop() + contentHeight(), - style()->direction() == LTR ? BSLeft : BSRight, ruleColor, style()->color(), ruleStyle, 0, 0); - } - - ruleX = currXOffset; } } @@ -1601,7 +1694,7 @@ void RenderBlock::paintContents(PaintInfo& paintInfo, int tx, int ty) return; if (childrenInline()) - paintLines(paintInfo, tx, ty); + m_lineBoxes.paint(this, paintInfo, tx, ty); else paintChildren(paintInfo, tx, ty); } @@ -1626,7 +1719,7 @@ void RenderBlock::paintChildren(PaintInfo& paintInfo, int tx, int ty) return; } - if (!child->hasLayer() && !child->isFloating()) + if (!child->hasSelfPaintingLayer() && !child->isFloating()) child->paint(info, tx, ty); // Check for page-break-after: always, and if it's set, break and bail. @@ -1662,9 +1755,11 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, int tx, int ty) PaintPhase paintPhase = paintInfo.phase; // 1. paint background, borders etc - if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && - hasBoxDecorations() && style()->visibility() == VISIBLE) { - paintBoxDecorations(paintInfo, tx, ty); + if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style()->visibility() == VISIBLE) { + if (hasBoxDecorations()) + paintBoxDecorations(paintInfo, tx, ty); + if (hasColumns()) + paintColumnRules(paintInfo, tx, ty); } if (paintPhase == PaintPhaseMask && style()->visibility() == VISIBLE) { @@ -1680,12 +1775,12 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, int tx, int ty) int scrolledX = tx; int scrolledY = ty; if (hasOverflowClip()) - m_layer->subtractScrolledContentOffset(scrolledX, scrolledY); + layer()->subtractScrolledContentOffset(scrolledX, scrolledY); // 2. paint contents if (paintPhase != PaintPhaseSelfOutline) { - if (m_hasColumns) - paintColumns(paintInfo, scrolledX, scrolledY); + if (hasColumns()) + paintColumnContents(paintInfo, scrolledX, scrolledY); else paintContents(paintInfo, scrolledX, scrolledY); } @@ -1693,30 +1788,30 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, int tx, int ty) // 3. paint selection // FIXME: Make this work with multi column layouts. For now don't fill gaps. bool isPrinting = document()->printing(); - if (!isPrinting && !m_hasColumns) + if (!isPrinting && !hasColumns()) paintSelection(paintInfo, scrolledX, scrolledY); // Fill in gaps in selection on lines and between blocks. // 4. paint floats. if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) { - if (m_hasColumns) - paintColumns(paintInfo, scrolledX, scrolledY, true); + if (hasColumns()) + paintColumnContents(paintInfo, scrolledX, scrolledY, true); else paintFloats(paintInfo, scrolledX, scrolledY, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); } // 5. paint outline. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) - RenderBox::paintOutline(paintInfo.context, tx, ty, width(), height(), style()); + paintOutline(paintInfo.context, tx, ty, width(), height(), style()); // 6. paint continuation outlines. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) { - if (continuation() && continuation()->hasOutline() && continuation()->style()->visibility() == VISIBLE) { - RenderFlow* inlineFlow = static_cast<RenderFlow*>(continuation()->element()->renderer()); - if (!inlineFlow->hasLayer()) - containingBlock()->addContinuationWithOutline(inlineFlow); - else if (!inlineFlow->firstLineBox()) - inlineFlow->paintOutline(paintInfo.context, tx - x() + inlineFlow->containingBlock()->x(), - ty - y() + inlineFlow->containingBlock()->y()); + if (inlineContinuation() && inlineContinuation()->hasOutline() && inlineContinuation()->style()->visibility() == VISIBLE) { + RenderInline* inlineRenderer = toRenderInline(inlineContinuation()->node()->renderer()); + if (!inlineRenderer->hasSelfPaintingLayer()) + containingBlock()->addContinuationWithOutline(inlineRenderer); + else if (!inlineRenderer->firstLineBox()) + inlineRenderer->paintOutline(paintInfo.context, tx - x() + inlineRenderer->containingBlock()->x(), + ty - y() + inlineRenderer->containingBlock()->y()); } paintContinuationOutlines(paintInfo, tx, ty); } @@ -1739,7 +1834,7 @@ void RenderBlock::paintFloats(PaintInfo& paintInfo, int tx, int ty, bool preserv DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for (; (r = it.current()); ++it) { // Only paint the object if our m_shouldPaint flag is set. - if (r->m_shouldPaint && !r->m_renderer->hasLayer()) { + if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) { PaintInfo currentPaintInfo(paintInfo); currentPaintInfo.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; int currentTX = tx + r->m_left - r->m_renderer->x() + r->m_renderer->marginLeft(); @@ -1767,8 +1862,8 @@ void RenderBlock::paintEllipsisBoxes(PaintInfo& paintInfo, int tx, int ty) if (style()->visibility() == VISIBLE && paintInfo.phase == PaintPhaseForeground) { // We can check the first box and last box and avoid painting if we don't // intersect. - int yPos = ty + firstLineBox()->yPos(); - int h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos(); + int yPos = ty + firstLineBox()->y(); + int h = lastLineBox()->y() + lastLineBox()->height() - firstLineBox()->y(); if (yPos >= paintInfo.rect.bottom() || yPos + h <= paintInfo.rect.y()) return; @@ -1776,7 +1871,7 @@ void RenderBlock::paintEllipsisBoxes(PaintInfo& paintInfo, int tx, int ty) // them. Note that boxes can easily overlap, so we can't make any assumptions // based off positions of our first line box or our last line box. for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { - yPos = ty + curr->yPos(); + yPos = ty + curr->y(); h = curr->height(); if (curr->ellipsisBox() && yPos < paintInfo.rect.bottom() && yPos + h > paintInfo.rect.y()) curr->paintEllipsisBox(paintInfo, tx, ty); @@ -1790,16 +1885,16 @@ static ContinuationOutlineTableMap* continuationOutlineTable() return &table; } -void RenderBlock::addContinuationWithOutline(RenderFlow* flow) +void RenderBlock::addContinuationWithOutline(RenderInline* flow) { // We can't make this work if the inline is in a layer. We'll just rely on the broken // way of painting. - ASSERT(!flow->layer()); + ASSERT(!flow->layer() && !flow->isInlineContinuation()); ContinuationOutlineTableMap* table = continuationOutlineTable(); - ListHashSet<RenderFlow*>* continuations = table->get(this); + ListHashSet<RenderInline*>* continuations = table->get(this); if (!continuations) { - continuations = new ListHashSet<RenderFlow*>; + continuations = new ListHashSet<RenderInline*>; table->set(this, continuations); } @@ -1812,15 +1907,15 @@ void RenderBlock::paintContinuationOutlines(PaintInfo& info, int tx, int ty) if (table->isEmpty()) return; - ListHashSet<RenderFlow*>* continuations = table->get(this); + ListHashSet<RenderInline*>* continuations = table->get(this); if (!continuations) return; // Paint each continuation outline. - ListHashSet<RenderFlow*>::iterator end = continuations->end(); - for (ListHashSet<RenderFlow*>::iterator it = continuations->begin(); it != end; ++it) { + ListHashSet<RenderInline*>::iterator end = continuations->end(); + for (ListHashSet<RenderInline*>::iterator it = continuations->begin(); it != end; ++it) { // Need to add in the coordinates of the intervening blocks. - RenderFlow* flow = *it; + RenderInline* flow = *it; RenderBlock* block = flow->containingBlock(); for ( ; block && block != this; block = block->containingBlock()) { tx += block->x(); @@ -1845,9 +1940,9 @@ void RenderBlock::setSelectionState(SelectionState s) if ((s == SelectionStart && selectionState() == SelectionEnd) || (s == SelectionEnd && selectionState() == SelectionStart)) - m_selectionState = SelectionBoth; + RenderBox::setSelectionState(SelectionBoth); else - m_selectionState = s; + RenderBox::setSelectionState(s); RenderBlock* cb = containingBlock(); if (cb && !cb->isRenderView()) @@ -1856,12 +1951,12 @@ void RenderBlock::setSelectionState(SelectionState s) bool RenderBlock::shouldPaintSelectionGaps() const { - return m_selectionState != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot(); + return selectionState() != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot(); } bool RenderBlock::isSelectionRoot() const { - if (!element()) + if (!node()) return false; // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases. @@ -1874,22 +1969,22 @@ bool RenderBlock::isSelectionRoot() const return true; if (view() && view()->selectionStart()) { - Node* startElement = view()->selectionStart()->element(); - if (startElement && startElement->rootEditableElement() == element()) + Node* startElement = view()->selectionStart()->node(); + if (startElement && startElement->rootEditableElement() == node()) return true; } return false; } -GapRects RenderBlock::selectionGapRects() +GapRects RenderBlock::selectionGapRectsForRepaint(RenderBoxModelObject* /*repaintContainer*/) { ASSERT(!needsLayout()); if (!shouldPaintSelectionGaps()) return GapRects(); - // FIXME: this is broken with transforms + // FIXME: this is broken with transforms and a non-null repaintContainer FloatPoint absContentPoint = localToAbsolute(FloatPoint()); if (hasOverflowClip()) absContentPoint -= layer()->scrolledContentOffset(); @@ -1952,7 +2047,7 @@ GapRects RenderBlock::fillSelectionGaps(RenderBlock* rootBlock, int blockX, int if (!isBlockFlow()) // FIXME: Make multi-column selection gap filling work someday. return result; - if (m_hasColumns || hasTransform()) { + if (hasColumns() || hasTransform()) { // FIXME: We should learn how to gap fill multiple columns and transforms eventually. lastTop = (ty - blockY) + height(); lastLeft = leftSelectionOffset(rootBlock, height()); @@ -1966,7 +2061,7 @@ GapRects RenderBlock::fillSelectionGaps(RenderBlock* rootBlock, int blockX, int result = fillBlockSelectionGaps(rootBlock, blockX, blockY, tx, ty, lastTop, lastLeft, lastRight, paintInfo); // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. - if (rootBlock == this && (m_selectionState != SelectionBoth && m_selectionState != SelectionEnd)) + if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, ty + height(), rootBlock, blockX, blockY, paintInfo)); return result; @@ -2004,14 +2099,14 @@ GapRects RenderBlock::fillInlineSelectionGaps(RenderBlock* rootBlock, int blockX result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, ty + selTop, rootBlock, blockX, blockY, paintInfo)); - if (!paintInfo || ty + selTop < paintInfo->rect.bottom() && ty + selTop + selHeight > paintInfo->rect.y()) + if (!paintInfo || (ty + selTop < paintInfo->rect.bottom() && ty + selTop + selHeight > paintInfo->rect.y())) result.unite(curr->fillLineSelectionGap(selTop, selHeight, rootBlock, blockX, blockY, tx, ty, paintInfo)); lastSelectedLine = curr; } if (containsStart && !lastSelectedLine) - // Selection must start just after our last line. + // VisibleSelection must start just after our last line. lastSelectedLine = lastRootBox(); if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) { @@ -2079,7 +2174,7 @@ GapRects RenderBlock::fillBlockSelectionGaps(RenderBlock* rootBlock, int blockX, lastRight = rightSelectionOffset(rootBlock, curr->y() + curr->height()); } else if (childState != SelectionNone) // We must be a block that has some selected object inside it. Go ahead and recur. - result.unite(static_cast<RenderBlock*>(curr)->fillSelectionGaps(rootBlock, blockX, blockY, tx + curr->x(), ty + curr->y(), + result.unite(toRenderBlock(curr)->fillSelectionGaps(rootBlock, blockX, blockY, tx + curr->x(), ty + curr->y(), lastTop, lastLeft, lastRight, paintInfo)); } return result; @@ -2161,7 +2256,7 @@ void RenderBlock::getHorizontalSelectionGapInfo(SelectionState state, bool& left int RenderBlock::leftSelectionOffset(RenderBlock* rootBlock, int yPos) { - int left = leftOffset(yPos); + int left = leftOffset(yPos, false); if (left == borderLeft() + paddingLeft()) { if (rootBlock != this) // The border can potentially be further extended by our containingBlock(). @@ -2181,7 +2276,7 @@ int RenderBlock::leftSelectionOffset(RenderBlock* rootBlock, int yPos) int RenderBlock::rightSelectionOffset(RenderBlock* rootBlock, int yPos) { - int right = rightOffset(yPos); + int right = rightOffset(yPos, false); if (right == (contentWidth() + (borderLeft() + paddingLeft()))) { if (rootBlock != this) // The border can potentially be further extended by our containingBlock(). @@ -2273,7 +2368,7 @@ void RenderBlock::insertFloatingObject(RenderBox* o) newObj->m_top = -1; newObj->m_bottom = -1; newObj->m_width = o->width() + o->marginLeft() + o->marginRight(); - newObj->m_shouldPaint = !o->hasLayer(); // If a layer exists, the float will paint itself. Otherwise someone else will. + newObj->m_shouldPaint = !o->hasSelfPaintingLayer(); // If a layer exists, the float will paint itself. Otherwise someone else will. newObj->m_isDescendant = true; newObj->m_renderer = o; @@ -2458,15 +2553,17 @@ void RenderBlock::removePercentHeightDescendant(RenderBox* descendant) delete containerSet; } -int -RenderBlock::leftOffset() const +HashSet<RenderBox*>* RenderBlock::percentHeightDescendants() const { - return borderLeft()+paddingLeft(); + return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0; } -int -RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, - int *heightRemaining ) const +int RenderBlock::leftOffset() const +{ + return borderLeft() + paddingLeft(); +} + +int RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, int* heightRemaining) const { int left = fixedOffset; if (m_floatingObjects) { @@ -2484,7 +2581,7 @@ RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, } } - if (applyTextIndent && m_firstLine && style()->direction() == LTR) { + if (applyTextIndent && style()->direction() == LTR) { int cw = 0; if (style()->textIndent().isPercent()) cw = containingBlock()->availableWidth(); @@ -2494,15 +2591,12 @@ RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, return left; } -int -RenderBlock::rightOffset() const +int RenderBlock::rightOffset() const { return borderLeft() + paddingLeft() + availableWidth(); } -int -RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, - int *heightRemaining ) const +int RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, int* heightRemaining) const { int right = fixedOffset; @@ -2521,7 +2615,7 @@ RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, } } - if (applyTextIndent && m_firstLine && style()->direction() == RTL) { + if (applyTextIndent && style()->direction() == RTL) { int cw = 0; if (style()->textIndent().isPercent()) cw = containingBlock()->availableWidth(); @@ -2532,9 +2626,9 @@ RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, } int -RenderBlock::lineWidth(int y) const +RenderBlock::lineWidth(int y, bool firstLine) const { - int result = rightOffset(y) - leftOffset(y); + int result = rightOffset(y, firstLine) - leftOffset(y, firstLine); return (result < 0) ? 0 : result; } @@ -2575,7 +2669,7 @@ IntRect RenderBlock::floatRect() const FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for (; (r = it.current()); ++it) { - if (r->m_shouldPaint && !r->m_renderer->hasLayer()) { + if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) { IntRect childRect = r->m_renderer->overflowRect(false); childRect.move(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop()); result.unite(childRect); @@ -2597,8 +2691,10 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to // the abs div. for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { - if (!c->isFloatingOrPositioned() && !c->isText() && !c->isRenderInline()) - bottom = max(bottom, toRenderBox(c)->y() + c->lowestPosition(false)); + if (!c->isFloatingOrPositioned() && c->isBox()) { + RenderBox* childBox = toRenderBox(c); + bottom = max(bottom, childBox->y() + childBox->lowestPosition(false)); + } } } @@ -2631,7 +2727,7 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) } } - if (m_hasColumns) { + if (hasColumns()) { Vector<IntRect>* colRects = columnRects(); for (unsigned i = 0; i < colRects->size(); i++) bottom = max(bottom, colRects->at(i).bottom() + relativeOffset); @@ -2642,16 +2738,30 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it ) { - if (r->m_shouldPaint || r->m_renderer->hasLayer()) { + if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { int lp = r->m_top + r->m_renderer->marginTop() + r->m_renderer->lowestPosition(false); bottom = max(bottom, lp + relativeOffset); } } } - if (!includeSelf && lastLineBox()) { - int lp = lastLineBox()->yPos() + lastLineBox()->height(); - bottom = max(bottom, lp); + if (!includeSelf) { + bottom = max(bottom, borderTop() + paddingTop() + paddingBottom()); + if (childrenInline()) { + if (lastLineBox()) { + int childBottomEdge = lastLineBox()->y() + lastLineBox()->height(); + bottom = max(bottom, childBottomEdge + paddingBottom()); + } + } else { + // Find the last normal flow child. + RenderBox* currBox = lastChildBox(); + while (currBox && currBox->isFloatingOrPositioned()) + currBox = currBox->previousSiblingBox(); + if (currBox) { + int childBottomEdge = currBox->y() + currBox->height() + currBox->collapsedMarginBottom(); + bottom = max(bottom, childBottomEdge + paddingBottom()); + } + } } return bottom; @@ -2670,8 +2780,10 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to // the abs div. for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { - if (!c->isFloatingOrPositioned() && c->isBox() && !c->isRenderInline()) - right = max(right, toRenderBox(c)->x() + c->rightmostPosition(false)); + if (!c->isFloatingOrPositioned() && c->isBox()) { + RenderBox* childBox = toRenderBox(c); + right = max(right, childBox->x() + childBox->rightmostPosition(false)); + } } } @@ -2705,7 +2817,7 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel } } - if (m_hasColumns) { + if (hasColumns()) { // This only matters for LTR if (style()->direction() == LTR) right = max(columnRects()->last().right() + relativeOffset, right); @@ -2716,21 +2828,33 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it ) { - if (r->m_shouldPaint || r->m_renderer->hasLayer()) { + if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { int rp = r->m_left + r->m_renderer->marginLeft() + r->m_renderer->rightmostPosition(false); right = max(right, rp + relativeOffset); } } } - if (!includeSelf && firstLineBox()) { - for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) { - int rp = currBox->xPos() + currBox->width(); - // If this node is a root editable element, then the rightmostPosition should account for a caret at the end. - // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to. - if (node()->isContentEditable() && node() == node()->rootEditableElement() && style()->direction() == LTR) - rp += 1; - right = max(right, rp); + if (!includeSelf) { + right = max(right, borderLeft() + paddingLeft() + paddingRight()); + if (childrenInline()) { + for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) { + int childRightEdge = currBox->x() + currBox->width(); + + // If this node is a root editable element, then the rightmostPosition should account for a caret at the end. + // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to. + if (node() && node()->isContentEditable() && node() == node()->rootEditableElement() && style()->direction() == LTR && !paddingRight()) + childRightEdge += 1; + right = max(right, childRightEdge + paddingRight()); + } + } else { + // Walk all normal flow children. + for (RenderBox* currBox = firstChildBox(); currBox; currBox = currBox->nextSiblingBox()) { + if (currBox->isFloatingOrPositioned()) + continue; + int childRightEdge = currBox->x() + currBox->width() + currBox->marginRight(); + right = max(right, childRightEdge + paddingRight()); + } } } @@ -2749,8 +2873,10 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to // the abs div. for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { - if (!c->isFloatingOrPositioned() && c->isBox() && !c->isRenderInline()) - left = min(left, toRenderBox(c)->x() + c->leftmostPosition(false)); + if (!c->isFloatingOrPositioned() && c->isBox()) { + RenderBox* childBox = toRenderBox(c); + left = min(left, childBox->x() + childBox->leftmostPosition(false)); + } } } @@ -2784,7 +2910,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf } } - if (m_hasColumns) { + if (hasColumns()) { // This only matters for RTL if (style()->direction() == RTL) left = min(columnRects()->last().x() + relativeOffset, left); @@ -2795,7 +2921,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it ) { - if (r->m_shouldPaint || r->m_renderer->hasLayer()) { + if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { int lp = r->m_left + r->m_renderer->marginLeft() + r->m_renderer->leftmostPosition(false); left = min(left, lp + relativeOffset); } @@ -2804,7 +2930,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf if (!includeSelf && firstLineBox()) { for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) - left = min(left, (int)currBox->xPos()); + left = min(left, (int)currBox->x()); } return left; @@ -2882,7 +3008,7 @@ void RenderBlock::clearFloats() // to avoid floats. bool parentHasFloats = false; RenderObject* prev = previousSibling(); - while (prev && (!prev->isBox() || !prev->isRenderBlock() || prev->avoidsFloats() || prev->isFloatingOrPositioned())) { + while (prev && (prev->isFloatingOrPositioned() || !prev->isBox() || !prev->isRenderBlock() || toRenderBlock(prev)->avoidsFloats())) { if (prev->isFloating()) parentHasFloats = true; prev = prev->previousSibling(); @@ -2891,7 +3017,7 @@ void RenderBlock::clearFloats() // First add in floats from the parent. int offset = y(); if (parentHasFloats) { - RenderBlock* parentBlock = static_cast<RenderBlock *>(parent()); + RenderBlock* parentBlock = toRenderBlock(parent()); addIntrudingFloats(parentBlock, parentBlock->borderLeft() + parentBlock->paddingLeft(), offset); } @@ -2907,7 +3033,7 @@ void RenderBlock::clearFloats() if (!prev || !prev->isRenderBlock()) return; - RenderBlock* block = static_cast<RenderBlock *>(prev); + RenderBlock* block = toRenderBlock(prev); if (block->m_floatingObjects && block->floatBottom() > offset) addIntrudingFloats(block, xoffset, offset); @@ -2978,8 +3104,8 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bo // The nearest enclosing layer always paints the float (so that zindex and stacking // behaves properly). We always want to propagate the desire to paint the float as // far out as we can, to the outermost block that overlaps the float, stopping only - // if we hit a layer boundary. - if (r->m_renderer->enclosingLayer() == enclosingLayer()) + // if we hit a self-painting layer boundary. + if (r->m_renderer->enclosingSelfPaintingLayer() == enclosingSelfPaintingLayer()) r->m_shouldPaint = false; else floatingObj->m_shouldPaint = false; @@ -2991,7 +3117,8 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bo } m_floatingObjects->append(floatingObj); } - } else if (makeChildPaintOtherFloats && !r->m_shouldPaint && !r->m_renderer->hasLayer() && r->m_renderer->isDescendantOf(child) && r->m_renderer->enclosingLayer() == child->enclosingLayer()) + } else if (makeChildPaintOtherFloats && !r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer() && + r->m_renderer->isDescendantOf(child) && r->m_renderer->enclosingLayer() == child->enclosingLayer()) // The float is not overhanging from this block, so if it is a descendant of the child, the child should // paint it (the other case is that it is intruding into the child), unless it has its own layer or enclosing // layer. @@ -2999,7 +3126,7 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bo // it should paint. r->m_shouldPaint = true; - if (r->m_shouldPaint && !r->m_renderer->hasLayer()) { + if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) { IntRect floatOverflowRect = r->m_renderer->overflowRect(false); floatOverflowRect.move(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop()); floatsOverflowRect.unite(floatOverflowRect); @@ -3059,7 +3186,7 @@ void RenderBlock::addIntrudingFloats(RenderBlock* prev, int xoff, int yoff) bool RenderBlock::avoidsFloats() const { // Floats can't intrude into our box if we have a non-auto column count or width. - return RenderFlow::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth(); + return RenderBox::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth(); } bool RenderBlock::containsFloat(RenderObject* o) @@ -3085,14 +3212,16 @@ void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove // Iterate over our children and mark them as needed. if (!childrenInline()) { for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (child->isRenderBlock() && !child->isFloatingOrPositioned() && - ((floatToRemove ? child->containsFloat(floatToRemove) : child->containsFloats()) || child->shrinkToAvoidFloats())) - static_cast<RenderBlock*>(child)->markAllDescendantsWithFloatsForLayout(floatToRemove, inLayout); + if ((!floatToRemove && child->isFloatingOrPositioned()) || !child->isRenderBlock()) + continue; + RenderBlock* childBlock = toRenderBlock(child); + if ((floatToRemove ? childBlock->containsFloat(floatToRemove) : childBlock->containsFloats()) || childBlock->shrinkToAvoidFloats()) + childBlock->markAllDescendantsWithFloatsForLayout(floatToRemove, inLayout); } } } -int RenderBlock::getClearDelta(RenderBox* child) +int RenderBlock::getClearDelta(RenderBox* child, int yPos) { // There is no need to compute clearance if we have no floats. if (!containsFloats()) @@ -3120,11 +3249,11 @@ int RenderBlock::getClearDelta(RenderBox* child) // to fit) and not all (we should be using nextFloatBottomBelow and looping). // Do not allow tables to wrap in quirks or even in almost strict mode // (ebay on the PLT, finance.yahoo.com in the real world, versiontracker.com forces even almost strict mode not to work) - int result = clearSet ? max(0, bottom - child->y()) : 0; + int result = clearSet ? max(0, bottom - yPos) : 0; if (!result && child->avoidsFloats() && child->style()->width().isFixed() && - child->minPrefWidth() > lineWidth(child->y()) && child->minPrefWidth() <= availableWidth() && + child->minPrefWidth() > lineWidth(yPos, false) && child->minPrefWidth() <= availableWidth() && document()->inStrictMode()) - result = max(0, floatBottom() - child->y()); + result = max(0, floatBottom() - yPos); return result; } @@ -3159,41 +3288,41 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu return false; } - if (isPointInOverflowControl(result, _x, _y, tx, ty)) { - if (hitTestAction == HitTestBlockBackground) { - updateHitTestResult(result, IntPoint(_x - tx, _y - ty)); - return true; - } - return false; + if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, _x, _y, tx, ty)) { + updateHitTestResult(result, IntPoint(_x - tx, _y - ty)); + return true; } - // If we have lightweight control clipping, then we can't have any spillout. - if (!hasControlClip() || controlClipRect(tx, ty).contains(_x, _y)) { + // If we have clipping, then we can't have any spillout. + bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); + bool useClip = (hasControlClip() || useOverflowClip); + bool checkChildren = !useClip || (hasControlClip() ? controlClipRect(tx, ty).contains(_x, _y) : overflowClipRect(tx, ty).contains(_x, _y)); + if (checkChildren) { // Hit test descendants first. int scrolledX = tx; int scrolledY = ty; if (hasOverflowClip()) - m_layer->subtractScrolledContentOffset(scrolledX, scrolledY); + layer()->subtractScrolledContentOffset(scrolledX, scrolledY); // Hit test contents if we don't have columns. - if (!m_hasColumns && hitTestContents(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) + if (!hasColumns() && hitTestContents(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) return true; // Hit test our columns if we do have them. - if (m_hasColumns && hitTestColumns(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) + if (hasColumns() && hitTestColumns(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) return true; // Hit test floats. if (hitTestAction == HitTestFloat && m_floatingObjects) { if (isRenderView()) { - scrolledX += static_cast<RenderView*>(this)->frameView()->scrollX(); - scrolledY += static_cast<RenderView*>(this)->frameView()->scrollY(); + scrolledX += toRenderView(this)->frameView()->scrollX(); + scrolledY += toRenderView(this)->frameView()->scrollY(); } FloatingObject* o; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for (it.toLast(); (o = it.current()); --it) { - if (o->m_shouldPaint && !o->m_renderer->hasLayer()) { + if (o->m_shouldPaint && !o->m_renderer->hasSelfPaintingLayer()) { int xoffset = scrolledX + o->m_left + o->m_renderer->marginLeft() - o->m_renderer->x(); int yoffset = scrolledY + o->m_top + o->m_renderer->marginTop() - o->m_renderer->y(); if (o->m_renderer->hitTest(request, result, IntPoint(_x, _y), xoffset, yoffset)) { @@ -3255,7 +3384,7 @@ bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& { if (childrenInline() && !isTable()) { // We have to hit-test our line boxes. - if (hitTestLines(request, result, x, y, tx, ty, hitTestAction)) { + if (m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction)) { updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } @@ -3264,10 +3393,8 @@ bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& HitTestAction childHitTest = hitTestAction; if (hitTestAction == HitTestChildBlockBackgrounds) childHitTest = HitTestChildBlockBackground; - for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { - // FIXME: We have to skip over inline flows, since they can show up inside RenderTables at the moment (a demoted inline <form> for example). If we ever implement a - // table-specific hit-test method (which we should do for performance reasons anyway), then we can remove this check. - if (!child->hasLayer() && !child->isFloating() && !child->isRenderInline() && child->nodeAtPoint(request, result, x, y, tx, ty, childHitTest)) { + for (RenderBox* child = lastChildBox(); child; child = child->previousSiblingBox()) { + if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, x, y, tx, ty, childHitTest)) { updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } @@ -3282,156 +3409,153 @@ Position RenderBlock::positionForBox(InlineBox *box, bool start) const if (!box) return Position(); - if (!box->object()->element()) - return Position(element(), start ? caretMinOffset() : caretMaxOffset()); + if (!box->renderer()->node()) + return Position(node(), start ? caretMinOffset() : caretMaxOffset()); if (!box->isInlineTextBox()) - return Position(box->object()->element(), start ? box->object()->caretMinOffset() : box->object()->caretMaxOffset()); + return Position(box->renderer()->node(), start ? box->renderer()->caretMinOffset() : box->renderer()->caretMaxOffset()); InlineTextBox *textBox = static_cast<InlineTextBox *>(box); - return Position(box->object()->element(), start ? textBox->start() : textBox->start() + textBox->len()); + return Position(box->renderer()->node(), start ? textBox->start() : textBox->start() + textBox->len()); } Position RenderBlock::positionForRenderer(RenderObject* renderer, bool start) const { if (!renderer) - return Position(element(), 0); + return Position(node(), 0); - Node* node = renderer->element() ? renderer->element() : element(); - if (!node) + Node* n = renderer->node() ? renderer->node() : node(); + if (!n) return Position(); - ASSERT(renderer == node->renderer()); + ASSERT(renderer == n->renderer()); int offset = start ? renderer->caretMinOffset() : renderer->caretMaxOffset(); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now. - ASSERT(!node->isCharacterDataNode() || renderer->isText()); + ASSERT(!n->isCharacterDataNode() || renderer->isText()); - return Position(node, offset); + return Position(n, offset); } -VisiblePosition RenderBlock::positionForCoordinates(int x, int y) +// FIXME: This function should go on RenderObject as an instance method. Then +// all cases in which positionForCoordinates recurs could call this instead to +// prevent crossing editable boundaries. This would require many tests. +static VisiblePosition positionForPointRespectingEditingBoundaries(RenderBox* parent, RenderBox* child, const IntPoint& parentCoords) { - if (isTable()) - return RenderFlow::positionForCoordinates(x, y); + int xInChildCoords = parentCoords.x() - child->x(); + int yInChildCoords = parentCoords.y() - child->y(); - int top = borderTop(); - int bottom = top + paddingTop() + contentHeight() + paddingBottom(); + // If this is an anonymous renderer, we just recur normally + Node* childNode = child->node(); + if (!childNode) + return child->positionForCoordinates(xInChildCoords, yInChildCoords); - int left = borderLeft(); - int right = left + paddingLeft() + contentWidth() + paddingRight(); + // Otherwise, first make sure that the editability of the parent and child agree. + // If they don't agree, then we return a visible position just before or after the child + RenderObject* ancestor = parent; + while (ancestor && !ancestor->node()) + ancestor = ancestor->parent(); - Node* n = element(); - - int contentsX = x; - int contentsY = y; - offsetForContents(contentsX, contentsY); + // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal + if (!ancestor || ancestor->node()->isContentEditable() == childNode->isContentEditable()) + return child->positionForCoordinates(xInChildCoords, yInChildCoords); - if (isReplaced()) { - if (y < 0 || y < height() && x < 0) - return VisiblePosition(n, caretMinOffset(), DOWNSTREAM); - if (y >= height() || y >= 0 && x >= width()) - return VisiblePosition(n, caretMaxOffset(), DOWNSTREAM); - } + // Otherwise return before or after the child, depending on if the click was left or right of the child + int childMidX = child->width() / 2; + if (xInChildCoords < childMidX) + return ancestor->createVisiblePosition(childNode->nodeIndex(), DOWNSTREAM); + return ancestor->createVisiblePosition(childNode->nodeIndex() + 1, UPSTREAM); +} - // If we start inside the shadow tree, we will stay inside (even if the point is above or below). - if (!(n && n->isShadowNode()) && !childrenInline()) { - // Don't return positions inside editable roots for coordinates outside those roots, except for coordinates outside - // a document that is entirely editable. - bool isEditableRoot = n && n->rootEditableElement() == n && !n->hasTagName(bodyTag) && !n->hasTagName(htmlTag); - - if (y < top || (isEditableRoot && (y < bottom && x < left))) { - if (!isEditableRoot) - if (RenderBox* c = firstChildBox()) { // FIXME: This code doesn't make any sense. This child could be an inline or a positioned element or a float, etc. - VisiblePosition p = c->positionForCoordinates(contentsX - c->x(), contentsY - c->y()); - if (p.isNotNull()) - return p; - } - if (n) { - if (Node* sp = n->shadowParentNode()) - n = sp; - if (Node* p = n->parent()) - return VisiblePosition(p, n->nodeIndex(), DOWNSTREAM); - } - return VisiblePosition(n, 0, DOWNSTREAM); - } +static VisiblePosition positionForPointWithInlineChildren(RenderBlock* block, const IntPoint& pointInContents) +{ + ASSERT(block->childrenInline()); - if (y >= bottom || (isEditableRoot && (y >= top && x >= right))) { - if (!isEditableRoot) - if (RenderBox* c = lastChildBox()) { // FIXME: This code doesn't make any sense. This child could be an inline or a positioned element or a float, etc. - VisiblePosition p = c->positionForCoordinates(contentsX - c->x(), contentsY - c->y()); - if (p.isNotNull()) - return p; - } - if (n) { - if (Node* sp = n->shadowParentNode()) - n = sp; - if (Node* p = n->parent()) - return VisiblePosition(p, n->nodeIndex() + 1, DOWNSTREAM); - } - return VisiblePosition(n, 0, DOWNSTREAM); + if (!block->firstRootBox()) + return block->createVisiblePosition(0, DOWNSTREAM); + + InlineBox* closestBox = 0; + // look for the closest line box in the root box which is at the passed-in y coordinate + for (RootInlineBox* root = block->firstRootBox(); root; root = root->nextRootBox()) { + int bottom; + // set the bottom based on whether there is a next root box + if (root->nextRootBox()) + // FIXME: make the break point halfway between the bottom of the previous root box and the top of the next root box + bottom = root->nextRootBox()->topOverflow(); + else + bottom = root->bottomOverflow() + verticalLineClickFudgeFactor; + // check if this root line box is located at this y coordinate + if (pointInContents.y() < bottom && root->firstChild()) { + closestBox = root->closestLeafChildForXPos(pointInContents.x()); + if (closestBox) + // pass the box a y position that is inside it + break; } } - if (childrenInline()) { - if (!firstRootBox()) - return VisiblePosition(n, 0, DOWNSTREAM); + // y coordinate is below last root line box, pretend we hit it + if (!closestBox) + closestBox = block->lastRootBox()->closestLeafChildForXPos(pointInContents.x()); - if (contentsY < firstRootBox()->topOverflow() - verticalLineClickFudgeFactor) - // y coordinate is above first root line box - return VisiblePosition(positionForBox(firstRootBox()->firstLeafChild(), true), DOWNSTREAM); - - // look for the closest line box in the root box which is at the passed-in y coordinate - for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { - // set the bottom based on whether there is a next root box - if (root->nextRootBox()) - // FIXME: make the break point halfway between the bottom of the previous root box and the top of the next root box - bottom = root->nextRootBox()->topOverflow(); - else - bottom = root->bottomOverflow() + verticalLineClickFudgeFactor; - // check if this root line box is located at this y coordinate - if (contentsY < bottom && root->firstChild()) { - InlineBox* closestBox = root->closestLeafChildForXPos(x); - if (closestBox) - // pass the box a y position that is inside it - return closestBox->object()->positionForCoordinates(contentsX, closestBox->m_y); - } - } + if (closestBox) { + // pass the box a y position that is inside it + return closestBox->renderer()->positionForCoordinates(pointInContents.x(), closestBox->m_y); + } + + // Can't reach this. We have a root line box, but it has no kids. + // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text + // seems to hit this codepath. + return block->createVisiblePosition(0, DOWNSTREAM); +} + +VisiblePosition RenderBlock::positionForPoint(const IntPoint& point) +{ + if (isTable()) + return RenderBox::positionForPoint(point); + + int contentsX = point.x(); + int contentsY = point.y(); + offsetForContents(contentsX, contentsY); + IntPoint pointInContents(contentsX, contentsY); - if (lastRootBox()) - // y coordinate is below last root line box - return VisiblePosition(positionForBox(lastRootBox()->lastLeafChild(), false), DOWNSTREAM); + if (isReplaced()) { + if (point.y() < 0 || (point.y() < height() && point.x() < 0)) + return createVisiblePosition(caretMinOffset(), DOWNSTREAM); + if (point.y() >= height() || (point.y() >= 0 && point.x() >= width())) + return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); + } - return VisiblePosition(n, 0, DOWNSTREAM); + if (childrenInline()) { + return positionForPointWithInlineChildren(this, pointInContents); } - - // See if any child blocks exist at this y coordinate. + + // Check top/bottom child-margin/parent-padding for clicks and place them in the first/last child + // FIXME: This will not correctly handle first or last children being positioned or non-visible if (firstChildBox() && contentsY < firstChildBox()->y()) - return VisiblePosition(n, 0, DOWNSTREAM); - for (RenderBox* renderer = firstChildBox(); renderer; renderer = renderer->nextSiblingBox()) { - if (renderer->height() == 0 || renderer->style()->visibility() != VISIBLE || renderer->isFloatingOrPositioned()) + return positionForPointRespectingEditingBoundaries(this, firstChildBox(), pointInContents); + if (lastChildBox() && contentsY > lastChildBox()->y()) + return positionForPointRespectingEditingBoundaries(this, lastChildBox(), pointInContents); + + for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { + if (childBox->height() == 0 || childBox->style()->visibility() != VISIBLE || childBox->isFloatingOrPositioned()) continue; - RenderBox* next = renderer->nextSiblingBox(); - while (next && next->isFloatingOrPositioned()) - next = next->nextSiblingBox(); - if (next) - bottom = next->y(); - else - bottom = top + scrollHeight(); - if (contentsY >= renderer->y() && contentsY < bottom) - return renderer->positionForCoordinates(contentsX - renderer->x(), contentsY - renderer->y()); + // We hit this child if our click was above the bottom of its padding box (like IE6/7 and FF3) + if (contentsY < childBox->y() + childBox->height()) + return positionForPointRespectingEditingBoundaries(this, childBox, pointInContents); } - - return RenderFlow::positionForCoordinates(x, y); + + // We only get here if there are no, or only floated/positioned, or only + // non-visible block children below the click. + return RenderBox::positionForPoint(point); } void RenderBlock::offsetForContents(int& tx, int& ty) const { if (hasOverflowClip()) - m_layer->addScrolledContentOffset(tx, ty); + layer()->addScrolledContentOffset(tx, ty); - if (m_hasColumns) { + if (hasColumns()) { IntPoint contentsPoint(tx, ty); adjustPointToColumnContents(contentsPoint); tx = contentsPoint.x(); @@ -3442,7 +3566,7 @@ void RenderBlock::offsetForContents(int& tx, int& ty) const int RenderBlock::availableWidth() const { // If we have multiple columns, then the available width is reduced to our column width. - if (m_hasColumns) + if (hasColumns()) return desiredColumnWidth(); return contentWidth(); } @@ -3500,20 +3624,20 @@ void RenderBlock::calcColumnWidth() void RenderBlock::setDesiredColumnCountAndWidth(int count, int width) { if (count == 1) { - if (m_hasColumns) { + if (hasColumns()) { delete gColumnInfoMap->take(this); - m_hasColumns = false; + setHasColumns(false); } } else { ColumnInfo* info; - if (m_hasColumns) + if (hasColumns()) info = gColumnInfoMap->get(this); else { if (!gColumnInfoMap) gColumnInfoMap = new ColumnInfoMap; info = new ColumnInfo; gColumnInfoMap->add(this, info); - m_hasColumns = true; + setHasColumns(true); } info->m_desiredColumnCount = count; info->m_desiredColumnWidth = width; @@ -3522,21 +3646,21 @@ void RenderBlock::setDesiredColumnCountAndWidth(int count, int width) int RenderBlock::desiredColumnWidth() const { - if (!m_hasColumns) + if (!hasColumns()) return contentWidth(); return gColumnInfoMap->get(this)->m_desiredColumnWidth; } unsigned RenderBlock::desiredColumnCount() const { - if (!m_hasColumns) + if (!hasColumns()) return 1; return gColumnInfoMap->get(this)->m_desiredColumnCount; } Vector<IntRect>* RenderBlock::columnRects() const { - if (!m_hasColumns) + if (!hasColumns()) return 0; return &gColumnInfoMap->get(this)->m_columnRects; } @@ -3544,7 +3668,7 @@ Vector<IntRect>* RenderBlock::columnRects() const int RenderBlock::layoutColumns(int endOfContent) { // Don't do anything if we have no columns - if (!m_hasColumns) + if (!hasColumns()) return -1; ColumnInfo* info = gColumnInfoMap->get(this); @@ -3596,9 +3720,9 @@ int RenderBlock::layoutColumns(int endOfContent) GraphicsContext context((PlatformGraphicsContext*)0); RenderObject::PaintInfo paintInfo(&context, pageRect, PaintPhaseForeground, false, 0, 0); - m_hasColumns = false; + setHasColumns(false); paintObject(paintInfo, 0, 0); - m_hasColumns = true; + setHasColumns(true); int adjustedBottom = v->bestTruncatedAt(); if (adjustedBottom <= currY) @@ -3656,7 +3780,7 @@ int RenderBlock::layoutColumns(int endOfContent) void RenderBlock::adjustPointToColumnContents(IntPoint& point) const { // Just bail if we have no columns. - if (!m_hasColumns) + if (!hasColumns()) return; Vector<IntRect>* colRects = columnRects(); @@ -3685,7 +3809,7 @@ void RenderBlock::adjustPointToColumnContents(IntPoint& point) const void RenderBlock::adjustRectForColumns(IntRect& r) const { // Just bail if we have no columns. - if (!m_hasColumns) + if (!hasColumns()) return; Vector<IntRect>* colRects = columnRects(); @@ -3742,7 +3866,7 @@ void RenderBlock::calcPrefWidths() m_minPrefWidth = m_maxPrefWidth; // A horizontal marquee with inline children has no minimum width. - if (m_layer && m_layer->marquee() && m_layer->marquee()->isHorizontal()) + if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal()) m_minPrefWidth = 0; } @@ -3844,7 +3968,7 @@ static int getBPMWidth(int childValue, Length cssUnit) return 0; } -static int getBorderPaddingMargin(const RenderBox* child, bool endOfInline) +static int getBorderPaddingMargin(const RenderBoxModelObject* child, bool endOfInline) { RenderStyle* cstyle = child->style(); int result = 0; @@ -3947,7 +4071,7 @@ void RenderBlock::calcInlinePrefWidths() if (child->isRenderInline()) { // Add in padding/border/margin from the appropriate side of // the element. - int bpm = getBorderPaddingMargin(static_cast<RenderFlow*>(child), childIterator.endOfInline); + int bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline); childMin += bpm; childMax += bpm; @@ -3979,14 +4103,14 @@ void RenderBlock::calcInlinePrefWidths() bool clearPreviousFloat; if (child->isFloating()) { clearPreviousFloat = (prevFloat - && (prevFloat->style()->floating() == FLEFT && (child->style()->clear() & CLEFT) - || prevFloat->style()->floating() == FRIGHT && (child->style()->clear() & CRIGHT))); + && ((prevFloat->style()->floating() == FLEFT && (child->style()->clear() & CLEFT)) + || (prevFloat->style()->floating() == FRIGHT && (child->style()->clear() & CRIGHT)))); prevFloat = child; } else clearPreviousFloat = false; bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; - if (canBreakReplacedElement && (autoWrap || oldAutoWrap) || clearPreviousFloat) { + if ((canBreakReplacedElement && (autoWrap || oldAutoWrap)) || clearPreviousFloat) { m_minPrefWidth = max(inlineMin, m_minPrefWidth); inlineMin = 0; } @@ -4152,7 +4276,7 @@ void RenderBlock::calcBlockPrefWidths() continue; } - if (child->isFloating() || child->avoidsFloats()) { + if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) { int floatTotalWidth = floatLeftWidth + floatRightWidth; if (child->style()->clear() & CLEFT) { m_maxPrefWidth = max(floatTotalWidth, m_maxPrefWidth); @@ -4186,7 +4310,7 @@ void RenderBlock::calcBlockPrefWidths() w = child->maxPrefWidth() + margin; if (!child->isFloating()) { - if (child->avoidsFloats()) { + if (child->isBox() && toRenderBox(child)->avoidsFloats()) { // Determine a left and right max value based off whether or not the floats can fit in the // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin // is smaller than the float width. @@ -4242,11 +4366,11 @@ void RenderBlock::calcBlockPrefWidths() bool RenderBlock::hasLineIfEmpty() const { - return element() && (element()->isContentEditable() && element()->rootEditableElement() == element() || - element()->isShadowNode() && element()->shadowParentNode()->hasTagName(inputTag)); + return node() && ((node()->isContentEditable() && node()->rootEditableElement() == node()) || + (node()->isShadowNode() && node()->shadowParentNode()->hasTagName(inputTag))); } -int RenderBlock::lineHeight(bool b, bool isRootLineBox) const +int RenderBlock::lineHeight(bool firstLine, bool isRootLineBox) const { // Inline blocks are replaced elements. Otherwise, just pass off to // the base class. If we're being queried as though we're the root line @@ -4254,7 +4378,17 @@ int RenderBlock::lineHeight(bool b, bool isRootLineBox) const // just like a block. if (isReplaced() && !isRootLineBox) return height() + marginTop() + marginBottom(); - return RenderFlow::lineHeight(b, isRootLineBox); + + if (firstLine && document()->usesFirstLineRules()) { + RenderStyle* s = style(firstLine); + if (s != style()) + return s->computedLineHeight(); + } + + if (m_lineHeight == -1) + m_lineHeight = style()->computedLineHeight(); + + return m_lineHeight; } int RenderBlock::baselinePosition(bool b, bool isRootLineBox) const @@ -4276,29 +4410,29 @@ int RenderBlock::baselinePosition(bool b, bool isRootLineBox) const // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled // vertically (e.g., an overflow:hidden block that has had scrollTop moved) or if the baseline is outside // of our content box. - int baselinePos = (m_layer && (m_layer->marquee() || m_layer->verticalScrollbar() || m_layer->scrollYOffset() != 0)) ? -1 : getBaselineOfLastLineBox(); + int baselinePos = (layer() && (layer()->marquee() || layer()->verticalScrollbar() || layer()->scrollYOffset() != 0)) ? -1 : lastLineBoxBaseline(); if (baselinePos != -1 && baselinePos <= borderTop() + paddingTop() + contentHeight()) return marginTop() + baselinePos; return height() + marginTop() + marginBottom(); } - return RenderFlow::baselinePosition(b, isRootLineBox); + return RenderBox::baselinePosition(b, isRootLineBox); } -int RenderBlock::getBaselineOfFirstLineBox() const +int RenderBlock::firstLineBoxBaseline() const { if (!isBlockFlow()) - return RenderFlow::getBaselineOfFirstLineBox(); + return -1; if (childrenInline()) { if (firstLineBox()) - return firstLineBox()->yPos() + firstLineBox()->baseline(); + return firstLineBox()->y() + style(true)->font().ascent(); else return -1; } else { for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) { if (!curr->isFloatingOrPositioned()) { - int result = curr->getBaselineOfFirstLineBox(); + int result = curr->firstLineBoxBaseline(); if (result != -1) return curr->y() + result; // Translate to our coordinate space. } @@ -4308,16 +4442,16 @@ int RenderBlock::getBaselineOfFirstLineBox() const return -1; } -int RenderBlock::getBaselineOfLastLineBox() const +int RenderBlock::lastLineBoxBaseline() const { if (!isBlockFlow()) - return RenderFlow::getBaselineOfLastLineBox(); + return -1; if (childrenInline()) { if (!firstLineBox() && hasLineIfEmpty()) - return RenderFlow::baselinePosition(true, true) + borderTop() + paddingTop(); + return RenderBox::baselinePosition(true, true) + borderTop() + paddingTop(); if (lastLineBox()) - return lastLineBox()->yPos() + lastLineBox()->baseline(); + return lastLineBox()->y() + style(lastLineBox() == firstLineBox())->font().ascent(); return -1; } else { @@ -4325,13 +4459,13 @@ int RenderBlock::getBaselineOfLastLineBox() const for (RenderBox* curr = lastChildBox(); curr; curr = curr->previousSiblingBox()) { if (!curr->isFloatingOrPositioned()) { haveNormalFlowChild = true; - int result = curr->getBaselineOfLastLineBox(); + int result = curr->lastLineBoxBaseline(); if (result != -1) return curr->y() + result; // Translate to our coordinate space. } } if (!haveNormalFlowChild && hasLineIfEmpty()) - return RenderFlow::baselinePosition(true, true) + borderTop() + paddingTop(); + return RenderBox::baselinePosition(true, true) + borderTop() + paddingTop(); } return -1; @@ -4353,7 +4487,7 @@ RenderBlock* RenderBlock::firstLineBlock() const RenderBlock* firstLineBlock = const_cast<RenderBlock*>(this); bool hasPseudo = false; while (true) { - hasPseudo = firstLineBlock->style()->hasPseudoStyle(RenderStyle::FIRST_LINE); + hasPseudo = firstLineBlock->style()->hasPseudoStyle(FIRST_LINE); if (hasPseudo) break; RenderObject* parentBlock = firstLineBlock->parent(); @@ -4361,7 +4495,7 @@ RenderBlock* RenderBlock::firstLineBlock() const !parentBlock || parentBlock->firstChild() != firstLineBlock || !parentBlock->isBlockFlow()) break; ASSERT(parentBlock->isRenderBlock()); - firstLineBlock = static_cast<RenderBlock*>(parentBlock); + firstLineBlock = toRenderBlock(parentBlock); } if (!hasPseudo) @@ -4374,8 +4508,8 @@ void RenderBlock::updateFirstLetter() { if (!document()->usesFirstLetterRules()) return; - // Don't recurse - if (style()->styleType() == RenderStyle::FIRST_LETTER) + // Don't recur + if (style()->styleType() == FIRST_LETTER) return; // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find @@ -4385,7 +4519,7 @@ void RenderBlock::updateFirstLetter() while (true) { // We only honor first-letter if the firstLetterBlock can have children in the DOM. This correctly // prevents form controls from honoring first-letter. - hasPseudoStyle = firstLetterBlock->style()->hasPseudoStyle(RenderStyle::FIRST_LETTER) + hasPseudoStyle = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER) && firstLetterBlock->canHaveChildren(); if (hasPseudoStyle) break; @@ -4403,7 +4537,7 @@ void RenderBlock::updateFirstLetter() RenderObject* currChild = firstLetterBlock->firstChild(); while (currChild && currChild->needsLayout() && (!currChild->isReplaced() || currChild->isFloatingOrPositioned()) && !currChild->isText()) { if (currChild->isFloatingOrPositioned()) { - if (currChild->style()->styleType() == RenderStyle::FIRST_LETTER) + if (currChild->style()->styleType() == FIRST_LETTER) break; currChild = currChild->nextSibling(); } else @@ -4421,8 +4555,8 @@ void RenderBlock::updateFirstLetter() // If the child already has style, then it has already been created, so we just want // to update it. - if (currChild->style()->styleType() == RenderStyle::FIRST_LETTER) { - RenderStyle* pseudo = firstLetterBlock->getCachedPseudoStyle(RenderStyle::FIRST_LETTER, + if (currChild->style()->styleType() == FIRST_LETTER) { + RenderStyle* pseudo = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle()); currChild->setStyle(pseudo); for (RenderObject* genChild = currChild->firstChild(); genChild; genChild = genChild->nextSibling()) { @@ -4433,7 +4567,7 @@ void RenderBlock::updateFirstLetter() } // If the child does not already have style, we create it here. - if (currChild->isText() && !currChild->isBR() && currChild->parent()->style()->styleType() != RenderStyle::FIRST_LETTER) { + if (currChild->isText() && !currChild->isBR() && currChild->parent()->style()->styleType() != FIRST_LETTER) { // Our layout state is not valid for the repaints we are going to trigger by // adding and removing children of firstLetterContainer. view()->disableLayoutState(); @@ -4441,14 +4575,19 @@ void RenderBlock::updateFirstLetter() RenderText* textObj = toRenderText(currChild); // Create our pseudo style now that we have our firstLetterContainer determined. - RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(RenderStyle::FIRST_LETTER, + RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle()); // Force inline display (except for floating first-letters) - pseudoStyle->setDisplay( pseudoStyle->isFloating() ? BLOCK : INLINE); - pseudoStyle->setPosition( StaticPosition ); // CSS2 says first-letter can't be positioned. + pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); + pseudoStyle->setPosition(StaticPosition); // CSS2 says first-letter can't be positioned. - RenderObject* firstLetter = RenderFlow::createAnonymousFlow(document(), pseudoStyle); // anonymous box + RenderObject* firstLetter = 0; + if (pseudoStyle->display() == INLINE) + firstLetter = new (renderArena()) RenderInline(document()); + else + firstLetter = new (renderArena()) RenderBlock(document()); + firstLetter->setStyle(pseudoStyle); firstLetterContainer->addChild(firstLetter, currChild); // The original string is going to be either a generated content string or a DOM node's @@ -4470,10 +4609,10 @@ void RenderBlock::updateFirstLetter() // construct text fragment for the text after the first letter // NOTE: this might empty RenderTextFragment* remainingText = - new (renderArena()) RenderTextFragment(textObj->node(), oldText.get(), length, oldText->length() - length); + new (renderArena()) RenderTextFragment(textObj->node() ? textObj->node() : textObj->document(), oldText.get(), length, oldText->length() - length); remainingText->setStyle(textObj->style()); - if (remainingText->element()) - remainingText->element()->setRenderer(remainingText); + if (remainingText->node()) + remainingText->node()->setRenderer(remainingText); RenderObject* nextObj = textObj->nextSibling(); firstLetterContainer->removeChild(textObj); @@ -4482,7 +4621,7 @@ void RenderBlock::updateFirstLetter() // construct text fragment for the first letter RenderTextFragment* letter = - new (renderArena()) RenderTextFragment(remainingText->node(), oldText.get(), 0, length); + new (renderArena()) RenderTextFragment(remainingText->node() ? remainingText->node() : remainingText->document(), oldText.get(), 0, length); RefPtr<RenderStyle> newStyle = RenderStyle::create(); newStyle->inheritFrom(pseudoStyle); letter->setStyle(newStyle.release()); @@ -4526,7 +4665,7 @@ static RootInlineBox* getLineAtIndex(RenderBlock* block, int i, int& count) else { for (RenderObject* obj = block->firstChild(); obj; obj = obj->nextSibling()) { if (shouldCheckLines(obj)) { - RootInlineBox *box = getLineAtIndex(static_cast<RenderBlock*>(obj), i, count); + RootInlineBox *box = getLineAtIndex(toRenderBlock(obj), i, count); if (box) return box; } @@ -4549,7 +4688,7 @@ static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, RenderBox* normalFlowChildWithoutLines = 0; for (RenderBox* obj = block->firstChildBox(); obj; obj = obj->nextSiblingBox()) { if (shouldCheckLines(obj)) { - int result = getHeightForLineCount(static_cast<RenderBlock*>(obj), l, false, count); + int result = getHeightForLineCount(toRenderBlock(obj), l, false, count); if (result != -1) return result + obj->y() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0); } @@ -4580,7 +4719,7 @@ int RenderBlock::lineCount() else for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) if (shouldCheckLines(obj)) - count += static_cast<RenderBlock*>(obj)->lineCount(); + count += toRenderBlock(obj)->lineCount(); } return count; } @@ -4599,16 +4738,16 @@ void RenderBlock::adjustForBorderFit(int x, int& left, int& right) const if (childrenInline()) { for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { if (box->firstChild()) - left = min(left, x + box->firstChild()->xPos()); + left = min(left, x + box->firstChild()->x()); if (box->lastChild()) - right = max(right, x + box->lastChild()->xPos() + box->lastChild()->width()); + right = max(right, x + box->lastChild()->x() + box->lastChild()->width()); } } else { for (RenderBox* obj = firstChildBox(); obj; obj = obj->nextSiblingBox()) { if (!obj->isFloatingOrPositioned()) { if (obj->isBlockFlow() && !obj->hasOverflowClip()) - static_cast<RenderBlock*>(obj)->adjustForBorderFit(x + obj->x(), left, right); + toRenderBlock(obj)->adjustForBorderFit(x + obj->x(), left, right); else if (obj->style()->visibility() == VISIBLE) { // We are a replaced element or some kind of non-block-flow object. left = min(left, x + obj->x()); @@ -4669,7 +4808,7 @@ void RenderBlock::clearTruncation() else for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) if (shouldCheckLines(obj)) - static_cast<RenderBlock*>(obj)->clearTruncation(); + toRenderBlock(obj)->clearTruncation(); } } @@ -4695,6 +4834,218 @@ void RenderBlock::setMaxBottomMargins(int pos, int neg) m_maxMargin->m_bottomNeg = neg; } +void RenderBlock::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel) +{ + // 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 + // shape). + if (topLevel && inlineContinuation()) { + rects.append(IntRect(tx, ty - collapsedMarginTop(), + width(), height() + collapsedMarginTop() + collapsedMarginBottom())); + inlineContinuation()->absoluteRects(rects, + tx - x() + inlineContinuation()->containingBlock()->x(), + ty - y() + inlineContinuation()->containingBlock()->y(), topLevel); + } else + rects.append(IntRect(tx, ty, width(), height())); +} + +void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool topLevel) +{ + // 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 + // shape). + if (topLevel && inlineContinuation()) { + FloatRect localRect(0, -collapsedMarginTop(), + width(), height() + collapsedMarginTop() + collapsedMarginBottom()); + quads.append(localToAbsoluteQuad(localRect)); + inlineContinuation()->absoluteQuads(quads, topLevel); + } else + quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width(), height()))); +} + +IntRect RenderBlock::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth) +{ + IntRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); + if (inlineContinuation()) + r.inflateY(collapsedMarginTop()); + return r; +} + +RenderObject* RenderBlock::hoverAncestor() const +{ + return inlineContinuation() ? inlineContinuation() : RenderBox::hoverAncestor(); +} + +void RenderBlock::updateDragState(bool dragOn) +{ + RenderBox::updateDragState(dragOn); + if (inlineContinuation()) + inlineContinuation()->updateDragState(dragOn); +} + +RenderStyle* RenderBlock::outlineStyleForRepaint() const +{ + return inlineContinuation() ? inlineContinuation()->style() : style(); +} + +void RenderBlock::childBecameNonInline(RenderObject*) +{ + makeChildrenNonInline(); + if (isAnonymousBlock() && parent() && parent()->isRenderBlock()) + toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); + // |this| may be dead here +} + +void RenderBlock::updateHitTestResult(HitTestResult& result, const IntPoint& point) +{ + if (result.innerNode()) + return; + + Node* n = node(); + if (inlineContinuation()) + // We are in the margins of block elements that are part of a continuation. In + // this case we're actually still inside the enclosing inline element that was + // split. Go ahead and set our inner node accordingly. + n = inlineContinuation()->node(); + + if (n) { + result.setInnerNode(n); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(n); + result.setLocalPoint(point); + } +} + +IntRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine) +{ + // Do the normal calculation in most cases. + if (firstChild()) + return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine); + + // This is a special case: + // The element is not an inline element, and it's empty. So we have to + // calculate a fake position to indicate where objects are to be inserted. + + // FIXME: This does not take into account either :first-line or :first-letter + // However, as soon as some content is entered, the line boxes will be + // constructed and this kludge is not called any more. So only the caret size + // of an empty :first-line'd block is wrong. I think we can live with that. + RenderStyle* currentStyle = firstLineStyle(); + int height = lineHeight(true); + + enum CaretAlignment { alignLeft, alignRight, alignCenter }; + + CaretAlignment alignment = alignLeft; + + switch (currentStyle->textAlign()) { + case TAAUTO: + case JUSTIFY: + if (currentStyle->direction() == RTL) + alignment = alignRight; + break; + case LEFT: + case WEBKIT_LEFT: + break; + case CENTER: + case WEBKIT_CENTER: + alignment = alignCenter; + break; + case RIGHT: + case WEBKIT_RIGHT: + alignment = alignRight; + break; + } + + int x = borderLeft() + paddingLeft(); + int w = width(); + + switch (alignment) { + case alignLeft: + break; + case alignCenter: + x = (x + w - (borderRight() + paddingRight())) / 2; + break; + case alignRight: + x = w - (borderRight() + paddingRight()); + break; + } + + if (extraWidthToEndOfLine) { + if (isRenderBlock()) { + *extraWidthToEndOfLine = w - (x + caretWidth); + } else { + // FIXME: This code looks wrong. + // myRight and containerRight are set up, but then clobbered. + // So *extraWidthToEndOfLine will always be 0 here. + + int myRight = x + caretWidth; + // FIXME: why call localToAbsoluteForContent() twice here, too? + FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight, 0)); + + int containerRight = containingBlock()->x() + containingBlockWidthForContent(); + FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight, 0)); + + *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x(); + } + } + + int y = paddingTop() + borderTop(); + + return IntRect(x, y, caretWidth, height); +} + +void RenderBlock::addFocusRingRects(GraphicsContext* graphicsContext, 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 + // shape). + if (inlineContinuation()) { + // FIXME: This check really isn't accurate. + bool nextInlineHasLineBox = inlineContinuation()->firstLineBox(); + // FIXME: This is wrong. The principal renderer may not be the continuation preceding this block. + 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())); + + if (!hasOverflowClip() && !hasControlClip()) { + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + graphicsContext->addFocusRingRect(IntRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())); + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText() && !curr->isListMarker() && curr->isBox()) { + RenderBox* box = toRenderBox(curr); + FloatPoint pos; + // FIXME: This doesn't work correctly with transforms. + if (box->layer()) + pos = curr->localToAbsolute(); + else + pos = FloatPoint(tx + box->x(), ty + box->y()); + box->addFocusRingRects(graphicsContext, pos.x(), pos.y()); + } + } + } + + if (inlineContinuation()) + inlineContinuation()->addFocusRingRects(graphicsContext, + tx - x() + inlineContinuation()->containingBlock()->x(), + ty - y() + inlineContinuation()->containingBlock()->y()); +} + +RenderBlock* RenderBlock::createAnonymousBlock() const +{ + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(BLOCK); + + RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + newBox->setStyle(newStyle.release()); + return newBox; +} + const char* RenderBlock::renderName() const { if (isBody()) diff --git a/WebCore/rendering/RenderBlock.h b/WebCore/rendering/RenderBlock.h index 2e74e41..d7a26ef 100644 --- a/WebCore/rendering/RenderBlock.h +++ b/WebCore/rendering/RenderBlock.h @@ -27,7 +27,8 @@ #include "DeprecatedPtrList.h" #include "GapRects.h" -#include "RenderFlow.h" +#include "RenderBox.h" +#include "RenderLineBoxList.h" #include "RootInlineBox.h" #include <wtf/ListHashSet.h> @@ -36,6 +37,7 @@ namespace WebCore { class InlineIterator; class BidiRun; class Position; +class RenderInline; class RootInlineBox; template <class Iterator, class Run> class BidiResolver; @@ -43,11 +45,18 @@ typedef BidiResolver<InlineIterator, BidiRun> InlineBidiResolver; enum CaretType { CursorCaret, DragCaret }; -class RenderBlock : public RenderFlow { +class RenderBlock : public RenderBox { public: RenderBlock(Node*); virtual ~RenderBlock(); + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + + virtual void destroy(); + virtual const char* renderName() const; // These two functions are overridden for inline-block. @@ -58,10 +67,17 @@ public: virtual bool isBlockFlow() const { return (!isInline() || isReplaced()) && !isTable(); } virtual bool isInlineBlockOrInlineTable() const { return isInline() && isReplaced(); } - virtual bool childrenInline() const { return m_childrenInline; } - virtual void setChildrenInline(bool b) { m_childrenInline = b; } void makeChildrenNonInline(RenderObject* insertionPoint = 0); + virtual void removeLeftoverAnonymousBlock(RenderBlock* child); + + RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } + const RenderLineBoxList* lineBoxes() const { return &m_lineBoxes; } + + InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); } + InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); } + void deleteLineBoxTree(); + virtual void dirtyLinesFromChangedChild(RenderObject* child) { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } // The height (and width) of a block when you include overflow spillage out of the bottom // of the block (e.g., a <div style="height:25px"> that has a 100px tall image inside @@ -77,8 +93,6 @@ public: void addVisualOverflow(const IntRect&); virtual bool isSelfCollapsingBlock() const; - virtual bool isTopMarginQuirk() const { return m_topMarginQuirk; } - virtual bool isBottomMarginQuirk() const { return m_bottomMarginQuirk; } virtual int maxTopMargin(bool positive) const { return positive ? maxTopPosMargin() : maxTopNegMargin(); } virtual int maxBottomMargin(bool positive) const { return positive ? maxBottomPosMargin() : maxBottomNegMargin(); } @@ -100,7 +114,7 @@ public: } } - virtual void addChildToFlow(RenderObject* newChild, RenderObject* beforeChild); + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); virtual void removeChild(RenderObject*); virtual void repaintOverhangingFloats(bool paintAllDescendants); @@ -117,11 +131,16 @@ public: void addPercentHeightDescendant(RenderBox*); static void removePercentHeightDescendant(RenderBox*); + HashSet<RenderBox*>* percentHeightDescendants() const; virtual void positionListMarker() { } virtual void borderFitAdjust(int& x, int& w) const; // Shrink the box in which the border paints if border-fit is set. + virtual void updateBeforeAfterContent(PseudoId); + + RootInlineBox* createRootInlineBox(); + // Called to lay out the legend for a fieldset. virtual RenderObject* layoutLegend(bool /*relayoutChildren*/) { return 0; } @@ -138,7 +157,7 @@ public: }; void bidiReorderLine(InlineBidiResolver&, const InlineIterator& end); - RootInlineBox* determineStartPosition(bool& fullLayout, InlineBidiResolver&, Vector<FloatWithRect>& floats, unsigned& numCleanFloats); + RootInlineBox* determineStartPosition(bool& firstLine, bool& fullLayout, InlineBidiResolver&, Vector<FloatWithRect>& floats, unsigned& numCleanFloats); RootInlineBox* determineEndPosition(RootInlineBox* startBox, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& yPos); @@ -146,12 +165,12 @@ public: RootInlineBox*& endLine, int& endYPos, int& repaintBottom, int& repaintTop); bool generatesLineBoxesForInlineChild(RenderObject*); void skipTrailingWhitespace(InlineIterator&); - int skipLeadingWhitespace(InlineBidiResolver&); - void fitBelowFloats(int widthToFit, int& availableWidth); - InlineIterator findNextLineBreak(InlineBidiResolver&, EClear* clear = 0); - RootInlineBox* constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool lastLine, RenderObject* endObject); - InlineFlowBox* createLineBoxes(RenderObject*); - void computeHorizontalPositionsForLine(RootInlineBox*, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd); + int skipLeadingWhitespace(InlineBidiResolver&, bool firstLine); + void fitBelowFloats(int widthToFit, bool firstLine, int& availableWidth); + InlineIterator findNextLineBreak(InlineBidiResolver&, bool firstLine, EClear* clear = 0); + RootInlineBox* constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool firstLine, bool lastLine, RenderObject* endObject); + InlineFlowBox* createLineBoxes(RenderObject*, bool firstLine); + void computeHorizontalPositionsForLine(RootInlineBox*, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd); void computeVerticalPositionsForLine(RootInlineBox*, BidiRun*); void checkLinesForOverflow(); void deleteEllipsisLineBoxes(); @@ -162,7 +181,8 @@ public: virtual void paintObject(PaintInfo&, int tx, int ty); void paintFloats(PaintInfo&, int tx, int ty, bool preservePhase = false); void paintContents(PaintInfo&, int tx, int ty); - void paintColumns(PaintInfo&, int tx, int ty, bool paintFloats = false); + void paintColumnContents(PaintInfo&, int tx, int ty, bool paintFloats = false); + void paintColumnRules(PaintInfo&, int tx, int ty); void paintChildren(PaintInfo&, int tx, int ty); void paintEllipsisBoxes(PaintInfo&, int tx, int ty); void paintSelection(PaintInfo&, int tx, int ty); @@ -175,16 +195,16 @@ public: // Returns ture if and only if it has positioned any floats. bool positionNewFloats(); void clearFloats(); - int getClearDelta(RenderBox* child); + int getClearDelta(RenderBox* child, int yPos); void markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove = 0, bool inLayout = true); void markPositionedObjectsForLayout(); - virtual bool containsFloats() { return m_floatingObjects && !m_floatingObjects->isEmpty(); } - virtual bool containsFloat(RenderObject*); + bool containsFloats() { return m_floatingObjects && !m_floatingObjects->isEmpty(); } + bool containsFloat(RenderObject*); virtual bool avoidsFloats() const; - virtual bool hasOverhangingFloats() { return !hasColumns() && floatBottom() > height(); } + bool hasOverhangingFloats() { return parent() && !hasColumns() && floatBottom() > height(); } void addIntrudingFloats(RenderBlock* prev, int xoffset, int yoffset); int addOverhangingFloats(RenderBlock* child, int xoffset, int yoffset, bool makeChildPaintOtherFloats); @@ -194,18 +214,18 @@ public: inline int rightBottom(); IntRect floatRect() const; - virtual int lineWidth(int) const; + int lineWidth(int y, bool firstLine) const; virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; int rightOffset() const; int rightRelOffset(int y, int fixedOffset, bool applyTextIndent = true, int* heightRemaining = 0) const; - int rightOffset(int y) const { return rightRelOffset(y, rightOffset(), true); } + int rightOffset(int y, bool firstLine) const { return rightRelOffset(y, rightOffset(), firstLine); } int leftOffset() const; int leftRelOffset(int y, int fixedOffset, bool applyTextIndent = true, int* heightRemaining = 0) const; - int leftOffset(int y) const { return leftRelOffset(y, leftOffset(), true); } + int leftOffset(int y, bool firstLine) const { return leftRelOffset(y, leftOffset(), firstLine); } virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); virtual bool hitTestColumns(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); @@ -213,7 +233,7 @@ public: virtual bool isPointInOverflowControl(HitTestResult&, int x, int y, int tx, int ty); - virtual VisiblePosition positionForCoordinates(int x, int y); + virtual VisiblePosition positionForPoint(const IntPoint&); // Block flows subclass availableWidth to handle multi column layout (shrinking the width available to children when laying out.) virtual int availableWidth() const; @@ -222,8 +242,8 @@ public: void calcInlinePrefWidths(); void calcBlockPrefWidths(); - virtual int getBaselineOfFirstLineBox() const; - virtual int getBaselineOfLastLineBox() const; + virtual int firstLineBoxBaseline() const; + virtual int lastLineBoxBaseline() const; RootInlineBox* firstRootBox() const { return static_cast<RootInlineBox*>(firstLineBox()); } RootInlineBox* lastRootBox() const { return static_cast<RootInlineBox*>(lastLineBox()); } @@ -237,38 +257,22 @@ public: bool inRootBlockContext() const; - void setHasMarkupTruncation(bool b = true) { m_hasMarkupTruncation = b; } - bool hasMarkupTruncation() const { return m_hasMarkupTruncation; } + virtual IntRect rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth); + virtual RenderStyle* outlineStyleForRepaint() const; + + virtual RenderObject* hoverAncestor() const; + virtual void updateDragState(bool dragOn); + virtual void updateHitTestResult(HitTestResult&, const IntPoint&); + + virtual void childBecameNonInline(RenderObject* child); - virtual bool hasSelectedChildren() const { return m_selectionState != SelectionNone; } - virtual SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState); } virtual void setSelectionState(SelectionState s); - struct BlockSelectionInfo { - RenderBlock* m_block; - GapRects m_rects; - SelectionState m_state; - - BlockSelectionInfo() - : m_block(0) - , m_state(SelectionNone) - { - } - - BlockSelectionInfo(RenderBlock* b) - : m_block(b) - , m_rects(b->needsLayout() ? GapRects() : b->selectionGapRects()) - , m_state(b->selectionState()) - { - } - - RenderBlock* block() const { return m_block; } - GapRects rects() const { return m_rects; } - SelectionState state() const { return m_state; } - }; - - virtual IntRect selectionRect(bool) { return selectionGapRects(); } - GapRects selectionGapRects(); + virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool /*clipToVisibleContent*/) + { + return selectionGapRectsForRepaint(repaintContainer); + } + GapRects selectionGapRectsForRepaint(RenderBoxModelObject* repaintContainer); virtual bool shouldPaintSelectionGaps() const; bool isSelectionRoot() const; GapRects fillSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, @@ -289,6 +293,9 @@ public: int leftSelectionOffset(RenderBlock* rootBlock, int y); int rightSelectionOffset(RenderBlock* rootBlock, int y); + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + // Helper methods for computing line counts and heights for line counts. RootInlineBox* lineAtIndex(int); int lineCount(); @@ -299,12 +306,24 @@ public: unsigned desiredColumnCount() const; Vector<IntRect>* columnRects() const; void setDesiredColumnCountAndWidth(int count, int width); + int columnGap() const; void adjustRectForColumns(IntRect&) const; - void addContinuationWithOutline(RenderFlow*); + void addContinuationWithOutline(RenderInline*); void paintContinuationOutlines(PaintInfo&, int tx, int ty); + RenderInline* inlineContinuation() const { return m_inlineContinuation; } + void setInlineContinuation(RenderInline* c) { m_inlineContinuation = c; } + + virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); + + virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + + // This function is a convenience helper for creating an anonymous block that inherits its + // style from this RenderBlock. + RenderBlock* createAnonymousBlock() const; + private: void adjustPointToColumnContents(IntPoint&) const; void adjustForBorderFit(int x, int& left, int& right) const; // Helper function for borderFitAdjust @@ -312,13 +331,15 @@ private: void markLinesDirtyInVerticalRange(int top, int bottom); protected: - virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); void newLine(EClear); virtual bool hasLineIfEmpty() const; bool layoutOnlyPositionedObjects(); + virtual RootInlineBox* createRootBox(); // Subclassed by SVG. + private: Position positionForBox(InlineBox*, bool start = true) const; Position positionForRenderer(RenderObject*, bool start = true) const; @@ -326,15 +347,6 @@ private: // Adjust tx and ty from painting offsets to the local coords of this renderer void offsetForContents(int& tx, int& ty) const; - // columGap() is used by WebKit when hit-testing columns. It's called by - // CacheBuilder when it duplicates the hit-testing logic. -#ifdef ANDROID_EXPOSE_COLUMN_GAP - public: -#endif - int columnGap() const; -#ifdef ANDROID_EXPOSE_COLUMN_GAP -private: -#endif void calcColumnWidth(); int layoutColumns(int endOfContent = -1); @@ -443,8 +455,8 @@ protected: RenderBox* handleFloatingChild(RenderBox* child, const MarginInfo&, bool& handled); RenderBox* handlePositionedChild(RenderBox* child, const MarginInfo&, bool& handled); RenderBox* handleRunInChild(RenderBox* child, bool& handled); - void collapseMargins(RenderBox* child, MarginInfo&, int yPosEstimate); - void clearFloatsIfNeeded(RenderBox* child, MarginInfo&, int oldTopPosMargin, int oldTopNegMargin); + int collapseMargins(RenderBox* child, MarginInfo&); + int clearFloatsIfNeeded(RenderBox* child, MarginInfo&, int oldTopPosMargin, int oldTopNegMargin, int yPos); int estimateVerticalPosition(RenderBox* child, const MarginInfo&); void determineHorizontalPosition(RenderBox* child); void handleBottomOfBlock(int top, int bottom, MarginInfo&); @@ -455,7 +467,13 @@ private: typedef ListHashSet<RenderBox*>::const_iterator Iterator; DeprecatedPtrList<FloatingObject>* m_floatingObjects; ListHashSet<RenderBox*>* m_positionedObjects; - + + // An inline can be split with blocks occurring in between the inline content. + // When this occurs we need a pointer to our next object. We can basically be + // split into a sequence of inlines and blocks. The continuation will either be + // an anonymous block (that houses other blocks) or it will be an inline flow. + RenderInline* m_inlineContinuation; + // Allocated only when some of these fields have non-default values struct MaxMargin { MaxMargin(const RenderBlock* o) @@ -480,13 +498,33 @@ private: MaxMargin* m_maxMargin; protected: + RenderObjectChildList m_children; + RenderLineBoxList m_lineBoxes; // All of the root line boxes created for this block flow. For example, <div>Hello<br>world.</div> will have two total lines for the <div>. + // How much content overflows out of our block vertically or horizontally. int m_overflowHeight; int m_overflowWidth; int m_overflowLeft; int m_overflowTop; + + mutable int m_lineHeight; }; +inline RenderBlock* toRenderBlock(RenderObject* o) +{ + ASSERT(!o || o->isRenderBlock()); + return static_cast<RenderBlock*>(o); +} + +inline const RenderBlock* toRenderBlock(const RenderObject* o) +{ + ASSERT(!o || o->isRenderBlock()); + return static_cast<const RenderBlock*>(o); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderBlock(const RenderBlock* o); + } // namespace WebCore #endif // RenderBlock_h diff --git a/WebCore/rendering/RenderBox.cpp b/WebCore/rendering/RenderBox.cpp index 7711f5e..5827f00 100644 --- a/WebCore/rendering/RenderBox.cpp +++ b/WebCore/rendering/RenderBox.cpp @@ -30,6 +30,7 @@ #include "Document.h" #include "FrameView.h" #include "GraphicsContext.h" +#include "htmlediting.h" #include "HTMLElement.h" #include "HTMLNames.h" #include "ImageBuffer.h" @@ -40,13 +41,13 @@ #include "RenderFlexibleBox.h" #include "RenderInline.h" #include "RenderLayer.h" -#include "RenderReplica.h" #include "RenderTableCell.h" #include "RenderTheme.h" #ifdef ANDROID_LAYOUT #include "Settings.h" #endif #include "RenderView.h" +#include "TransformState.h" #include <algorithm> #include <math.h> @@ -64,11 +65,10 @@ using namespace HTMLNames; typedef WTF::HashMap<const RenderBox*, int> OverrideSizeMap; static OverrideSizeMap* gOverrideSizeMap = 0; -bool RenderBox::s_wasFloating = false; bool RenderBox::s_hadOverflowClip = false; RenderBox::RenderBox(Node* node) - : RenderObject(node) + : RenderBoxModelObject(node) #ifdef ANDROID_LAYOUT , m_visibleWidth(0) #endif @@ -78,7 +78,6 @@ RenderBox::RenderBox(Node* node) , m_marginBottom(0) , m_minPrefWidth(-1) , m_maxPrefWidth(-1) - , m_layer(0) , m_inlineBoxWrapper(0) { setIsBox(); @@ -96,51 +95,61 @@ void RenderBox::destroy() if (hasOverrideSize()) gOverrideSizeMap->remove(this); - // This must be done before we destroy the RenderObject. - if (m_layer) - m_layer->clearClipRects(); - if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent())) RenderBlock::removePercentHeightDescendant(this); - RenderObject::destroy(); + RenderBoxModelObject::destroy(); +} + +void RenderBox::removeFloatingOrPositionedChildFromBlockLists() +{ + ASSERT(isFloatingOrPositioned()); + + if (documentBeingDestroyed()) + return; + + if (isFloating()) { + RenderBlock* outermostBlock = containingBlock(); + for (RenderBlock* p = outermostBlock; p && !p->isRenderView(); p = p->containingBlock()) { + if (p->containsFloat(this)) + outermostBlock = p; + } + + if (outermostBlock) + outermostBlock->markAllDescendantsWithFloatsForLayout(this, false); + } + + if (isPositioned()) { + RenderObject* p; + for (p = parent(); p; p = p->parent()) { + if (p->isRenderBlock()) + toRenderBlock(p)->removePositionedObject(this); + } + } } -void RenderBox::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { - s_wasFloating = isFloating(); s_hadOverflowClip = hasOverflowClip(); if (style()) { - // If our z-index changes value or our visibility changes, - // we need to dirty our stacking context's z-order list. - if (newStyle) { - if (hasLayer() && (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || - style()->zIndex() != newStyle->zIndex() || - style()->visibility() != newStyle->visibility())) { - layer()->dirtyStackingContextZOrderLists(); - if (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || style()->visibility() != newStyle->visibility()) - layer()->dirtyZOrderLists(); - } - } - // The background of the root element or the body element could propagate up to // the canvas. Just dirty the entire canvas when our style changes substantially. - if (diff >= RenderStyle::Repaint && element() && - (element()->hasTagName(htmlTag) || element()->hasTagName(bodyTag))) + if (diff >= StyleDifferenceRepaint && node() && + (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag))) view()->repaint(); else if (parent() && !isText()) { // Do a repaint with the old style first, e.g., for example if we go from // having an outline to not having an outline. - if (diff == RenderStyle::RepaintLayer) { + if (diff == StyleDifferenceRepaintLayer) { layer()->repaintIncludingDescendants(); if (!(style()->clip() == newStyle->clip())) layer()->clearClipRectsIncludingDescendants(); - } else if (diff == RenderStyle::Repaint || newStyle->outlineSize() < style()->outlineSize()) + } else if (diff == StyleDifferenceRepaint || newStyle->outlineSize() < style()->outlineSize()) repaint(); } - if (diff == RenderStyle::Layout) { + if (diff == StyleDifferenceLayout) { // When a layout hint happens, we go ahead and do a repaint of the layer, since the layer could // end up being destroyed. if (hasLayer()) { @@ -165,26 +174,45 @@ void RenderBox::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newSt if (style()->position() == StaticPosition) repaint(); if (isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition)) - removeFromObjectLists(); + removeFloatingOrPositionedChildFromBlockLists(); } } } - RenderObject::styleWillChange(diff, newStyle); + RenderBoxModelObject::styleWillChange(diff, newStyle); } -void RenderBox::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { - // We need to ensure that view->maximalOutlineSize() is valid for any repaints that happen - // during the style change (it's used by clippedOverflowRectForRepaint()). - if (style()->outlineWidth() > 0 && style()->outlineSize() > maximalOutlineSize(PaintPhaseOutline)) - static_cast<RenderView*>(document()->renderer())->setMaximalOutlineSize(style()->outlineSize()); - - RenderObject::styleDidChange(diff, oldStyle); + RenderBoxModelObject::styleDidChange(diff, oldStyle); if (needsLayout() && oldStyle && (oldStyle->height().isPercent() || oldStyle->minHeight().isPercent() || oldStyle->maxHeight().isPercent())) RenderBlock::removePercentHeightDescendant(this); + // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the + // new zoomed coordinate space. + if (hasOverflowClip() && oldStyle && style() && oldStyle->effectiveZoom() != style()->effectiveZoom()) { + int left = scrollLeft(); + if (left) { + left = (left / oldStyle->effectiveZoom()) * style()->effectiveZoom(); + setScrollLeft(left); + } + int top = scrollTop(); + if (top) { + top = (top / oldStyle->effectiveZoom()) * style()->effectiveZoom(); + setScrollTop(top); + } + } + + // Set the text color if we're the body. + if (isBody()) + document()->setTextColor(style()->color()); +} + +void RenderBox::updateBoxModelInfoFromStyle() +{ + RenderBoxModelObject::updateBoxModelInfoFromStyle(); + bool isRootObject = isRoot(); bool isViewObject = isRenderView(); @@ -192,23 +220,8 @@ void RenderBox::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldSty if (isRootObject || isViewObject) setHasBoxDecorations(true); - setInline(style()->isDisplayInlineType()); - - switch (style()->position()) { - case AbsolutePosition: - case FixedPosition: - setPositioned(true); - break; - default: - setPositioned(false); - - if (style()->isFloating()) - setFloating(true); - - if (style()->position() == RelativePosition) - setRelPositioned(true); - break; - } + setPositioned(style()->position() == AbsolutePosition || style()->position() == FixedPosition); + setFloating(!isPositioned() && style()->isFloating()); // We also handle <body> and <html>, whose overflow applies to the viewport. if (style()->overflowX() != OVISIBLE && !isRootObject && (isRenderBlock() || isTableRow() || isTableSection())) { @@ -219,7 +232,7 @@ void RenderBox::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldSty // (2) We are the primary <body> (can be checked by looking at document.body). // (3) The root element has visible overflow. if (document()->documentElement()->hasTagName(htmlTag) && - document()->body() == element() && + document()->body() == node() && document()->documentElement()->renderer()->style()->overflowX() == OVISIBLE) boxHasOverflowClip = false; } @@ -234,133 +247,28 @@ void RenderBox::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldSty } } - setHasTransform(style()->hasTransform()); + setHasTransform(style()->hasTransformRelatedProperty()); setHasReflection(style()->boxReflect()); - - if (requiresLayer()) { - if (!m_layer) { - if (s_wasFloating && isFloating()) - setChildNeedsLayout(true); - m_layer = new (renderArena()) RenderLayer(this); - setHasLayer(true); - m_layer->insertOnlyThisLayer(); - if (parent() && !needsLayout() && containingBlock()) - m_layer->updateLayerPositions(); - } - } else if (m_layer && !isRootObject && !isViewObject) { - ASSERT(m_layer->parent()); - RenderLayer* layer = m_layer; - m_layer = 0; - setHasLayer(false); - setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit. - setHasReflection(false); - layer->removeOnlyThisLayer(); - if (s_wasFloating && isFloating()) - setChildNeedsLayout(true); - } - - // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the - // new zoomed coordinate space. - if (hasOverflowClip() && oldStyle && style() && oldStyle->effectiveZoom() != style()->effectiveZoom()) { - int left = scrollLeft(); - if (left) { - left = (left / oldStyle->effectiveZoom()) * style()->effectiveZoom(); - setScrollLeft(left); - } - int top = scrollTop(); - if (top) { - top = (top / oldStyle->effectiveZoom()) * style()->effectiveZoom(); - setScrollTop(top); - } - } - - if (m_layer) - m_layer->styleChanged(diff, oldStyle); - - // Set the text color if we're the body. - if (isBody()) - document()->setTextColor(style()->color()); } - -int RenderBox::offsetLeft() const +void RenderBox::layout() { - RenderBox* offsetPar = offsetParent(); - if (!offsetPar) - return 0; - int xPos = x() - offsetPar->borderLeft(); - if (!isPositioned()) { - if (isRelPositioned()) - xPos += relativePositionOffsetX(); - RenderObject* curr = parent(); - while (curr && curr != offsetPar) { - // FIXME: What are we supposed to do inside SVG content? - if (curr->isBox() && !curr->isTableRow()) - xPos += toRenderBox(curr)->x(); - curr = curr->parent(); - } - if (offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) - xPos += offsetPar->x(); - } - return xPos; -} + ASSERT(needsLayout()); -int RenderBox::offsetTop() const -{ - RenderBox* offsetPar = offsetParent(); - if (!offsetPar) - return 0; - int yPos = y() - offsetPar->borderTop(); - if (!isPositioned()) { - if (isRelPositioned()) - yPos += relativePositionOffsetY(); - RenderObject* curr = parent(); - while (curr && curr != offsetPar) { - // FIXME: What are we supposed to do inside SVG content? - if (curr->isBox() && !curr->isTableRow()) - yPos += toRenderBox(curr)->y(); - curr = curr->parent(); - } - if (offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) - yPos += offsetPar->y(); + RenderObject* child = firstChild(); + if (!child) { + setNeedsLayout(false); + return; } - return yPos; -} - -RenderBox* RenderBox::offsetParent() const -{ - // FIXME: It feels like this function could almost be written using containing blocks. - if (isBody()) - return 0; - - bool skipTables = isPositioned() || isRelPositioned(); - float currZoom = style()->effectiveZoom(); - RenderObject* curr = parent(); - while (curr && (!curr->element() || - (!curr->isPositioned() && !curr->isRelPositioned() && !curr->isBody()))) { - Node* element = curr->element(); - if (!skipTables && element) { - bool isTableElement = element->hasTagName(tableTag) || - element->hasTagName(tdTag) || - element->hasTagName(thTag); - -#if ENABLE(WML) - if (!isTableElement && element->isWMLElement()) - isTableElement = element->hasTagName(WMLNames::tableTag) || - element->hasTagName(WMLNames::tdTag); -#endif - - if (isTableElement) - break; - } - float newZoom = curr->style()->effectiveZoom(); - if (currZoom != newZoom) - break; - currZoom = newZoom; - curr = curr->parent(); + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y())); + while (child) { + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + child = child->nextSibling(); } - return curr && curr->isBox() ? toRenderBox(curr) : 0; + statePusher.pop(); + setNeedsLayout(false); } // More IE extensions. clientWidth and clientHeight represent the interior of an object @@ -382,104 +290,47 @@ int RenderBox::clientHeight() const int RenderBox::scrollWidth() const { if (hasOverflowClip()) - return m_layer->scrollWidth(); + return layer()->scrollWidth(); return overflowWidth(); } int RenderBox::scrollHeight() const { if (hasOverflowClip()) - return m_layer->scrollHeight(); + return layer()->scrollHeight(); return overflowHeight(); } int RenderBox::scrollLeft() const { - return hasOverflowClip() ? m_layer->scrollXOffset() : 0; + return hasOverflowClip() ? layer()->scrollXOffset() : 0; } int RenderBox::scrollTop() const { - return hasOverflowClip() ? m_layer->scrollYOffset() : 0; + return hasOverflowClip() ? layer()->scrollYOffset() : 0; } void RenderBox::setScrollLeft(int newLeft) { if (hasOverflowClip()) - m_layer->scrollToXOffset(newLeft); + layer()->scrollToXOffset(newLeft); } void RenderBox::setScrollTop(int newTop) { if (hasOverflowClip()) - m_layer->scrollToYOffset(newTop); -} - -int RenderBox::paddingTop(bool) const -{ - int w = 0; - Length padding = style()->paddingTop(); - if (padding.isPercent()) - w = containingBlock()->availableWidth(); - return padding.calcMinValue(w); -} - -int RenderBox::paddingBottom(bool) const -{ - int w = 0; - Length padding = style()->paddingBottom(); - if (padding.isPercent()) - w = containingBlock()->availableWidth(); - return padding.calcMinValue(w); -} - -int RenderBox::paddingLeft(bool) const -{ - int w = 0; - Length padding = style()->paddingLeft(); - if (padding.isPercent()) - w = containingBlock()->availableWidth(); - return padding.calcMinValue(w); + layer()->scrollToYOffset(newTop); } -int RenderBox::paddingRight(bool) const +void RenderBox::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool) { - int w = 0; - Length padding = style()->paddingRight(); - if (padding.isPercent()) - w = containingBlock()->availableWidth(); - return padding.calcMinValue(w); + rects.append(IntRect(tx, ty, width(), height())); } -void RenderBox::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel) +void RenderBox::absoluteQuads(Vector<FloatQuad>& quads, bool) { - // 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 - // shape). - RenderFlow* continuation = virtualContinuation(); - if (topLevel && continuation) { - rects.append(IntRect(tx, ty - collapsedMarginTop(), - width(), height() + collapsedMarginTop() + collapsedMarginBottom())); - continuation->absoluteRects(rects, - tx - x() + continuation->containingBlock()->x(), - ty - y() + continuation->containingBlock()->y(), topLevel); - } else - rects.append(IntRect(tx, ty, width(), height())); -} - -void RenderBox::absoluteQuads(Vector<FloatQuad>& quads, bool topLevel) -{ - // 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 - // shape). - RenderFlow* continuation = virtualContinuation(); - if (topLevel && continuation) { - FloatRect localRect(0, -collapsedMarginTop(), - width(), height() + collapsedMarginTop() + collapsedMarginBottom()); - quads.append(localToAbsoluteQuad(localRect)); - continuation->absoluteQuads(quads, topLevel); - } else - quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height()))); + quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height()))); } IntRect RenderBox::absoluteContentBox() const @@ -497,7 +348,7 @@ FloatQuad RenderBox::absoluteContentQuad() const } -IntRect RenderBox::outlineBoundsForRepaint(RenderBox* /*repaintContainer*/) const +IntRect RenderBox::outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/) const { IntRect box = borderBoundingBox(); adjustRectForOutlineAndShadow(box); @@ -514,17 +365,7 @@ IntRect RenderBox::outlineBoundsForRepaint(RenderBox* /*repaintContainer*/) cons void RenderBox::addFocusRingRects(GraphicsContext* graphicsContext, 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 - // shape). - RenderFlow* continuation = virtualContinuation(); - if (continuation) { - graphicsContext->addFocusRingRect(IntRect(tx, ty - collapsedMarginTop(), width(), height() + collapsedMarginTop() + collapsedMarginBottom())); - continuation->addFocusRingRects(graphicsContext, - tx - x() + continuation->containingBlock()->x(), - ty - y() + continuation->containingBlock()->y()); - } else - graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); + graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); } @@ -709,11 +550,7 @@ bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result // Check kids first. for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { - // FIXME: We have to skip over inline flows, since they can show up inside table rows - // at the moment (a demoted inline <form> for example). If we ever implement a - // table-specific hit-test method (which we should do for performance reasons anyway), - // then we can remove this check. - if (!child->hasLayer() && !child->isRenderInline() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) { + if (!child->hasLayer() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) { updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); return true; } @@ -747,7 +584,7 @@ void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty) { const FillLayer* bgLayer = style()->backgroundLayers(); Color bgColor = style()->backgroundColor(); - if (!style()->hasBackground() && element() && element()->hasTagName(HTMLNames::htmlTag)) { + 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> @@ -919,60 +756,20 @@ void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const paintFillLayerExtended(paintInfo, c, fillLayer, clipY, clipH, tx, ty, width, height, 0, op); } -IntSize RenderBox::calculateBackgroundSize(const FillLayer* bgLayer, int scaledWidth, int scaledHeight) const -{ - StyleImage* bg = bgLayer->image(); - bg->setImageContainerSize(IntSize(scaledWidth, scaledHeight)); // Use the box established by background-origin. - - if (bgLayer->isSizeSet()) { - int w = scaledWidth; - int h = scaledHeight; - Length bgWidth = bgLayer->size().width(); - Length bgHeight = bgLayer->size().height(); - - if (bgWidth.isFixed()) - w = bgWidth.value(); - else if (bgWidth.isPercent()) - w = bgWidth.calcValue(scaledWidth); - - if (bgHeight.isFixed()) - h = bgHeight.value(); - else if (bgHeight.isPercent()) - h = bgHeight.calcValue(scaledHeight); - - // If one of the values is auto we have to use the appropriate - // scale to maintain our aspect ratio. - if (bgWidth.isAuto() && !bgHeight.isAuto()) - w = bg->imageSize(this, style()->effectiveZoom()).width() * h / bg->imageSize(this, style()->effectiveZoom()).height(); - else if (!bgWidth.isAuto() && bgHeight.isAuto()) - h = bg->imageSize(this, style()->effectiveZoom()).height() * w / bg->imageSize(this, style()->effectiveZoom()).width(); - else if (bgWidth.isAuto() && bgHeight.isAuto()) { - // If both width and height are auto, we just want to use the image's - // intrinsic size. - w = bg->imageSize(this, style()->effectiveZoom()).width(); - h = bg->imageSize(this, style()->effectiveZoom()).height(); - } - - return IntSize(max(1, w), max(1, h)); - } else - return bg->imageSize(this, style()->effectiveZoom()); -} - void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*) { if (!parent()) return; - if (isRenderInline() || style()->borderImage().image() && style()->borderImage().image()->data() == image || - style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image) { + if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) || + (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) { repaint(); return; } bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true); - if (!didFullRepaint) { + if (!didFullRepaint) repaintLayerRectsForImage(image, style()->maskLayers(), false); - } } bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground) @@ -992,7 +789,7 @@ bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer int rw; int rh; - if (FrameView* frameView = static_cast<RenderView*>(layerRenderer)->frameView()) { + if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) { rw = frameView->contentsWidth(); rh = frameView->contentsHeight(); } else { @@ -1021,260 +818,6 @@ bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer return false; } -void RenderBox::calculateBackgroundImageGeometry(const FillLayer* bgLayer, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize) -{ - int pw; - int ph; - int left = 0; - int right = 0; - int top = 0; - int bottom = 0; - int cx; - int cy; - int rw = 0; - int rh = 0; - - // CSS2 chapter 14.2.1 - - if (bgLayer->attachment()) { - // Scroll - if (bgLayer->origin() != BorderFillBox) { - left = borderLeft(); - right = borderRight(); - top = borderTop(); - bottom = borderBottom(); - if (bgLayer->origin() == ContentFillBox) { - left += paddingLeft(); - right += paddingRight(); - top += paddingTop(); - bottom += paddingBottom(); - } - } - - // The background of the box generated by the root element covers the entire canvas including - // its margins. Since those were added in already, we have to factor them out when computing the - // box used by background-origin/size/position. - if (isRoot()) { - rw = width() - left - right; - rh = height() - top - bottom; - left += marginLeft(); - right += marginRight(); - top += marginTop(); - bottom += marginBottom(); - } - cx = tx; - cy = ty; - pw = w - left - right; - ph = h - top - bottom; - } else { - // Fixed - IntRect vr = viewRect(); - cx = vr.x(); - cy = vr.y(); - pw = vr.width(); - ph = vr.height(); - } - - int sx = 0; - int sy = 0; - int cw; - int ch; - - IntSize scaledImageSize; - if (isRoot() && bgLayer->attachment()) - scaledImageSize = calculateBackgroundSize(bgLayer, rw, rh); - else - scaledImageSize = calculateBackgroundSize(bgLayer, pw, ph); - - int scaledImageWidth = scaledImageSize.width(); - int scaledImageHeight = scaledImageSize.height(); - - EFillRepeat backgroundRepeat = bgLayer->repeat(); - - int xPosition; - if (isRoot() && bgLayer->attachment()) - xPosition = bgLayer->xPosition().calcMinValue(rw - scaledImageWidth, true); - else - xPosition = bgLayer->xPosition().calcMinValue(pw - scaledImageWidth, true); - if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatXFill) { - cw = pw + left + right; - sx = scaledImageWidth ? scaledImageWidth - (xPosition + left) % scaledImageWidth : 0; - } else { - cx += max(xPosition + left, 0); - sx = -min(xPosition + left, 0); - cw = scaledImageWidth + min(xPosition + left, 0); - } - - int yPosition; - if (isRoot() && bgLayer->attachment()) - yPosition = bgLayer->yPosition().calcMinValue(rh - scaledImageHeight, true); - else - yPosition = bgLayer->yPosition().calcMinValue(ph - scaledImageHeight, true); - if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatYFill) { - ch = ph + top + bottom; - sy = scaledImageHeight ? scaledImageHeight - (yPosition + top) % scaledImageHeight : 0; - } else { - cy += max(yPosition + top, 0); - sy = -min(yPosition + top, 0); - ch = scaledImageHeight + min(yPosition + top, 0); - } - - if (!bgLayer->attachment()) { - sx += max(tx - cx, 0); - sy += max(ty - cy, 0); - } - - destRect = IntRect(cx, cy, cw, ch); - destRect.intersect(IntRect(tx, ty, w, h)); - phase = IntPoint(sx, sy); - tileSize = IntSize(scaledImageWidth, scaledImageHeight); -} - -void RenderBox::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int clipY, int clipH, - int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op) -{ - GraphicsContext* context = paintInfo.context; - bool includeLeftEdge = box ? box->includeLeftEdge() : true; - bool includeRightEdge = box ? box->includeRightEdge() : true; - int bLeft = includeLeftEdge ? borderLeft() : 0; - int bRight = includeRightEdge ? borderRight() : 0; - int pLeft = includeLeftEdge ? paddingLeft() : 0; - int pRight = includeRightEdge ? paddingRight() : 0; - - bool clippedToBorderRadius = false; - if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) { - context->save(); - context->addRoundedRectClip(IntRect(tx, ty, w, h), - includeLeftEdge ? style()->borderTopLeftRadius() : IntSize(), - includeRightEdge ? style()->borderTopRightRadius() : IntSize(), - includeLeftEdge ? style()->borderBottomLeftRadius() : IntSize(), - includeRightEdge ? style()->borderBottomRightRadius() : IntSize()); - clippedToBorderRadius = true; - } - - if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) { - // Clip to the padding or content boxes as necessary. - bool includePadding = bgLayer->clip() == ContentFillBox; - int x = tx + bLeft + (includePadding ? pLeft : 0); - int y = ty + borderTop() + (includePadding ? paddingTop() : 0); - int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0); - int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0); - context->save(); - context->clip(IntRect(x, y, width, height)); - } else if (bgLayer->clip() == TextFillBox) { - // We have to draw our text into a mask that can then be used to clip background drawing. - // First figure out how big the mask has to be. It should be no bigger than what we need - // to actually render, so we should intersect the dirty rect with the border box of the background. - IntRect maskRect(tx, ty, w, h); - maskRect.intersect(paintInfo.rect); - - // Now create the mask. - auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), false); - if (!maskImage.get()) - return; - - GraphicsContext* maskImageContext = maskImage->context(); - maskImageContext->translate(-maskRect.x(), -maskRect.y()); - - // Now add the text to the clip. We do this by painting using a special paint phase that signals to - // InlineTextBoxes that they should just add their contents to the clip. - PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0); - if (box) - box->paint(info, tx - box->xPos(), ty - box->yPos()); - else - paint(info, tx, ty); - - // The mask has been created. Now we just need to clip to it. - context->save(); - context->clipToImageBuffer(maskRect, maskImage.get()); - } - - StyleImage* bg = bgLayer->image(); - bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom()); - Color bgColor = c; - - // When this style flag is set, change existing background colors and images to a solid white background. - // If there's no bg color or image, leave it untouched to avoid affecting transparency. - // We don't try to avoid loading the background images, because this style flag is only set - // when printing, and at that point we've already loaded the background images anyway. (To avoid - // loading the background images we'd have to do this check when applying styles rather than - // while rendering.) - if (style()->forceBackgroundsToWhite()) { - // Note that we can't reuse this variable below because the bgColor might be changed - bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0; - if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) { - bgColor = Color::white; - shouldPaintBackgroundImage = false; - } - } - - // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with - // no background in the child document should show the parent's background. - bool isTransparent = false; - if (!bgLayer->next() && isRoot() && !(bgColor.isValid() && bgColor.alpha() > 0) && view()->frameView()) { - Node* elt = document()->ownerElement(); - if (elt) { - if (!elt->hasTagName(frameTag)) { - // 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(); - isTransparent = !body || !body->hasLocalName(framesetTag); // Can't scroll a frameset document anyway. - } - } else - isTransparent = view()->frameView()->isTransparent(); - - // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being transparent. - if (isTransparent) - view()->frameView()->setUseSlowRepaints(); // The parent must show behind the child. - } - - // Paint the color first underneath all images. - if (!bgLayer->next()) { - IntRect rect(tx, clipY, w, clipH); - // If we have an alpha and we are painting the root element, go ahead and blend with the base background color. - if (isRoot() && (!bgColor.isValid() || bgColor.alpha() < 0xFF) && !isTransparent) { - Color baseColor = view()->frameView()->baseBackgroundColor(); - if (baseColor.alpha() > 0) { - context->save(); - context->setCompositeOperation(CompositeCopy); - context->fillRect(rect, baseColor); - context->restore(); -#ifdef ANDROID_ALLOW_TRANSPARENT_BACKGROUNDS - } -#else - } else - context->clearRect(rect); -#endif - } - - if (bgColor.isValid() && bgColor.alpha() > 0) - context->fillRect(rect, bgColor); - } - - // no progressive loading of the background image - if (shouldPaintBackgroundImage) { - IntRect destRect; - IntPoint phase; - IntSize tileSize; - - calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase, tileSize); - if (!destRect.isEmpty()) { - CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; - context->drawTiledImage(bg->image(this, tileSize), destRect, phase, tileSize, compositeOp); - } - } - - if (bgLayer->clip() != BorderFillBox) - // Undo the background clip - context->restore(); - - if (clippedToBorderRadius) - // Undo the border radius clip - context->restore(); -} - #if PLATFORM(MAC) void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText) @@ -1289,7 +832,7 @@ void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, b InlineBox* boxWrap = inlineBoxWrapper(); RootInlineBox* r = boxWrap ? boxWrap->root() : 0; if (r) { - FloatRect rootRect(tx + r->xPos(), ty + r->selectionTop(), r->width(), r->selectionHeight()); + FloatRect rootRect(tx + r->x(), ty + r->selectionTop(), r->width(), r->selectionHeight()); FloatRect imageRect(tx + x(), rootRect.y(), width(), rootRect.height()); page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false); } else { @@ -1300,7 +843,50 @@ void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, b #endif -IntRect RenderBox::getOverflowClipRect(int tx, int ty) +bool RenderBox::pushContentsClip(PaintInfo& paintInfo, int tx, int ty) +{ + if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask) + return false; + + bool isControlClip = hasControlClip(); + bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer(); + + if (!isControlClip && !isOverflowClip) + return false; + + if (paintInfo.phase == PaintPhaseOutline) + paintInfo.phase = PaintPhaseChildOutlines; + else if (paintInfo.phase == PaintPhaseChildBlockBackground) { + paintInfo.phase = PaintPhaseBlockBackground; + paintObject(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseChildBlockBackgrounds; + } + IntRect clipRect(isControlClip ? controlClipRect(tx, ty) : overflowClipRect(tx, ty)); + paintInfo.context->save(); + if (style()->hasBorderRadius()) + paintInfo.context->addRoundedRectClip(clipRect, style()->borderTopLeftRadius(), + style()->borderTopRightRadius(), + style()->borderBottomLeftRadius(), + style()->borderBottomRightRadius()); + else + paintInfo.context->clip(clipRect); + return true; +} + +void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, int tx, int ty) +{ + ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer())); + + paintInfo.context->restore(); + if (originalPhase == PaintPhaseOutline) { + paintInfo.phase = PaintPhaseSelfOutline; + paintObject(paintInfo, tx, ty); + paintInfo.phase = originalPhase; + } else if (originalPhase == PaintPhaseChildBlockBackground) + paintInfo.phase = originalPhase; +} + +IntRect RenderBox::overflowClipRect(int tx, int ty) { // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property // here. @@ -1314,15 +900,15 @@ IntRect RenderBox::getOverflowClipRect(int tx, int ty) int clipHeight = height() - bTop - borderBottom(); // Subtract out scrollbars if we have them. - if (m_layer) { - clipWidth -= m_layer->verticalScrollbarWidth(); - clipHeight -= m_layer->horizontalScrollbarHeight(); + if (layer()) { + clipWidth -= layer()->verticalScrollbarWidth(); + clipHeight -= layer()->horizontalScrollbarHeight(); } return IntRect(clipX, clipY, clipWidth, clipHeight); } -IntRect RenderBox::getClipRect(int tx, int ty) +IntRect RenderBox::clipRect(int tx, int ty) { int clipX = tx; int clipY = ty; @@ -1350,64 +936,28 @@ IntRect RenderBox::getClipRect(int tx, int ty) return IntRect(clipX, clipY, clipWidth, clipHeight); } -int RenderBox::containingBlockWidth() const +int RenderBox::containingBlockWidthForContent() const { RenderBlock* cb = containingBlock(); - if (!cb) - return 0; if (shrinkToAvoidFloats()) - return cb->lineWidth(y()); + return cb->lineWidth(y(), false); return cb->availableWidth(); } -IntSize RenderBox::offsetForPositionedInContainer(RenderObject* container) const +void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const { - if (!container->isRelPositioned() || !container->isRenderInline()) - return IntSize(); - - // When we have an enclosing relpositioned inline, we need to add in the offset of the first line - // box from the rest of the content, but only in the cases where we know we're positioned - // relative to the inline itself. - - IntSize offset; - RenderFlow* flow = static_cast<RenderFlow*>(container); - int sx; - int sy; - if (flow->firstLineBox()) { - sx = flow->firstLineBox()->xPos(); - sy = flow->firstLineBox()->yPos(); - } else { - sx = flow->staticX(); - sy = flow->staticY(); - } - - if (!hasStaticX()) - offset.setWidth(sx); - // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside - // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct - // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers - // do. - else if (!style()->isOriginalDisplayInlineType()) - // Avoid adding in the left border/padding of the containing block twice. Subtract it out. - offset.setWidth(sx - (containingBlock()->borderLeft() + containingBlock()->paddingLeft())); - - if (!hasStaticY()) - offset.setHeight(sy); - - return offset; -} + if (repaintContainer == this) + return; -FloatPoint RenderBox::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const -{ if (RenderView* v = view()) { if (v->layoutStateEnabled()) { LayoutState* layoutState = v->layoutState(); IntSize offset = layoutState->m_offset; offset.expand(x(), y()); - localPoint += offset; - if (style()->position() == RelativePosition && m_layer) - localPoint += m_layer->relativePositionOffset(); - return localPoint; + if (style()->position() == RelativePosition && layer()) + offset += layer()->relativePositionOffset(); + transformState.move(offset); + return; } } @@ -1415,21 +965,25 @@ FloatPoint RenderBox::localToAbsolute(FloatPoint localPoint, bool fixed, bool us fixed = true; RenderObject* o = container(); - if (o) { - if (useTransforms && m_layer && m_layer->transform()) { - fixed = false; // Elements with transforms act as a containing block for fixed position descendants - localPoint = m_layer->transform()->mapPoint(localPoint); - } + if (!o) + return; - localPoint += offsetFromContainer(o); + bool hasTransform = hasLayer() && layer()->transform(); + if (hasTransform) + fixed = false; // Elements with transforms act as a containing block for fixed position descendants - return o->localToAbsolute(localPoint, fixed, useTransforms); - } - - return FloatPoint(); + IntSize containerOffset = offsetFromContainer(o); + + bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); + if (useTransforms && hasTransform) + transformState.applyTransform(transformFromContainer(o, containerOffset), preserve3D); + else + transformState.move(containerOffset.width(), containerOffset.height(), preserve3D); + + o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); } -FloatPoint RenderBox::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const +void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const { // We don't expect absoluteToLocal() to be called during layout (yet) ASSERT(!view() || !view()->layoutStateEnabled()); @@ -1437,41 +991,23 @@ FloatPoint RenderBox::absoluteToLocal(FloatPoint containerPoint, bool fixed, boo if (style()->position() == FixedPosition) fixed = true; - if (useTransforms && m_layer && m_layer->transform()) - fixed = false; + bool hasTransform = hasLayer() && layer()->transform(); + if (hasTransform) + fixed = false; // Elements with transforms act as a containing block for fixed position descendants RenderObject* o = container(); - if (o) { - FloatPoint localPoint = o->absoluteToLocal(containerPoint, fixed, useTransforms); - localPoint -= offsetFromContainer(o); - if (useTransforms && m_layer && m_layer->transform()) - localPoint = m_layer->transform()->inverse().mapPoint(localPoint); - return localPoint; - } - - return FloatPoint(); -} + if (!o) + return; -FloatQuad RenderBox::localToContainerQuad(const FloatQuad& localQuad, RenderBox* repaintContainer, bool fixed) const -{ - if (repaintContainer == this) - return localQuad; + o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); - if (style()->position() == FixedPosition) - fixed = true; + IntSize containerOffset = offsetFromContainer(o); - RenderObject* o = container(); - if (o) { - FloatQuad quad = localQuad; - if (m_layer && m_layer->transform()) { - fixed = false; // Elements with transforms act as a containing block for fixed position descendants - quad = m_layer->transform()->mapQuad(quad); - } - quad += offsetFromContainer(o); - return o->localToContainerQuad(quad, repaintContainer, fixed); - } - - return FloatQuad(); + bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); + if (useTransforms && hasTransform) + transformState.applyTransform(transformFromContainer(o, containerOffset), preserve3D); + else + transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D); } IntSize RenderBox::offsetFromContainer(RenderObject* o) const @@ -1485,7 +1021,7 @@ IntSize RenderBox::offsetFromContainer(RenderObject* o) const if (!isInline() || isReplaced()) { RenderBlock* cb; if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition - && (cb = static_cast<RenderBlock*>(o))->hasColumns()) { + && (cb = toRenderBlock(o))->hasColumns()) { IntRect rect(x(), y(), 1, 1); cb->adjustRectForColumns(rect); offset.expand(rect.x(), rect.y()); @@ -1496,13 +1032,18 @@ IntSize RenderBox::offsetFromContainer(RenderObject* o) const if (o->hasOverflowClip()) offset -= toRenderBox(o)->layer()->scrolledContentOffset(); - if (style()->position() == AbsolutePosition) - offset += offsetForPositionedInContainer(o); + if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline()) + offset += toRenderInline(o)->relativePositionedInlineOffset(this); return offset; } -void RenderBox::dirtyLineBoxes(bool fullLayout, bool /*isRootLineBox*/) +InlineBox* RenderBox::createInlineBox() +{ + return new (renderArena()) InlineBox(this); +} + +void RenderBox::dirtyLineBoxes(bool fullLayout) { if (m_inlineBoxWrapper) { if (fullLayout) { @@ -1513,23 +1054,23 @@ void RenderBox::dirtyLineBoxes(bool fullLayout, bool /*isRootLineBox*/) } } -void RenderBox::position(InlineBox* box) +void RenderBox::positionLineBox(InlineBox* box) { if (isPositioned()) { // Cache the x position only if we were an INLINE type originally. bool wasInline = style()->isOriginalDisplayInlineType(); - if (wasInline && hasStaticX()) { + if (wasInline && style()->hasStaticX()) { // The value is cached in the xPos of the box. We only need this value if // our object was inline originally, since otherwise it would have ended up underneath // the inlines. - setStaticX(box->xPos()); + layer()->setStaticX(box->x()); setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. - } else if (!wasInline && hasStaticY()) { + } else if (!wasInline && style()->hasStaticY()) { // Our object was a block originally, so we make our normal flow position be // just below the line box (as though all the inlines that came before us got // wrapped in an anonymous block, which is what would have happened had we been - // in flow). This value was cached in the yPos() of the box. - setStaticY(box->yPos()); + // in flow). This value was cached in the y() of the box. + layer()->setStaticY(box->y()); setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. } @@ -1537,7 +1078,7 @@ void RenderBox::position(InlineBox* box) box->remove(); box->destroy(renderArena()); } else if (isReplaced()) { - setLocation(box->xPos(), box->yPos()); + setLocation(box->x(), box->y()); m_inlineBoxWrapper = box; } } @@ -1552,7 +1093,7 @@ void RenderBox::deleteLineBoxWrapper() } } -IntRect RenderBox::clippedOverflowRectForRepaint(RenderBox* repaintContainer) +IntRect RenderBox::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) return IntRect(); @@ -1578,18 +1119,18 @@ IntRect RenderBox::clippedOverflowRectForRepaint(RenderBox* repaintContainer) r.inflate(v->maximalOutlineSize()); } } - computeRectForRepaint(r, repaintContainer); + computeRectForRepaint(repaintContainer, r); return r; } -void RenderBox::computeRectForRepaint(IntRect& rect, RenderBox* repaintContainer, bool fixed) +void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) { if (RenderView* v = view()) { // LayoutState is only valid for root-relative repainting if (v->layoutStateEnabled() && !repaintContainer) { LayoutState* layoutState = v->layoutState(); - if (style()->position() == RelativePosition && m_layer) - rect.move(m_layer->relativePositionOffset()); + if (style()->position() == RelativePosition && layer()) + rect.move(layer()->relativePositionOffset()); rect.move(x(), y()); rect.move(layoutState->m_offset); @@ -1616,7 +1157,7 @@ void RenderBox::computeRectForRepaint(IntRect& rect, RenderBox* repaintContainer fixed = true; if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) { - RenderBlock* cb = static_cast<RenderBlock*>(o); + RenderBlock* cb = toRenderBlock(o); if (cb->hasColumns()) { IntRect repaintRect(topLeft, rect.size()); cb->adjustRectForColumns(repaintRect); @@ -1627,22 +1168,22 @@ void RenderBox::computeRectForRepaint(IntRect& rect, RenderBox* repaintContainer // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box // in the parent's coordinate space that encloses us. - if (m_layer && m_layer->transform()) { + if (layer() && layer()->transform()) { fixed = false; - rect = m_layer->transform()->mapRect(rect); + rect = layer()->transform()->mapRect(rect); // FIXME: this clobbers topLeft adjustment done for multicol above topLeft = rect.location(); topLeft.move(x(), y()); } - if (style()->position() == AbsolutePosition) - topLeft += offsetForPositionedInContainer(o); - else if (style()->position() == RelativePosition && m_layer) { + if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline()) + topLeft += toRenderInline(o)->relativePositionedInlineOffset(this); + else if (style()->position() == RelativePosition && layer()) { // Apply the relative position offset when invalidating a rectangle. The layer // is translated, but the render box isn't, so we need to do this to get the // right dirty rect. Since this is called from RenderObject::setStyle, the relative position // flag on the RenderObject has been cleared, so use the one on the style(). - topLeft += m_layer->relativePositionOffset(); + topLeft += layer()->relativePositionOffset(); } // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, @@ -1663,7 +1204,7 @@ void RenderBox::computeRectForRepaint(IntRect& rect, RenderBox* repaintContainer } else rect.setLocation(topLeft); - o->computeRectForRepaint(rect, repaintContainer, fixed); + o->computeRectForRepaint(repaintContainer, rect, fixed); } void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect) @@ -1684,30 +1225,6 @@ void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect) } } -int RenderBox::relativePositionOffsetX() const -{ - if (!style()->left().isAuto()) { - if (!style()->right().isAuto() && containingBlock()->style()->direction() == RTL) - return -style()->right().calcValue(containingBlockWidth()); - return style()->left().calcValue(containingBlockWidth()); - } - if (!style()->right().isAuto()) - return -style()->right().calcValue(containingBlockWidth()); - return 0; -} - -int RenderBox::relativePositionOffsetY() const -{ - if (!style()->top().isAuto()) { - if (!style()->top().isPercent() || containingBlock()->style()->height().isFixed()) - return style()->top().calcValue(containingBlockHeight()); - } else if (!style()->bottom().isAuto()) { - if (!style()->bottom().isPercent() || containingBlock()->style()->height().isFixed()) - return -style()->bottom().calcValue(containingBlockHeight()); - } - return 0; -} - void RenderBox::calcWidth() { #ifdef ANDROID_LAYOUT @@ -1744,7 +1261,7 @@ void RenderBox::calcWidth() Length w = (treatAsReplaced) ? Length(calcReplacedWidth(), Fixed) : style()->width(); RenderBlock* cb = containingBlock(); - int containerWidth = max(0, containingBlockWidth()); + int containerWidth = max(0, containingBlockWidthForContent()); Length marginLeft = style()->marginLeft(); Length marginRight = style()->marginRight(); @@ -2110,7 +1627,7 @@ int RenderBox::calcReplacedWidthUsing(Length width) const case Fixed: return calcContentBoxWidth(width.value()); case Percent: { - const int cw = isPositioned() ? containingBlockWidthForPositioned(container()) : containingBlockWidth(); + const int cw = isPositioned() ? containingBlockWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockWidthForContent(); if (cw > 0) return calcContentBoxWidth(width.calcMinValue(cw)); } @@ -2139,12 +1656,12 @@ int RenderBox::calcReplacedHeightUsing(Length height) const RenderObject* cb = isPositioned() ? container() : containingBlock(); while (cb->isAnonymous()) { cb = cb->containingBlock(); - static_cast<RenderBlock*>(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); + toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); } if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { ASSERT(cb->isRenderBlock()); - RenderBlock* block = static_cast<RenderBlock*>(cb); + RenderBlock* block = toRenderBlock(cb); int oldHeight = block->height(); block->calcHeight(); int newHeight = block->calcContentBoxHeight(block->contentHeight()); @@ -2152,7 +1669,7 @@ int RenderBox::calcReplacedHeightUsing(Length height) const return calcContentBoxHeight(height.calcValue(newHeight)); } - int availableHeight = isPositioned() ? containingBlockHeightForPositioned(cb) : toRenderBox(cb)->availableHeight(); + int availableHeight = isPositioned() ? containingBlockHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableHeight(); // It is necessary to use the border-box to match WinIE's broken // box model. This is essential for sizing inside @@ -2183,7 +1700,7 @@ int RenderBox::availableHeightUsing(const Length& h) const return calcContentBoxHeight(h.value()); if (isRenderView()) - return static_cast<const RenderView*>(this)->frameView()->visibleHeight(); + return toRenderView(this)->frameView()->visibleHeight(); // We need to stop here, since we don't want to increase the height of the table // artificially. We're going to rely on this cell getting expanded to some new @@ -2195,7 +1712,7 @@ int RenderBox::availableHeightUsing(const Length& h) const return calcContentBoxHeight(h.calcValue(containingBlock()->availableHeight())); if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) { - RenderBlock* block = const_cast<RenderBlock*>(static_cast<const RenderBlock*>(this)); + RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this)); int oldHeight = block->height(); block->calcHeight(); int newHeight = block->calcContentBoxHeight(block->contentHeight()); @@ -2222,75 +1739,46 @@ void RenderBox::calcVerticalMargins() m_marginBottom = style()->marginBottom().calcMinValue(cw); } -int RenderBox::staticX() const -{ - return m_layer ? m_layer->staticX() : 0; -} - -int RenderBox::staticY() const -{ - return m_layer ? m_layer->staticY() : 0; -} - -void RenderBox::setStaticX(int staticX) +int RenderBox::containingBlockWidthForPositioned(const RenderBoxModelObject* containingBlock) const { - ASSERT(isPositioned() || isRelPositioned()); - m_layer->setStaticX(staticX); -} - -void RenderBox::setStaticY(int staticY) -{ - ASSERT(isPositioned() || isRelPositioned()); - - if (staticY == m_layer->staticY()) - return; + if (containingBlock->isBox()) { + const RenderBox* containingBlockBox = toRenderBox(containingBlock); + return containingBlockBox->width() - containingBlockBox->borderLeft() - containingBlockBox->borderRight() - containingBlockBox->verticalScrollbarWidth(); + } - m_layer->setStaticY(staticY); - setChildNeedsLayout(true, false); -} - -int RenderBox::containingBlockWidthForPositioned(const RenderObject* containingBlock) const -{ - if (containingBlock->isRenderInline()) { - ASSERT(containingBlock->isRelPositioned()); - - const RenderFlow* flow = static_cast<const RenderFlow*>(containingBlock); - InlineFlowBox* first = flow->firstLineBox(); - InlineFlowBox* last = flow->lastLineBox(); + ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned()); - // If the containing block is empty, return a width of 0. - if (!first || !last) - return 0; + const RenderInline* flow = toRenderInline(containingBlock); + InlineFlowBox* first = flow->firstLineBox(); + InlineFlowBox* last = flow->lastLineBox(); - int fromLeft; - int fromRight; - if (containingBlock->style()->direction() == LTR) { - fromLeft = first->xPos() + first->borderLeft(); - fromRight = last->xPos() + last->width() - last->borderRight(); - } else { - fromRight = first->xPos() + first->width() - first->borderRight(); - fromLeft = last->xPos() + last->borderLeft(); - } + // If the containing block is empty, return a width of 0. + if (!first || !last) + return 0; - return max(0, (fromRight - fromLeft)); + int fromLeft; + int fromRight; + if (containingBlock->style()->direction() == LTR) { + fromLeft = first->x() + first->borderLeft(); + fromRight = last->x() + last->width() - last->borderRight(); + } else { + fromRight = first->x() + first->width() - first->borderRight(); + fromLeft = last->x() + last->borderLeft(); } - const RenderBox* containingBlockBox = toRenderBox(containingBlock); - return containingBlockBox->width() - containingBlockBox->borderLeft() - containingBlockBox->borderRight() - containingBlockBox->verticalScrollbarWidth(); + return max(0, (fromRight - fromLeft)); } -int RenderBox::containingBlockHeightForPositioned(const RenderObject* containingBlock) const -{ - const RenderBox* containingBlockBox = toRenderBox(containingBlock); - - int heightResult; - if (containingBlock->isRenderInline()) { +int RenderBox::containingBlockHeightForPositioned(const RenderBoxModelObject* containingBlock) const +{ + int heightResult = 0; + if (containingBlock->isBox()) + heightResult = toRenderBox(containingBlock)->height(); + else if (containingBlock->isRenderInline()) { ASSERT(containingBlock->isRelPositioned()); - heightResult = static_cast<const RenderInline*>(containingBlock)->linesBoundingBox().height(); - } else - heightResult = containingBlockBox->height(); - - return heightResult - containingBlockBox->borderTop() - containingBlockBox->borderBottom(); + heightResult = toRenderInline(containingBlock)->linesBoundingBox().height(); + } + return heightResult - containingBlock->borderTop() - containingBlock->borderBottom(); } void RenderBox::calcAbsoluteHorizontal() @@ -2327,7 +1815,7 @@ void RenderBox::calcAbsoluteHorizontal() // We don't use containingBlock(), since we may be positioned by an enclosing // relative positioned inline. - const RenderBox* containerBlock = toRenderBox(container()); + const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); const int containerWidth = containingBlockWidthForPositioned(containerBlock); @@ -2370,16 +1858,22 @@ void RenderBox::calcAbsoluteHorizontal() if (left.isAuto() && right.isAuto()) { if (containerDirection == LTR) { // 'staticX' should already have been set through layout of the parent. - int staticPosition = staticX() - containerBlock->borderLeft(); - for (RenderBox* po = parentBox(); po && po != containerBlock; po = po->parentBox()) - staticPosition += po->x(); + int staticPosition = layer()->staticX() - containerBlock->borderLeft(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { + if (po->isBox()) + staticPosition += toRenderBox(po)->x(); + } left.setValue(Fixed, staticPosition); } else { - RenderBox* po = parentBox(); + RenderObject* po = parent(); // 'staticX' should already have been set through layout of the parent. - int staticPosition = staticX() + containerWidth + containerBlock->borderRight() - po->width(); - for (; po && po != containerBlock; po = po->parentBox()) - staticPosition -= po->x(); + int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight(); + if (po->isBox()) + staticPosition -= toRenderBox(po)->width(); + for (; po && po != containerBlock; po = po->parent()) { + if (po->isBox()) + staticPosition -= toRenderBox(po)->x(); + } right.setValue(Fixed, staticPosition); } } @@ -2447,7 +1941,7 @@ void RenderBox::calcAbsoluteHorizontal() setWidth(width() + bordersPlusPadding); } -void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBox* containerBlock, TextDirection containerDirection, +void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, const int containerWidth, const int bordersPlusPadding, const Length left, const Length right, const Length marginLeft, const Length marginRight, int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos) @@ -2603,15 +2097,15 @@ void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBox* cont // Use computed values to calculate the horizontal position. // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively - // positioned, inline containing block because right now, it is using the xPos + // positioned, inline because right now, it is using the xPos // of the first line box when really it should use the last line box. When // this is fixed elsewhere, this block should be removed. - if (containerBlock->isInline() && containerBlock->style()->direction() == RTL) { - const RenderFlow* flow = static_cast<const RenderFlow*>(containerBlock); + if (containerBlock->isRenderInline() && containerBlock->style()->direction() == RTL) { + const RenderInline* flow = toRenderInline(containerBlock); InlineFlowBox* firstLine = flow->firstLineBox(); InlineFlowBox* lastLine = flow->lastLineBox(); if (firstLine && lastLine && firstLine != lastLine) { - xPos = leftValue + marginLeftValue + lastLine->borderLeft() + (lastLine->xPos() - firstLine->xPos()); + xPos = leftValue + marginLeftValue + lastLine->borderLeft() + (lastLine->x() - firstLine->x()); return; } } @@ -2634,7 +2128,7 @@ void RenderBox::calcAbsoluteVertical() // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. - const RenderBox* containerBlock = toRenderBox(container()); + const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); const int containerHeight = containingBlockHeightForPositioned(containerBlock); @@ -2665,10 +2159,10 @@ void RenderBox::calcAbsoluteVertical() // Calculate the static distance if needed. if (top.isAuto() && bottom.isAuto()) { // staticY should already have been set through layout of the parent() - int staticTop = staticY() - containerBlock->borderTop(); - for (RenderBox* po = parentBox(); po && po != containerBlock; po = po->parentBox()) { - if (!po->isTableRow()) - staticTop += po->y(); + int staticTop = layer()->staticY() - containerBlock->borderTop(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { + if (po->isBox() && !po->isTableRow()) + staticTop += toRenderBox(po)->y(); } top.setValue(Fixed, staticTop); } @@ -2728,7 +2222,7 @@ void RenderBox::calcAbsoluteVertical() setHeight(h + bordersPlusPadding); } -void RenderBox::calcAbsoluteVerticalValues(Length h, const RenderBox* containerBlock, +void RenderBox::calcAbsoluteVerticalValues(Length h, const RenderBoxModelObject* containerBlock, const int containerHeight, const int bordersPlusPadding, const Length top, const Length bottom, const Length marginTop, const Length marginBottom, int& heightValue, int& marginTopValue, int& marginBottomValue, int& yPos) @@ -2857,7 +2351,7 @@ void RenderBox::calcAbsoluteHorizontalReplaced() // We don't use containingBlock(), since we may be positioned by an enclosing // relative positioned inline. - const RenderBox* containerBlock = toRenderBox(container()); + const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); const int containerWidth = containingBlockWidthForPositioned(containerBlock); @@ -2892,16 +2386,20 @@ void RenderBox::calcAbsoluteHorizontalReplaced() // see FIXME 1 if (containerDirection == LTR) { // 'staticX' should already have been set through layout of the parent. - int staticPosition = staticX() - containerBlock->borderLeft(); - for (RenderBox* po = parentBox(); po && po != containerBlock; po = po->parentBox()) - staticPosition += po->x(); + int staticPosition = layer()->staticX() - containerBlock->borderLeft(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { + if (po->isBox()) + staticPosition += toRenderBox(po)->x(); + } left.setValue(Fixed, staticPosition); } else { - RenderBox* po = parentBox(); + RenderObject* po = parent(); // 'staticX' should already have been set through layout of the parent. - int staticPosition = staticX() + containerWidth + containerBlock->borderRight() - po->width(); - for (; po && po != containerBlock; po = po->parentBox()) - staticPosition -= po->x(); + int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight(); + for ( ; po && po != containerBlock; po = po->parent()) { + if (po->isBox()) + staticPosition += toRenderBox(po)->x(); + } right.setValue(Fixed, staticPosition); } } @@ -3009,11 +2507,11 @@ void RenderBox::calcAbsoluteHorizontalReplaced() // of the first line box when really it should use the last line box. When // this is fixed elsewhere, this block should be removed. if (containerBlock->isInline() && containerBlock->style()->direction() == RTL) { - const RenderFlow* flow = static_cast<const RenderFlow*>(containerBlock); + const RenderInline* flow = toRenderInline(containerBlock); InlineFlowBox* firstLine = flow->firstLineBox(); InlineFlowBox* lastLine = flow->lastLineBox(); if (firstLine && lastLine && firstLine != lastLine) { - m_frameRect.setX(leftValue + m_marginLeft + lastLine->borderLeft() + (lastLine->xPos() - firstLine->xPos())); + m_frameRect.setX(leftValue + m_marginLeft + lastLine->borderLeft() + (lastLine->x() - firstLine->x())); return; } } @@ -3030,7 +2528,7 @@ void RenderBox::calcAbsoluteVerticalReplaced() // the numbers correspond to numbers in spec) // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. - const RenderBox* containerBlock = toRenderBox(container()); + const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); const int containerHeight = containingBlockHeightForPositioned(containerBlock); @@ -3058,10 +2556,10 @@ void RenderBox::calcAbsoluteVerticalReplaced() // see FIXME 2 if (top.isAuto() && bottom.isAuto()) { // staticY should already have been set through layout of the parent(). - int staticTop = staticY() - containerBlock->borderTop(); - for (RenderBox* po = parentBox(); po && po != containerBlock; po = po->parentBox()) { - if (!po->isTableRow()) - staticTop += po->y(); + int staticTop = layer()->staticY() - containerBlock->borderTop(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { + if (po->isBox() && !po->isTableRow()) + staticTop += toRenderBox(po)->y(); } top.setValue(Fixed, staticTop); } @@ -3161,7 +2659,6 @@ IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWid // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements. // FIXME: What about border and padding? - const int caretWidth = 1; IntRect rect(x(), y(), caretWidth, height()); TextDirection direction = box ? box->direction() : style()->direction(); @@ -3184,7 +2681,7 @@ IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWid // // FIXME: ignoring :first-line, missing good reason to take care of int fontHeight = style()->font().height(); - if (fontHeight > rect.height() || !isReplaced() && !isTable()) + if (fontHeight > rect.height() || (!isReplaced() && !isTable())) rect.setHeight(fontHeight); if (extraWidthToEndOfLine) @@ -3225,6 +2722,118 @@ int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSe return left; } +bool RenderBox::isAfterContent(RenderObject* child) const +{ + return (child && child->style()->styleType() == AFTER && (!child->isText() || child->isBR())); +} + +VisiblePosition RenderBox::positionForPoint(const IntPoint& point) +{ + // no children...return this render object's element, if there is one, and offset 0 + if (!firstChild()) + return createVisiblePosition(firstDeepEditingPositionForNode(node())); + + int xPos = point.x(); + int yPos = point.y(); + + if (isTable() && node()) { + int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft(); + int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom(); + + if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) { + if (xPos <= right / 2) + return createVisiblePosition(firstDeepEditingPositionForNode(node())); + return createVisiblePosition(lastDeepEditingPositionForNode(node())); + } + } + + // Pass off to the closest child. + int minDist = INT_MAX; + RenderBox* closestRenderer = 0; + int newX = xPos; + int newY = yPos; + if (isTableRow()) { + newX += x(); + newY += y(); + } + for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) { + if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() ) + || renderObject->style()->visibility() != VISIBLE) + continue; + + if (!renderObject->isBox()) + continue; + + RenderBox* renderer = toRenderBox(renderObject); + + int top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? 0 : renderer->y()); + int bottom = top + renderer->contentHeight(); + int left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? 0 : renderer->x()); + int right = left + renderer->contentWidth(); + + if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) { + if (renderer->isTableRow()) + return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y()); + return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y()); + } + + // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces + // and use a different compare depending on which piece (x, y) is in. + IntPoint cmp; + if (xPos > right) { + if (yPos < top) + cmp = IntPoint(right, top); + else if (yPos > bottom) + cmp = IntPoint(right, bottom); + else + cmp = IntPoint(right, yPos); + } else if (xPos < left) { + if (yPos < top) + cmp = IntPoint(left, top); + else if (yPos > bottom) + cmp = IntPoint(left, bottom); + else + cmp = IntPoint(left, yPos); + } else { + if (yPos < top) + cmp = IntPoint(xPos, top); + else + cmp = IntPoint(xPos, bottom); + } + + int x1minusx2 = cmp.x() - xPos; + int y1minusy2 = cmp.y() - yPos; + + int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2; + if (dist < minDist) { + closestRenderer = renderer; + minDist = dist; + } + } + + if (closestRenderer) + return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y()); + + return createVisiblePosition(firstDeepEditingPositionForNode(node())); +} + +bool RenderBox::shrinkToAvoidFloats() const +{ + // FIXME: Technically we should be able to shrink replaced elements on a line, but this is difficult to accomplish, since this + // involves doing a relayout during findNextLineBreak and somehow overriding the containingBlockWidth method to return the + // current remaining width on a line. + if ((isInline() && !isHTMLMarquee()) || !avoidsFloats()) + return false; + + // All auto-width objects that avoid floats should always use lineWidth. + return style()->width().isAuto(); +} + +bool RenderBox::avoidsFloats() const +{ + return isReplaced() || hasOverflowClip() || isHR(); +} + #if ENABLE(SVG) TransformationMatrix RenderBox::localTransform() const diff --git a/WebCore/rendering/RenderBox.h b/WebCore/rendering/RenderBox.h index 33e7411..182d4e3 100644 --- a/WebCore/rendering/RenderBox.h +++ b/WebCore/rendering/RenderBox.h @@ -23,24 +23,26 @@ #ifndef RenderBox_h #define RenderBox_h -#include "RenderObject.h" +#include "RenderBoxModelObject.h" #include "ScrollTypes.h" namespace WebCore { enum WidthType { Width, MinWidth, MaxWidth }; -class RenderBox : public RenderObject { +class RenderBox : public RenderBoxModelObject { public: RenderBox(Node*); virtual ~RenderBox(); - virtual const char* renderName() const { return "RenderBox"; } + // Use this with caution! No type checking is done! + RenderBox* firstChildBox() const; + RenderBox* lastChildBox() const; int x() const { return m_frameRect.x(); } int y() const { return m_frameRect.y(); } - int width() const { ASSERT(!isRenderInline()); return m_frameRect.width(); } - int height() const { ASSERT(!isRenderInline()); return m_frameRect.height(); } + int width() const { return m_frameRect.width(); } + int height() const { return m_frameRect.height(); } void setX(int x) { m_frameRect.setX(x); } void setY(int y) { m_frameRect.setY(y); } @@ -48,7 +50,7 @@ public: void setHeight(int height) { m_frameRect.setHeight(height); } IntPoint location() const { return m_frameRect.location(); } - IntSize size() const { ASSERT(!isRenderInline()); return m_frameRect.size(); } + IntSize size() const { return m_frameRect.size(); } void setLocation(const IntPoint& location) { m_frameRect.setLocation(location); } void setLocation(int x, int y) { setLocation(IntPoint(x, y)); } @@ -56,12 +58,12 @@ public: void setSize(const IntSize& size) { m_frameRect.setSize(size); } void move(int dx, int dy) { m_frameRect.move(dx, dy); } - IntRect frameRect() const { ASSERT(!isRenderInline()); return m_frameRect; } + IntRect frameRect() const { return m_frameRect; } void setFrameRect(const IntRect& rect) { m_frameRect = rect; } IntRect borderBoxRect() const { return IntRect(0, 0, width(), height()); } - virtual IntRect borderBoundingBox() const { return borderBoxRect(); } // This will work on inlines to return the bounding box of all of the lines' border boxes. - + virtual IntRect borderBoundingBox() const { return borderBoxRect(); } + // The content area of the box (excludes padding and border). IntRect contentBoxRect() const { return IntRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), contentWidth(), contentHeight()); } // The content box in absolute coords. Ignores transforms. @@ -70,7 +72,7 @@ public: FloatQuad absoluteContentQuad() const; // Bounds of the outline box in absolute coords. Respects transforms - virtual IntRect outlineBoundsForRepaint(RenderBox* /*repaintContainer*/) const; + virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/) const; virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); // Use this with caution! No type checking is done! @@ -96,9 +98,6 @@ public: // to return the remaining width on a given line (and the height of a single line). virtual int offsetWidth() const { return width(); } virtual int offsetHeight() const { return height(); } - virtual int offsetLeft() const; - virtual int offsetTop() const; - virtual RenderBox* offsetParent() const; // More IE extensions. clientWidth and clientHeight represent the interior of an object // excluding border and scrollbar. clientLeft/Top are just the borderLeftWidth and borderTopWidth. @@ -119,26 +118,12 @@ public: virtual void setScrollLeft(int); virtual void setScrollTop(int); - bool hasHorizontalBordersPaddingOrMargin() const { return hasHorizontalBordersOrPadding() || marginLeft() != 0 || marginRight() != 0; } - bool hasHorizontalBordersOrPadding() const { return borderLeft() != 0 || borderRight() != 0 || paddingLeft() != 0 || paddingRight() != 0; } - - int marginTop() const { return m_marginTop; } - int marginBottom() const { return m_marginBottom; } - int marginLeft() const { return m_marginLeft; } - int marginRight() const { return m_marginRight; } + virtual int marginTop() const { return m_marginTop; } + virtual int marginBottom() const { return m_marginBottom; } + virtual int marginLeft() const { return m_marginLeft; } + virtual int marginRight() const { return m_marginRight; } - // Virtual since table cells override - virtual int paddingTop(bool includeIntrinsicPadding = true) const; - virtual int paddingBottom(bool includeIntrinsicPadding = true) const; - virtual int paddingLeft(bool includeIntrinsicPadding = true) const; - virtual int paddingRight(bool includeIntrinsicPadding = true) const; - - virtual int borderTop() const { return style()->borderTopWidth(); } - virtual int borderBottom() const { return style()->borderBottomWidth(); } - virtual int borderLeft() const { return style()->borderLeftWidth(); } - virtual int borderRight() const { return style()->borderRightWidth(); } - - // The following seven functions are used to implement collapsing margins. + // The following five functions are used to implement collapsing margins. // All objects know their maximal positive and negative margins. The // formula for computing a collapsed margin is |maxPosMargin| - |maxNegmargin|. // For a non-collapsing box, such as a leaf element, this formula will simply return @@ -147,8 +132,6 @@ public: virtual bool isSelfCollapsingBlock() const { return false; } int collapsedMarginTop() const { return maxTopMargin(true) - maxTopMargin(false); } int collapsedMarginBottom() const { return maxBottomMargin(true) - maxBottomMargin(false); } - virtual bool isTopMarginQuirk() const { return false; } - virtual bool isBottomMarginQuirk() const { return false; } virtual int maxTopMargin(bool positive) const { return positive ? std::max(0, marginTop()) : -std::min(0, marginTop()); } virtual int maxBottomMargin(bool positive) const { return positive ? std::max(0, marginBottom()) : -std::min(0, marginBottom()); } @@ -160,6 +143,7 @@ public: // Given a rect in the object's coordinate space, returns the corresponding rect in the reflection. IntRect reflectedRect(const IntRect&) const; + virtual void layout(); virtual void paint(PaintInfo&, int tx, int ty); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); @@ -168,14 +152,11 @@ public: virtual int minPrefWidth() const; virtual int maxPrefWidth() const; - virtual int overrideSize() const; - virtual int overrideWidth() const; - virtual int overrideHeight() const; + int overrideSize() const; + int overrideWidth() const; + int overrideHeight() const; virtual void setOverrideSize(int); - virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; - virtual FloatPoint absoluteToLocal(FloatPoint containerPoint, bool fixed = false, bool useTransforms = false) const; - virtual IntSize offsetFromContainer(RenderObject*) const; int calcBorderBoxWidth(int width) const; @@ -191,29 +172,28 @@ public: // shifted. -dwh void calcHorizontalMargins(const Length& marginLeft, const Length& marginRight, int containerWidth); - virtual void position(InlineBox*); + void positionLineBox(InlineBox*); - virtual void dirtyLineBoxes(bool fullLayout, bool isRootLineBox = false); + virtual InlineBox* createInlineBox(); + void dirtyLineBoxes(bool fullLayout); // For inline replaced elements, this function returns the inline box that owns us. Enables // the replaced RenderObject to quickly determine what line it is contained on and to easily // iterate over structures on the line. - virtual InlineBox* inlineBoxWrapper() const { return m_inlineBoxWrapper; } - virtual void setInlineBoxWrapper(InlineBox* boxWrapper) { m_inlineBoxWrapper = boxWrapper; } - virtual void deleteLineBoxWrapper(); + InlineBox* inlineBoxWrapper() const { return m_inlineBoxWrapper; } + void setInlineBoxWrapper(InlineBox* boxWrapper) { m_inlineBoxWrapper = boxWrapper; } + void deleteLineBoxWrapper(); virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); - virtual void computeRectForRepaint(IntRect&, RenderBox* repaintContainer, bool fixed = false); - IntSize offsetForPositionedInContainer(RenderObject*) const; - virtual FloatQuad localToContainerQuad(const FloatQuad&, RenderBox* repaintContainer, bool fixed = false) const; + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); + virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); virtual void repaintDuringLayoutIfMoved(const IntRect&); - virtual int containingBlockWidth() const; + virtual int containingBlockWidthForContent() const; virtual void calcWidth(); virtual void calcHeight(); @@ -247,13 +227,6 @@ public: void calcVerticalMargins(); - int relativePositionOffsetX() const; - int relativePositionOffsetY() const; - IntSize relativePositionOffset() const { return IntSize(relativePositionOffsetX(), relativePositionOffsetY()); } - - RenderLayer* layer() const { return m_layer; } - virtual bool requiresLayer() const { return isRoot() || isPositioned() || isRelPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasMask() || hasReflection(); } - virtual int verticalScrollbarWidth() const; int horizontalScrollbarHeight() const; virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); @@ -269,18 +242,14 @@ public: virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); - virtual void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, int clipY, int clipHeight, - int tx, int ty, int width, int height, InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver); - IntSize calculateBackgroundSize(const FillLayer*, int scaledWidth, int scaledHeight) const; - - virtual int staticX() const; - virtual int staticY() const; - virtual void setStaticX(int staticX); - virtual void setStaticY(int staticY); - - virtual IntRect getOverflowClipRect(int tx, int ty); - virtual IntRect getClipRect(int tx, int ty); + virtual IntRect overflowClipRect(int tx, int ty); + IntRect clipRect(int tx, int ty); + virtual bool hasControlClip() const { return false; } + virtual IntRect controlClipRect(int /*tx*/, int /*ty*/) const { return IntRect(); } + bool pushContentsClip(PaintInfo&, int tx, int ty); + void popContentsClip(PaintInfo&, PaintPhase originalPhase, int tx, int ty); + virtual void paintObject(PaintInfo&, int /*tx*/, int /*ty*/) { ASSERT_NOT_REACHED(); } virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); virtual void paintMask(PaintInfo& paintInfo, int tx, int ty); virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); @@ -299,14 +268,25 @@ public: } IntRect maskClipRect(); + + virtual VisiblePosition positionForPoint(const IntPoint&); + + void removeFloatingOrPositionedChildFromBlockLists(); + virtual int firstLineBoxBaseline() const { return -1; } + virtual int lastLineBoxBaseline() const { return -1; } + + bool shrinkToAvoidFloats() const; + virtual bool avoidsFloats() const; + #if ENABLE(SVG) virtual TransformationMatrix localTransform() const; #endif protected: - virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + virtual void updateBoxModelInfoFromStyle(); void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, int clipY, int clipHeight, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver); void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, int clipY, int clipHeight, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver); @@ -321,6 +301,9 @@ protected: virtual bool shouldCalculateSizeAsReplaced() const { return isReplaced() && !isInlineBlockOrInlineTable(); } + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; + virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const; + private: bool includeVerticalScrollbarSize() const { return hasOverflowClip() && (style()->overflowY() == OSCROLL || style()->overflowY() == OAUTO); } bool includeHorizontalScrollbarSize() const { return hasOverflowClip() && (style()->overflowX() == OSCROLL || style()->overflowX() == OAUTO); } @@ -328,18 +311,16 @@ private: void paintRootBoxDecorations(PaintInfo&, int tx, int ty); // Returns true if we did a full repaint bool repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground); - - void calculateBackgroundImageGeometry(const FillLayer*, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize); - - int containingBlockWidthForPositioned(const RenderObject* containingBlock) const; - int containingBlockHeightForPositioned(const RenderObject* containingBlock) const; + + int containingBlockWidthForPositioned(const RenderBoxModelObject* containingBlock) const; + int containingBlockHeightForPositioned(const RenderBoxModelObject* containingBlock) const; void calcAbsoluteVertical(); - void calcAbsoluteHorizontalValues(Length width, const RenderBox* cb, TextDirection containerDirection, + void calcAbsoluteHorizontalValues(Length width, const RenderBoxModelObject* cb, TextDirection containerDirection, int containerWidth, int bordersPlusPadding, Length left, Length right, Length marginLeft, Length marginRight, int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos); - void calcAbsoluteVerticalValues(Length height, const RenderBox* cb, + void calcAbsoluteVerticalValues(Length height, const RenderBoxModelObject* cb, int containerHeight, int bordersPlusPadding, Length top, Length bottom, Length marginTop, Length marginBottom, int& heightValue, int& marginTopValue, int& marginBottomValue, int& yPos); @@ -350,7 +331,10 @@ private: // This function calculates the minimum and maximum preferred widths for an object. // These values are used in shrink-to-fit layout systems. // These include tables, positioned objects, floats and flexible boxes. - virtual void calcPrefWidths() = 0; + virtual void calcPrefWidths() { setPrefWidthsDirty(false); } + +protected: + bool isAfterContent(RenderObject* child) const; private: // The width/height of the contents + borders + padding. The x/y location is relative to our container (which is not always our parent). @@ -373,15 +357,11 @@ protected: // The preferred width of the element if it never breaks any lines at all. int m_maxPrefWidth; - // A pointer to our layer if we have one. - RenderLayer* m_layer; - // For inline replaced elements, the inline box that owns us. InlineBox* m_inlineBoxWrapper; private: // Used to store state between styleWillChange and styleDidChange - static bool s_wasFloating; static bool s_hadOverflowClip; }; @@ -397,6 +377,9 @@ inline const RenderBox* toRenderBox(const RenderObject* o) return static_cast<const RenderBox*>(o); } +// This will catch anyone doing an unnecessary cast. +void toRenderBox(const RenderBox*); + inline RenderBox* RenderBox::previousSiblingBox() const { return toRenderBox(previousSibling()); @@ -412,6 +395,16 @@ inline RenderBox* RenderBox::parentBox() const return toRenderBox(parent()); } +inline RenderBox* RenderBox::firstChildBox() const +{ + return toRenderBox(firstChild()); +} + +inline RenderBox* RenderBox::lastChildBox() const +{ + return toRenderBox(lastChild()); +} + } // namespace WebCore #endif // RenderBox_h diff --git a/WebCore/rendering/RenderBoxModelObject.cpp b/WebCore/rendering/RenderBoxModelObject.cpp new file mode 100644 index 0000000..c79da7b --- /dev/null +++ b/WebCore/rendering/RenderBoxModelObject.cpp @@ -0,0 +1,1111 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) + * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2005, 2006, 2007, 2008, 2009 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 "RenderBoxModelObject.h" + +#include "GraphicsContext.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "ImageBuffer.h" +#include "RenderBlock.h" +#include "RenderInline.h" +#include "RenderLayer.h" +#include "RenderView.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +bool RenderBoxModelObject::s_wasFloating = false; + +RenderBoxModelObject::RenderBoxModelObject(Node* node) + : RenderObject(node) + , m_layer(0) +{ +} + +RenderBoxModelObject::~RenderBoxModelObject() +{ + // Our layer should have been destroyed and cleared by now + ASSERT(!hasLayer()); + ASSERT(!m_layer); +} + +void RenderBoxModelObject::destroyLayer() +{ + ASSERT(hasLayer()); + ASSERT(m_layer); + m_layer->destroy(renderArena()); + m_layer = 0; + setHasLayer(false); +} + +void RenderBoxModelObject::destroy() +{ + // This must be done before we destroy the RenderObject. + if (m_layer) + m_layer->clearClipRects(); + + // RenderObject::destroy calls back to destroyLayer() for layer destruction + RenderObject::destroy(); +} + +bool RenderBoxModelObject::hasSelfPaintingLayer() const +{ + return m_layer && m_layer->isSelfPaintingLayer(); +} + +void RenderBoxModelObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) +{ + s_wasFloating = isFloating(); + + // If our z-index changes value or our visibility changes, + // we need to dirty our stacking context's z-order list. + if (style() && newStyle) { + if (hasLayer() && (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || + style()->zIndex() != newStyle->zIndex() || + style()->visibility() != newStyle->visibility())) { + layer()->dirtyStackingContextZOrderLists(); + if (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || style()->visibility() != newStyle->visibility()) + layer()->dirtyZOrderLists(); + } + } + + RenderObject::styleWillChange(diff, newStyle); +} + +void RenderBoxModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderObject::styleDidChange(diff, oldStyle); + updateBoxModelInfoFromStyle(); + + if (requiresLayer()) { + if (!layer()) { + if (s_wasFloating && isFloating()) + setChildNeedsLayout(true); + m_layer = new (renderArena()) RenderLayer(this); + setHasLayer(true); + m_layer->insertOnlyThisLayer(); + if (parent() && !needsLayout() && containingBlock()) + m_layer->updateLayerPositions(); + } + } else if (layer() && layer()->parent()) { + setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit. + setHasReflection(false); + m_layer->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer + if (s_wasFloating && isFloating()) + setChildNeedsLayout(true); + } + + if (m_layer) + m_layer->styleChanged(diff, oldStyle); +} + +void RenderBoxModelObject::updateBoxModelInfoFromStyle() +{ + // Set the appropriate bits for a box model object. Since all bits are cleared in styleWillChange, + // we only check for bits that could possibly be set to true. + setHasBoxDecorations(style()->hasBorder() || style()->hasBackground() || style()->hasAppearance() || style()->boxShadow()); + setInline(style()->isDisplayInlineType()); + setRelPositioned(style()->position() == RelativePosition); +} + +int RenderBoxModelObject::relativePositionOffsetX() const +{ + if (!style()->left().isAuto()) { + if (!style()->right().isAuto() && containingBlock()->style()->direction() == RTL) + return -style()->right().calcValue(containingBlockWidthForContent()); + return style()->left().calcValue(containingBlockWidthForContent()); + } + if (!style()->right().isAuto()) + return -style()->right().calcValue(containingBlockWidthForContent()); + return 0; +} + +int RenderBoxModelObject::relativePositionOffsetY() const +{ + if (!style()->top().isAuto()) + return style()->top().calcValue(containingBlock()->availableHeight()); + else if (!style()->bottom().isAuto()) + return -style()->bottom().calcValue(containingBlock()->availableHeight()); + + return 0; +} + +int RenderBoxModelObject::offsetLeft() const +{ + // If the element is the HTML body element or does not have an associated box + // return 0 and stop this algorithm. + if (isBody()) + return 0; + + RenderBoxModelObject* offsetPar = offsetParent(); + int xPos = (isBox() ? toRenderBox(this)->x() : 0); + + // If the offsetParent of the element is null, or is the HTML body element, + // return the distance between the canvas origin and the left border edge + // of the element and stop this algorithm. + if (offsetPar) { + if (offsetPar->isBox() && !offsetPar->isBody()) + xPos -= toRenderBox(offsetPar)->borderLeft(); + if (!isPositioned()) { + if (isRelPositioned()) + xPos += relativePositionOffsetX(); + RenderObject* curr = parent(); + while (curr && curr != offsetPar) { + // FIXME: What are we supposed to do inside SVG content? + if (curr->isBox() && !curr->isTableRow()) + xPos += toRenderBox(curr)->x(); + curr = curr->parent(); + } + if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) + xPos += toRenderBox(offsetPar)->x(); + } + } + + return xPos; +} + +int RenderBoxModelObject::offsetTop() const +{ + // If the element is the HTML body element or does not have an associated box + // return 0 and stop this algorithm. + if (isBody()) + return 0; + + RenderBoxModelObject* offsetPar = offsetParent(); + int yPos = (isBox() ? toRenderBox(this)->y() : 0); + + // If the offsetParent of the element is null, or is the HTML body element, + // return the distance between the canvas origin and the top border edge + // of the element and stop this algorithm. + if (offsetPar) { + if (offsetPar->isBox() && !offsetPar->isBody()) + yPos -= toRenderBox(offsetPar)->borderTop(); + if (!isPositioned()) { + if (isRelPositioned()) + yPos += relativePositionOffsetY(); + RenderObject* curr = parent(); + while (curr && curr != offsetPar) { + // FIXME: What are we supposed to do inside SVG content? + if (curr->isBox() && !curr->isTableRow()) + yPos += toRenderBox(curr)->y(); + curr = curr->parent(); + } + if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) + yPos += toRenderBox(offsetPar)->y(); + } + } + return yPos; +} + +int RenderBoxModelObject::paddingTop(bool) const +{ + int w = 0; + Length padding = style()->paddingTop(); + if (padding.isPercent()) + w = containingBlock()->availableWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingBottom(bool) const +{ + int w = 0; + Length padding = style()->paddingBottom(); + if (padding.isPercent()) + w = containingBlock()->availableWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingLeft(bool) const +{ + int w = 0; + Length padding = style()->paddingLeft(); + if (padding.isPercent()) + w = containingBlock()->availableWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingRight(bool) const +{ + int w = 0; + Length padding = style()->paddingRight(); + if (padding.isPercent()) + w = containingBlock()->availableWidth(); + return padding.calcMinValue(w); +} + + +void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int clipY, int clipH, + int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op) +{ + GraphicsContext* context = paintInfo.context; + bool includeLeftEdge = box ? box->includeLeftEdge() : true; + bool includeRightEdge = box ? box->includeRightEdge() : true; + int bLeft = includeLeftEdge ? borderLeft() : 0; + int bRight = includeRightEdge ? borderRight() : 0; + int pLeft = includeLeftEdge ? paddingLeft() : 0; + int pRight = includeRightEdge ? paddingRight() : 0; + + bool clippedToBorderRadius = false; + if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) { + context->save(); + context->addRoundedRectClip(IntRect(tx, ty, w, h), + includeLeftEdge ? style()->borderTopLeftRadius() : IntSize(), + includeRightEdge ? style()->borderTopRightRadius() : IntSize(), + includeLeftEdge ? style()->borderBottomLeftRadius() : IntSize(), + includeRightEdge ? style()->borderBottomRightRadius() : IntSize()); + clippedToBorderRadius = true; + } + + if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) { + // Clip to the padding or content boxes as necessary. + bool includePadding = bgLayer->clip() == ContentFillBox; + int x = tx + bLeft + (includePadding ? pLeft : 0); + int y = ty + borderTop() + (includePadding ? paddingTop() : 0); + int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0); + int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0); + context->save(); + context->clip(IntRect(x, y, width, height)); + } else if (bgLayer->clip() == TextFillBox) { + // We have to draw our text into a mask that can then be used to clip background drawing. + // First figure out how big the mask has to be. It should be no bigger than what we need + // to actually render, so we should intersect the dirty rect with the border box of the background. + IntRect maskRect(tx, ty, w, h); + maskRect.intersect(paintInfo.rect); + + // Now create the mask. + auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), false); + if (!maskImage.get()) + return; + + GraphicsContext* maskImageContext = maskImage->context(); + maskImageContext->translate(-maskRect.x(), -maskRect.y()); + + // Now add the text to the clip. We do this by painting using a special paint phase that signals to + // InlineTextBoxes that they should just add their contents to the clip. + PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0); + if (box) + box->paint(info, tx - box->x(), ty - box->y()); + else + paint(info, tx, ty); + + // The mask has been created. Now we just need to clip to it. + context->save(); + context->clipToImageBuffer(maskRect, maskImage.get()); + } + + StyleImage* bg = bgLayer->image(); + bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom()); + Color bgColor = c; + + // When this style flag is set, change existing background colors and images to a solid white background. + // If there's no bg color or image, leave it untouched to avoid affecting transparency. + // We don't try to avoid loading the background images, because this style flag is only set + // when printing, and at that point we've already loaded the background images anyway. (To avoid + // loading the background images we'd have to do this check when applying styles rather than + // while rendering.) + if (style()->forceBackgroundsToWhite()) { + // Note that we can't reuse this variable below because the bgColor might be changed + bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0; + if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) { + bgColor = Color::white; + shouldPaintBackgroundImage = false; + } + } + + // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with + // no background in the child document should show the parent's background. + bool isTransparent = false; + if (!bgLayer->next() && isRoot() && !(bgColor.isValid() && bgColor.alpha() > 0) && view()->frameView()) { + Node* elt = document()->ownerElement(); + if (elt) { + if (!elt->hasTagName(frameTag)) { + // 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(); + isTransparent = !body || !body->hasLocalName(framesetTag); // Can't scroll a frameset document anyway. + } + } else + isTransparent = view()->frameView()->isTransparent(); + + // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being transparent. + if (isTransparent) + view()->frameView()->setUseSlowRepaints(); // The parent must show behind the child. + } + + // Paint the color first underneath all images. + if (!bgLayer->next()) { + IntRect rect(tx, clipY, w, clipH); + // If we have an alpha and we are painting the root element, go ahead and blend with the base background color. + if (isRoot() && (!bgColor.isValid() || bgColor.alpha() < 0xFF) && !isTransparent) { + Color baseColor = view()->frameView()->baseBackgroundColor(); + if (baseColor.alpha() > 0) { + context->save(); + context->setCompositeOperation(CompositeCopy); + context->fillRect(rect, baseColor); + context->restore(); +#ifdef ANDROID_ALLOW_TRANSPARENT_BACKGROUNDS + } +#else + } else + context->clearRect(rect); +#endif + } + + if (bgColor.isValid() && bgColor.alpha() > 0) + context->fillRect(rect, bgColor); + } + + // no progressive loading of the background image + if (shouldPaintBackgroundImage) { + IntRect destRect; + IntPoint phase; + IntSize tileSize; + + calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase, tileSize); + IntPoint destOrigin = destRect.location(); + destRect.intersect(paintInfo.rect); + if (!destRect.isEmpty()) { + phase += destRect.location() - destOrigin; + CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; + context->drawTiledImage(bg->image(this, tileSize), destRect, phase, tileSize, compositeOp); + } + } + + if (bgLayer->clip() != BorderFillBox) + // Undo the background clip + context->restore(); + + if (clippedToBorderRadius) + // Undo the border radius clip + context->restore(); +} + +IntSize RenderBoxModelObject::calculateBackgroundSize(const FillLayer* bgLayer, int scaledWidth, int scaledHeight) const +{ + StyleImage* bg = bgLayer->image(); + bg->setImageContainerSize(IntSize(scaledWidth, scaledHeight)); // Use the box established by background-origin. + + if (bgLayer->isSizeSet()) { + int w = scaledWidth; + int h = scaledHeight; + Length bgWidth = bgLayer->size().width(); + Length bgHeight = bgLayer->size().height(); + + if (bgWidth.isFixed()) + w = bgWidth.value(); + else if (bgWidth.isPercent()) + w = bgWidth.calcValue(scaledWidth); + + if (bgHeight.isFixed()) + h = bgHeight.value(); + else if (bgHeight.isPercent()) + h = bgHeight.calcValue(scaledHeight); + + // If one of the values is auto we have to use the appropriate + // scale to maintain our aspect ratio. + if (bgWidth.isAuto() && !bgHeight.isAuto()) + w = bg->imageSize(this, style()->effectiveZoom()).width() * h / bg->imageSize(this, style()->effectiveZoom()).height(); + else if (!bgWidth.isAuto() && bgHeight.isAuto()) + h = bg->imageSize(this, style()->effectiveZoom()).height() * w / bg->imageSize(this, style()->effectiveZoom()).width(); + else if (bgWidth.isAuto() && bgHeight.isAuto()) { + // If both width and height are auto, we just want to use the image's + // intrinsic size. + w = bg->imageSize(this, style()->effectiveZoom()).width(); + h = bg->imageSize(this, style()->effectiveZoom()).height(); + } + + return IntSize(max(1, w), max(1, h)); + } else + return bg->imageSize(this, style()->effectiveZoom()); +} + +void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* bgLayer, int tx, int ty, int w, int h, + IntRect& destRect, IntPoint& phase, IntSize& tileSize) +{ + int pw; + int ph; + int left = 0; + int right = 0; + int top = 0; + int bottom = 0; + int cx; + int cy; + int rw = 0; + int rh = 0; + + // CSS2 chapter 14.2.1 + + if (bgLayer->attachment()) { + // Scroll + if (bgLayer->origin() != BorderFillBox) { + left = borderLeft(); + right = borderRight(); + top = borderTop(); + bottom = borderBottom(); + if (bgLayer->origin() == ContentFillBox) { + left += paddingLeft(); + right += paddingRight(); + top += paddingTop(); + bottom += paddingBottom(); + } + } + + // The background of the box generated by the root element covers the entire canvas including + // its margins. Since those were added in already, we have to factor them out when computing the + // box used by background-origin/size/position. + if (isRoot()) { + rw = toRenderBox(this)->width() - left - right; + rh = toRenderBox(this)->height() - top - bottom; + left += marginLeft(); + right += marginRight(); + top += marginTop(); + bottom += marginBottom(); + } + cx = tx; + cy = ty; + pw = w - left - right; + ph = h - top - bottom; + } else { + // Fixed + IntRect vr = viewRect(); + cx = vr.x(); + cy = vr.y(); + pw = vr.width(); + ph = vr.height(); + } + + int sx = 0; + int sy = 0; + int cw; + int ch; + + IntSize scaledImageSize; + if (isRoot() && bgLayer->attachment()) + scaledImageSize = calculateBackgroundSize(bgLayer, rw, rh); + else + scaledImageSize = calculateBackgroundSize(bgLayer, pw, ph); + + int scaledImageWidth = scaledImageSize.width(); + int scaledImageHeight = scaledImageSize.height(); + + EFillRepeat backgroundRepeat = bgLayer->repeat(); + + int xPosition; + if (isRoot() && bgLayer->attachment()) + xPosition = bgLayer->xPosition().calcMinValue(rw - scaledImageWidth, true); + else + xPosition = bgLayer->xPosition().calcMinValue(pw - scaledImageWidth, true); + if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatXFill) { + cw = pw + left + right; + sx = scaledImageWidth ? scaledImageWidth - (xPosition + left) % scaledImageWidth : 0; + } else { + cx += max(xPosition + left, 0); + sx = -min(xPosition + left, 0); + cw = scaledImageWidth + min(xPosition + left, 0); + } + + int yPosition; + if (isRoot() && bgLayer->attachment()) + yPosition = bgLayer->yPosition().calcMinValue(rh - scaledImageHeight, true); + else + yPosition = bgLayer->yPosition().calcMinValue(ph - scaledImageHeight, true); + if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatYFill) { + ch = ph + top + bottom; + sy = scaledImageHeight ? scaledImageHeight - (yPosition + top) % scaledImageHeight : 0; + } else { + cy += max(yPosition + top, 0); + sy = -min(yPosition + top, 0); + ch = scaledImageHeight + min(yPosition + top, 0); + } + + if (!bgLayer->attachment()) { + sx += max(tx - cx, 0); + sy += max(ty - cy, 0); + } + + destRect = IntRect(cx, cy, cw, ch); + destRect.intersect(IntRect(tx, ty, w, h)); + phase = IntPoint(sx, sy); + tileSize = IntSize(scaledImageWidth, scaledImageHeight); +} + +int RenderBoxModelObject::verticalPosition(bool firstLine) const +{ + // This method determines the vertical position for inline elements. + ASSERT(isInline()); + if (!isInline()) + return 0; + + int vpos = 0; + EVerticalAlign va = style()->verticalAlign(); + if (va == TOP) + vpos = PositionTop; + else if (va == BOTTOM) + vpos = PositionBottom; + else { + bool checkParent = parent()->isRenderInline() && parent()->style()->verticalAlign() != TOP && parent()->style()->verticalAlign() != BOTTOM; + vpos = checkParent ? toRenderInline(parent())->verticalPositionFromCache(firstLine) : 0; + // don't allow elements nested inside text-top to have a different valignment. + if (va == BASELINE) + return vpos; + + const Font& f = parent()->style(firstLine)->font(); + int fontsize = f.pixelSize(); + + if (va == SUB) + vpos += fontsize / 5 + 1; + else if (va == SUPER) + vpos -= fontsize / 3 + 1; + else if (va == TEXT_TOP) + vpos += baselinePosition(firstLine) - f.ascent(); + else if (va == MIDDLE) + vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine) / 2 + baselinePosition(firstLine); + else if (va == TEXT_BOTTOM) { + vpos += f.descent(); + if (!isReplaced()) // lineHeight - baselinePosition is always 0 for replaced elements, so don't bother wasting time in that case. + vpos -= (lineHeight(firstLine) - baselinePosition(firstLine)); + } else if (va == BASELINE_MIDDLE) + vpos += -lineHeight(firstLine) / 2 + baselinePosition(firstLine); + else if (va == LENGTH) + vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine)); + } + + return vpos; +} + +bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style, + const NinePieceImage& ninePieceImage, CompositeOperator op) +{ + StyleImage* styleImage = ninePieceImage.image(); + if (!styleImage) + return false; + + if (!styleImage->isLoaded()) + return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either. + + if (!styleImage->canRender(style->effectiveZoom())) + return false; + + // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function + // doesn't have any understanding of the zoom that is in effect on the tile. + styleImage->setImageContainerSize(IntSize(w, h)); + IntSize imageSize = styleImage->imageSize(this, 1.0f); + int imageWidth = imageSize.width(); + int imageHeight = imageSize.height(); + + int topSlice = min(imageHeight, ninePieceImage.m_slices.top().calcValue(imageHeight)); + int bottomSlice = min(imageHeight, ninePieceImage.m_slices.bottom().calcValue(imageHeight)); + int leftSlice = min(imageWidth, ninePieceImage.m_slices.left().calcValue(imageWidth)); + int rightSlice = min(imageWidth, ninePieceImage.m_slices.right().calcValue(imageWidth)); + + ENinePieceImageRule hRule = ninePieceImage.horizontalRule(); + ENinePieceImageRule vRule = ninePieceImage.verticalRule(); + + bool fitToBorder = style->borderImage() == ninePieceImage; + + int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice; + int topWidth = fitToBorder ? style->borderTopWidth() : topSlice; + int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice; + int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice; + + bool drawLeft = leftSlice > 0 && leftWidth > 0; + bool drawTop = topSlice > 0 && topWidth > 0; + bool drawRight = rightSlice > 0 && rightWidth > 0; + bool drawBottom = bottomSlice > 0 && bottomWidth > 0; + bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 && + (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0; + + Image* image = styleImage->image(this, imageSize); + + if (drawLeft) { + // Paint the top and bottom left corners. + + // The top left corner rect is (tx, ty, leftWidth, topWidth) + // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice) + if (drawTop) + graphicsContext->drawImage(image, IntRect(tx, ty, leftWidth, topWidth), + IntRect(0, 0, leftSlice, topSlice), op); + + // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth) + // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice) + if (drawBottom) + graphicsContext->drawImage(image, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth), + IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op); + + // Paint the left edge. + // Have to scale and tile into the border rect. + graphicsContext->drawTiledImage(image, IntRect(tx, ty + topWidth, leftWidth, + h - topWidth - bottomWidth), + IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice), + Image::StretchTile, (Image::TileRule)vRule, op); + } + + if (drawRight) { + // Paint the top and bottom right corners + // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth) + // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice) + if (drawTop) + graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth), + IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op); + + // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth) + // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice) + if (drawBottom) + graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth), + IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op); + + // Paint the right edge. + graphicsContext->drawTiledImage(image, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth, + h - topWidth - bottomWidth), + IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice), + Image::StretchTile, (Image::TileRule)vRule, op); + } + + // Paint the top edge. + if (drawTop) + graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth), + IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice), + (Image::TileRule)hRule, Image::StretchTile, op); + + // Paint the bottom edge. + if (drawBottom) + graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + h - bottomWidth, + w - leftWidth - rightWidth, bottomWidth), + IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice), + (Image::TileRule)hRule, Image::StretchTile, op); + + // Paint the middle. + if (drawMiddle) + graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth, + h - topWidth - bottomWidth), + IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice), + (Image::TileRule)hRule, (Image::TileRule)vRule, op); + + return true; +} + +void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, + const RenderStyle* style, bool begin, bool end) +{ + if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) + return; + + const Color& tc = style->borderTopColor(); + const Color& bc = style->borderBottomColor(); + const Color& lc = style->borderLeftColor(); + const Color& rc = style->borderRightColor(); + + bool tt = style->borderTopIsTransparent(); + bool bt = style->borderBottomIsTransparent(); + bool rt = style->borderRightIsTransparent(); + bool lt = style->borderLeftIsTransparent(); + + EBorderStyle ts = style->borderTopStyle(); + EBorderStyle bs = style->borderBottomStyle(); + EBorderStyle ls = style->borderLeftStyle(); + EBorderStyle rs = style->borderRightStyle(); + + bool renderTop = ts > BHIDDEN && !tt; + bool renderLeft = ls > BHIDDEN && begin && !lt; + bool renderRight = rs > BHIDDEN && end && !rt; + bool renderBottom = bs > BHIDDEN && !bt; + + // Need sufficient width and height to contain border radius curves. Sanity check our border radii + // and our width/height values to make sure the curves can all fit. If not, then we won't paint + // any border radii. + bool renderRadii = false; + IntSize topLeft = style->borderTopLeftRadius(); + IntSize topRight = style->borderTopRightRadius(); + IntSize bottomLeft = style->borderBottomLeftRadius(); + IntSize bottomRight = style->borderBottomRightRadius(); + + if (style->hasBorderRadius() && + static_cast<unsigned>(w) >= static_cast<unsigned>(topLeft.width()) + static_cast<unsigned>(topRight.width()) && + static_cast<unsigned>(w) >= static_cast<unsigned>(bottomLeft.width()) + static_cast<unsigned>(bottomRight.width()) && + static_cast<unsigned>(h) >= static_cast<unsigned>(topLeft.height()) + static_cast<unsigned>(bottomLeft.height()) && + static_cast<unsigned>(h) >= static_cast<unsigned>(topRight.height()) + static_cast<unsigned>(bottomRight.height())) + renderRadii = true; + + // Clip to the rounded rectangle. + if (renderRadii) { + graphicsContext->save(); + graphicsContext->addRoundedRectClip(IntRect(tx, ty, w, h), topLeft, topRight, bottomLeft, bottomRight); + } + + int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan; + float thickness; + bool upperLeftBorderStylesMatch = renderLeft && (ts == ls) && (tc == lc); + bool upperRightBorderStylesMatch = renderRight && (ts == rs) && (tc == rc) && (ts != OUTSET) && (ts != RIDGE) && (ts != INSET) && (ts != GROOVE); + bool lowerLeftBorderStylesMatch = renderLeft && (bs == ls) && (bc == lc) && (bs != OUTSET) && (bs != RIDGE) && (bs != INSET) && (bs != GROOVE); + bool lowerRightBorderStylesMatch = renderRight && (bs == rs) && (bc == rc); + + if (renderTop) { + bool ignore_left = (renderRadii && topLeft.width() > 0) || + (tc == lc && tt == lt && ts >= OUTSET && + (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); + + bool ignore_right = (renderRadii && topRight.width() > 0) || + (tc == rc && tt == rt && ts >= OUTSET && + (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); + + int x = tx; + int x2 = tx + w; + if (renderRadii) { + x += topLeft.width(); + x2 -= topRight.width(); + } + + drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, + ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); + + if (renderRadii) { + int leftY = ty; + + // We make the arc double thick and let the clip rect take care of clipping the extra off. + // We're doing this because it doesn't seem possible to match the curve of the clip exactly + // with the arc-drawing function. + thickness = style->borderTopWidth() * 2; + + if (topLeft.width()) { + int leftX = tx; + // The inner clip clips inside the arc. This is especially important for 1px borders. + bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width()) + && (style->borderTopWidth() < topLeft.height()) + && (ts != DOUBLE || style->borderTopWidth() > 6); + if (applyLeftInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2), + style->borderTopWidth()); + } + + firstAngleStart = 90; + firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45; + + // Draw upper left arc + drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan, + BSTop, tc, style->color(), ts, true); + if (applyLeftInnerClip) + graphicsContext->restore(); + } + + if (topRight.width()) { + int rightX = tx + w - topRight.width() * 2; + bool applyRightInnerClip = (style->borderRightWidth() < topRight.width()) + && (style->borderTopWidth() < topRight.height()) + && (ts != DOUBLE || style->borderTopWidth() > 6); + if (applyRightInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2), + style->borderTopWidth()); + } + + if (upperRightBorderStylesMatch) { + secondAngleStart = 0; + secondAngleSpan = 90; + } else { + secondAngleStart = 45; + secondAngleSpan = 45; + } + + // Draw upper right arc + drawArcForBoxSide(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan, + BSTop, tc, style->color(), ts, false); + if (applyRightInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderBottom) { + bool ignore_left = (renderRadii && bottomLeft.width() > 0) || + (bc == lc && bt == lt && bs >= OUTSET && + (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); + + bool ignore_right = (renderRadii && bottomRight.width() > 0) || + (bc == rc && bt == rt && bs >= OUTSET && + (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); + + int x = tx; + int x2 = tx + w; + if (renderRadii) { + x += bottomLeft.width(); + x2 -= bottomRight.width(); + } + + drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bc, style->color(), bs, + ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); + + if (renderRadii) { + thickness = style->borderBottomWidth() * 2; + + if (bottomLeft.width()) { + int leftX = tx; + int leftY = ty + h - bottomLeft.height() * 2; + bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width()) + && (style->borderBottomWidth() < bottomLeft.height()) + && (bs != DOUBLE || style->borderBottomWidth() > 6); + if (applyLeftInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2), + style->borderBottomWidth()); + } + + if (lowerLeftBorderStylesMatch) { + firstAngleStart = 180; + firstAngleSpan = 90; + } else { + firstAngleStart = 225; + firstAngleSpan = 45; + } + + // Draw lower left arc + drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan, + BSBottom, bc, style->color(), bs, true); + if (applyLeftInnerClip) + graphicsContext->restore(); + } + + if (bottomRight.width()) { + int rightY = ty + h - bottomRight.height() * 2; + int rightX = tx + w - bottomRight.width() * 2; + bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width()) + && (style->borderBottomWidth() < bottomRight.height()) + && (bs != DOUBLE || style->borderBottomWidth() > 6); + if (applyRightInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2), + style->borderBottomWidth()); + } + + secondAngleStart = 270; + secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45; + + // Draw lower right arc + drawArcForBoxSide(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan, + BSBottom, bc, style->color(), bs, false); + if (applyRightInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderLeft) { + bool ignore_top = (renderRadii && topLeft.height() > 0) || + (tc == lc && tt == lt && ls >= OUTSET && + (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); + + bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) || + (bc == lc && bt == lt && ls >= OUTSET && + (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); + + int y = ty; + int y2 = ty + h; + if (renderRadii) { + y += topLeft.height(); + y2 -= bottomLeft.height(); + } + + drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, lc, style->color(), ls, + ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); + + if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) { + int topX = tx; + thickness = style->borderLeftWidth() * 2; + + if (!upperLeftBorderStylesMatch && topLeft.width()) { + int topY = ty; + bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width()) + && (style->borderTopWidth() < topLeft.height()) + && (ls != DOUBLE || style->borderLeftWidth() > 6); + if (applyTopInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2), + style->borderLeftWidth()); + } + + firstAngleStart = 135; + firstAngleSpan = 45; + + // Draw top left arc + drawArcForBoxSide(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan, + BSLeft, lc, style->color(), ls, true); + if (applyTopInnerClip) + graphicsContext->restore(); + } + + if (!lowerLeftBorderStylesMatch && bottomLeft.width()) { + int bottomY = ty + h - bottomLeft.height() * 2; + bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width()) + && (style->borderBottomWidth() < bottomLeft.height()) + && (ls != DOUBLE || style->borderLeftWidth() > 6); + if (applyBottomInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2), + style->borderLeftWidth()); + } + + secondAngleStart = 180; + secondAngleSpan = 45; + + // Draw bottom left arc + drawArcForBoxSide(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan, + BSLeft, lc, style->color(), ls, false); + if (applyBottomInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderRight) { + bool ignore_top = (renderRadii && topRight.height() > 0) || + ((tc == rc) && (tt == rt) && + (rs >= DOTTED || rs == INSET) && + (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); + + bool ignore_bottom = (renderRadii && bottomRight.height() > 0) || + ((bc == rc) && (bt == rt) && + (rs >= DOTTED || rs == INSET) && + (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); + + int y = ty; + int y2 = ty + h; + if (renderRadii) { + y += topRight.height(); + y2 -= bottomRight.height(); + } + + drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rc, style->color(), rs, + ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); + + if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) { + thickness = style->borderRightWidth() * 2; + + if (!upperRightBorderStylesMatch && topRight.width()) { + int topX = tx + w - topRight.width() * 2; + int topY = ty; + bool applyTopInnerClip = (style->borderRightWidth() < topRight.width()) + && (style->borderTopWidth() < topRight.height()) + && (rs != DOUBLE || style->borderRightWidth() > 6); + if (applyTopInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2), + style->borderRightWidth()); + } + + firstAngleStart = 0; + firstAngleSpan = 45; + + // Draw top right arc + drawArcForBoxSide(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan, + BSRight, rc, style->color(), rs, true); + if (applyTopInnerClip) + graphicsContext->restore(); + } + + if (!lowerRightBorderStylesMatch && bottomRight.width()) { + int bottomX = tx + w - bottomRight.width() * 2; + int bottomY = ty + h - bottomRight.height() * 2; + bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width()) + && (style->borderBottomWidth() < bottomRight.height()) + && (rs != DOUBLE || style->borderRightWidth() > 6); + if (applyBottomInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2), + style->borderRightWidth()); + } + + secondAngleStart = 315; + secondAngleSpan = 45; + + // Draw bottom right arc + drawArcForBoxSide(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan, + BSRight, rc, style->color(), rs, false); + if (applyBottomInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderRadii) + graphicsContext->restore(); +} + +void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, bool begin, bool end) +{ + // FIXME: Deal with border-image. Would be great to use border-image as a mask. + + IntRect rect(tx, ty, w, h); + bool hasBorderRadius = s->hasBorderRadius(); + bool hasOpaqueBackground = s->backgroundColor().isValid() && s->backgroundColor().alpha() == 255; + for (ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next) { + context->save(); + + IntSize shadowOffset(shadow->x, shadow->y); + int shadowBlur = shadow->blur; + IntRect fillRect(rect); + + if (hasBorderRadius) { + IntRect shadowRect(rect); + shadowRect.inflate(shadowBlur); + shadowRect.move(shadowOffset); + context->clip(shadowRect); + + // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not + // bleed in (due to antialiasing) if the context is transformed. + IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 1, 0); + shadowOffset -= extraOffset; + fillRect.move(extraOffset); + } + + context->setShadow(shadowOffset, shadowBlur, shadow->color); + if (hasBorderRadius) { + IntSize topLeft = begin ? s->borderTopLeftRadius() : IntSize(); + IntSize topRight = end ? s->borderTopRightRadius() : IntSize(); + IntSize bottomLeft = begin ? s->borderBottomLeftRadius() : IntSize(); + IntSize bottomRight = end ? s->borderBottomRightRadius() : IntSize(); + if (!hasOpaqueBackground) + context->clipOutRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); + context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black); + } else { + if (!hasOpaqueBackground) + context->clipOut(rect); + context->fillRect(fillRect, Color::black); + } + context->restore(); + } +} + +int RenderBoxModelObject::containingBlockWidthForContent() const +{ + return containingBlock()->availableWidth(); +} + +} // namespace WebCore diff --git a/WebCore/rendering/RenderBoxModelObject.h b/WebCore/rendering/RenderBoxModelObject.h new file mode 100644 index 0000000..161e5d2 --- /dev/null +++ b/WebCore/rendering/RenderBoxModelObject.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2006, 2007, 2009 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 RenderBoxModelObject_h +#define RenderBoxModelObject_h + +#include "RenderObject.h" + +namespace WebCore { + +// Values for vertical alignment. +const int PositionTop = -0x7fffffff; +const int PositionBottom = 0x7fffffff; +const int PositionUndefined = 0x80000000; + +// This class is the base for all objects that adhere to the CSS box model as described +// at http://www.w3.org/TR/CSS21/box.html + +class RenderBoxModelObject : public RenderObject { +public: + RenderBoxModelObject(Node*); + virtual ~RenderBoxModelObject(); + + virtual void destroy(); + + int relativePositionOffsetX() const; + int relativePositionOffsetY() const; + IntSize relativePositionOffset() const { return IntSize(relativePositionOffsetX(), relativePositionOffsetY()); } + + // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines (RenderFlow) + // to return the remaining width on a given line (and the height of a single line). + virtual int offsetLeft() const; + virtual int offsetTop() const; + virtual int offsetWidth() const = 0; + virtual int offsetHeight() const = 0; + + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + virtual void updateBoxModelInfoFromStyle(); + + bool hasSelfPaintingLayer() const; + RenderLayer* layer() const { return m_layer; } + virtual bool requiresLayer() const { return isRoot() || isPositioned() || isRelPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasMask() || hasReflection(); } + + // This will work on inlines to return the bounding box of all of the lines' border boxes. + virtual IntRect borderBoundingBox() const = 0; + + // Virtual since table cells override + virtual int paddingTop(bool includeIntrinsicPadding = true) const; + virtual int paddingBottom(bool includeIntrinsicPadding = true) const; + virtual int paddingLeft(bool includeIntrinsicPadding = true) const; + virtual int paddingRight(bool includeIntrinsicPadding = true) const; + + virtual int borderTop() const { return style()->borderTopWidth(); } + virtual int borderBottom() const { return style()->borderBottomWidth(); } + virtual int borderLeft() const { return style()->borderLeftWidth(); } + virtual int borderRight() const { return style()->borderRightWidth(); } + + virtual int marginTop() const = 0; + virtual int marginBottom() const = 0; + virtual int marginLeft() const = 0; + virtual int marginRight() const = 0; + + bool hasHorizontalBordersPaddingOrMargin() const { return hasHorizontalBordersOrPadding() || marginLeft() != 0 || marginRight() != 0; } + bool hasHorizontalBordersOrPadding() const { return borderLeft() != 0 || borderRight() != 0 || paddingLeft() != 0 || paddingRight() != 0; } + + virtual int containingBlockWidthForContent() const; + + virtual void childBecameNonInline(RenderObject* /*child*/) { } + + 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*, bool begin = true, bool end = true); + virtual void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, int clipY, int clipHeight, + int tx, int ty, int width, int height, InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver); + + // The difference between this inline's baseline position and the line's baseline position. + int verticalPosition(bool firstLine) const; + + // Called by RenderObject::destroy() (and RenderWidget::destroy()) and is the only way layers should ever be destroyed + void destroyLayer(); + +protected: + void calculateBackgroundImageGeometry(const FillLayer*, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize); + IntSize calculateBackgroundSize(const FillLayer*, int scaledWidth, int scaledHeight) const; + +private: + virtual bool isBoxModelObject() const { return true; } + friend class RenderView; + + RenderLayer* m_layer; + + // Used to store state between styleWillChange and styleDidChange + static bool s_wasFloating; +}; + +inline RenderBoxModelObject* toRenderBoxModelObject(RenderObject* o) +{ + ASSERT(!o || o->isBoxModelObject()); + return static_cast<RenderBoxModelObject*>(o); +} + +inline const RenderBoxModelObject* toRenderBoxModelObject(const RenderObject* o) +{ + ASSERT(!o || o->isBoxModelObject()); + return static_cast<const RenderBoxModelObject*>(o); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderBoxModelObject(const RenderBoxModelObject*); + +} // namespace WebCore + +#endif // RenderBoxModelObject_h diff --git a/WebCore/rendering/RenderButton.cpp b/WebCore/rendering/RenderButton.cpp index f7ccd0c..d64d7c3 100644 --- a/WebCore/rendering/RenderButton.cpp +++ b/WebCore/rendering/RenderButton.cpp @@ -69,7 +69,7 @@ void RenderButton::removeChild(RenderObject* oldChild) m_inner->removeChild(oldChild); } -void RenderButton::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +void RenderButton::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { if (m_inner) { // RenderBlock::setStyle is going to apply a new style to the inner block, which @@ -81,7 +81,7 @@ void RenderButton::styleWillChange(RenderStyle::Diff diff, const RenderStyle* ne RenderBlock::styleWillChange(diff, newStyle); } -void RenderButton::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderButton::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); @@ -108,23 +108,26 @@ void RenderButton::setupInnerStyle(RenderStyle* innerStyle) // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is // safe to modify. innerStyle->setBoxFlex(1.0f); - if (style()->hasAppearance()) - theme()->adjustButtonInnerStyle(innerStyle); + + innerStyle->setPaddingTop(Length(theme()->buttonInternalPaddingTop(), Fixed)); + innerStyle->setPaddingRight(Length(theme()->buttonInternalPaddingRight(), Fixed)); + innerStyle->setPaddingBottom(Length(theme()->buttonInternalPaddingBottom(), Fixed)); + innerStyle->setPaddingLeft(Length(theme()->buttonInternalPaddingLeft(), Fixed)); } void RenderButton::updateFromElement() { // If we're an input element, we may need to change our button text. - if (element()->hasTagName(inputTag)) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(element()); + if (node()->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); String value = input->valueWithDefault(); setText(value); } #if ENABLE(WML) - else if (element()->hasTagName(WMLNames::doTag)) { - WMLDoElement* doElement = static_cast<WMLDoElement*>(element()); + else if (node()->hasTagName(WMLNames::doTag)) { + WMLDoElement* doElement = static_cast<WMLDoElement*>(node()); String value = doElement->label(); if (value.isEmpty()) @@ -140,7 +143,7 @@ bool RenderButton::canHaveChildren() const // Input elements can't have children, but button elements can. We'll // write the code assuming any other button types that might emerge in the future // can also have children. - return !element()->hasTagName(inputTag); + return !node()->hasTagName(inputTag); } void RenderButton::setText(const String& str) @@ -161,12 +164,12 @@ void RenderButton::setText(const String& str) } } -void RenderButton::updateBeforeAfterContent(RenderStyle::PseudoId type) +void RenderButton::updateBeforeAfterContent(PseudoId type) { if (m_inner) - m_inner->updateBeforeAfterContentForContainer(type, this); + m_inner->children()->updateBeforeAfterContent(m_inner, type, this); else - updateBeforeAfterContentForContainer(type, this); + children()->updateBeforeAfterContent(this, type); } IntRect RenderButton::controlClipRect(int tx, int ty) const diff --git a/WebCore/rendering/RenderButton.h b/WebCore/rendering/RenderButton.h index 24e4767..89f7cf8 100644 --- a/WebCore/rendering/RenderButton.h +++ b/WebCore/rendering/RenderButton.h @@ -39,6 +39,7 @@ public: RenderButton(Node*); virtual const char* renderName() const { return "RenderButton"; } + virtual bool isRenderButton() const { return true; } virtual void addChild(RenderObject* newChild, RenderObject *beforeChild = 0); virtual void removeChild(RenderObject*); @@ -48,7 +49,7 @@ public: void setupInnerStyle(RenderStyle*); virtual void updateFromElement(); - virtual void updateBeforeAfterContent(RenderStyle::PseudoId); + virtual void updateBeforeAfterContent(PseudoId); virtual bool hasControlClip() const { return true; } virtual IntRect controlClipRect(int /*tx*/, int /*ty*/) const; @@ -58,8 +59,8 @@ public: virtual bool canHaveChildren() const; protected: - virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); virtual bool hasLineIfEmpty() const { return true; } @@ -72,6 +73,21 @@ protected: bool m_default; }; +inline RenderButton* toRenderButton(RenderObject* o) +{ + ASSERT(!o || o->isRenderButton()); + return static_cast<RenderButton*>(o); +} + +inline const RenderButton* toRenderButton(const RenderObject* o) +{ + ASSERT(!o || o->isRenderButton()); + return static_cast<const RenderButton*>(o); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderButton(const RenderButton*); + } // namespace WebCore #endif // RenderButton_h diff --git a/WebCore/rendering/RenderContainer.cpp b/WebCore/rendering/RenderContainer.cpp deleted file mode 100644 index dd39d9f..0000000 --- a/WebCore/rendering/RenderContainer.cpp +++ /dev/null @@ -1,726 +0,0 @@ -/** - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2000 Dirk Mueller (mueller@kde.org) - * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) - * - * 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 "RenderContainer.h" - -#include "AXObjectCache.h" -#include "Document.h" -#include "RenderCounter.h" -#include "RenderImageGeneratedContent.h" -#include "RenderInline.h" -#include "RenderLayer.h" -#include "RenderListItem.h" -#include "RenderTable.h" -#include "RenderTextFragment.h" -#include "RenderView.h" -#include "htmlediting.h" - -namespace WebCore { - -RenderContainer::RenderContainer(Node* node) - : RenderBox(node) - , m_firstChild(0) - , m_lastChild(0) -{ -} - -RenderContainer::~RenderContainer() -{ -} - -void RenderContainer::destroy() -{ - destroyLeftoverChildren(); - RenderBox::destroy(); -} - -void RenderContainer::destroyLeftoverChildren() -{ - while (m_firstChild) { - if (m_firstChild->isListMarker() || (m_firstChild->style()->styleType() == RenderStyle::FIRST_LETTER && !m_firstChild->isText())) - m_firstChild->remove(); // List markers are owned by their enclosing list and so don't get destroyed by this container. Similarly, first letters are destroyed by their remaining text fragment. - else { - // Destroy any anonymous children remaining in the render tree, as well as implicit (shadow) DOM elements like those used in the engine-based text fields. - if (m_firstChild->element()) - m_firstChild->element()->setRenderer(0); - m_firstChild->destroy(); - } - } -} - -bool RenderContainer::canHaveChildren() const -{ - return true; -} - -static void updateListMarkerNumbers(RenderObject* child) -{ - for (RenderObject* r = child; r; r = r->nextSibling()) - if (r->isListItem()) - static_cast<RenderListItem*>(r)->updateValue(); -} - -void RenderContainer::addChild(RenderObject* newChild, RenderObject* beforeChild) -{ - bool needsTable = false; - - if (newChild->isListItem()) - updateListMarkerNumbers(beforeChild ? beforeChild : m_lastChild); - else if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) - needsTable = !isTable(); - else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) - needsTable = !isTable(); - else if (newChild->isTableSection()) - needsTable = !isTable(); - else if (newChild->isTableRow()) - needsTable = !isTableSection(); - else if (newChild->isTableCell()) { - needsTable = !isTableRow(); - // I'm not 100% sure this is the best way to fix this, but without this - // change we recurse infinitely when trying to render the CSS2 test page: - // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html. - // See Radar 2925291. - if (needsTable && isTableCell() && !m_firstChild && !newChild->isTableCell()) - needsTable = false; - } - - if (needsTable) { - RenderTable* table; - RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : m_lastChild; - if (afterChild && afterChild->isAnonymous() && afterChild->isTable()) - table = static_cast<RenderTable*>(afterChild); - else { - table = new (renderArena()) RenderTable(document() /* is anonymous */); - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(style()); - newStyle->setDisplay(TABLE); - table->setStyle(newStyle.release()); - addChild(table, beforeChild); - } - table->addChild(newChild); - } else { - // just add it... - insertChildNode(newChild, beforeChild); - } - - if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { - RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalText(); - if (textToTransform) - toRenderText(newChild)->setText(textToTransform.release(), true); - } -} - -RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild, bool fullRemove) -{ - ASSERT(oldChild->parent() == this); - - // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or - // that a positioned child got yanked). We also repaint, so that the area exposed when the child - // disappears gets repainted properly. - if (!documentBeingDestroyed() && fullRemove && oldChild->m_everHadLayout) { - oldChild->setNeedsLayoutAndPrefWidthsRecalc(); - oldChild->repaint(); - } - - // If we have a line box wrapper, delete it. - oldChild->deleteLineBoxWrapper(); - - if (!documentBeingDestroyed() && fullRemove) { - // if we remove visible child from an invisible parent, we don't know the layer visibility any more - RenderLayer* layer = 0; - if (m_style->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->hasLayer()) { - layer = enclosingLayer(); - layer->dirtyVisibleContentStatus(); - } - - // Keep our layer hierarchy updated. - if (oldChild->firstChild() || oldChild->hasLayer()) { - if (!layer) layer = enclosingLayer(); - oldChild->removeLayers(layer); - } - - // renumber ordered lists - if (oldChild->isListItem()) - updateListMarkerNumbers(oldChild->nextSibling()); - - if (oldChild->isPositioned() && childrenInline()) - dirtyLinesFromChangedChild(oldChild); - } - - // If oldChild is the start or end of the selection, then clear the selection to - // avoid problems of invalid pointers. - // FIXME: The SelectionController should be responsible for this when it - // is notified of DOM mutations. - if (!documentBeingDestroyed() && oldChild->isSelectionBorder()) - view()->clearSelection(); - - // remove the child - if (oldChild->previousSibling()) - oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); - if (oldChild->nextSibling()) - oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); - - if (m_firstChild == oldChild) - m_firstChild = oldChild->nextSibling(); - if (m_lastChild == oldChild) - m_lastChild = oldChild->previousSibling(); - - oldChild->setPreviousSibling(0); - oldChild->setNextSibling(0); - oldChild->setParent(0); - - if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->childrenChanged(this); - - return oldChild; -} - -void RenderContainer::removeChild(RenderObject* oldChild) -{ - // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode - // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on - // layout anyway). - oldChild->removeFromObjectLists(); - - removeChildNode(oldChild); -} - -RenderObject* RenderContainer::beforeAfterContainer(RenderStyle::PseudoId type) -{ - if (type == RenderStyle::BEFORE) { - RenderObject* first = this; - do { - // Skip list markers. - first = first->firstChild(); - while (first && first->isListMarker()) - first = first->nextSibling(); - } while (first && first->isAnonymous() && first->style()->styleType() == RenderStyle::NOPSEUDO); - if (first && first->style()->styleType() != type) - return 0; - return first; - } - if (type == RenderStyle::AFTER) { - RenderObject* last = this; - do { - last = last->lastChild(); - } while (last && last->isAnonymous() && last->style()->styleType() == RenderStyle::NOPSEUDO && !last->isListMarker()); - if (last && last->style()->styleType() != type) - return 0; - return last; - } - - ASSERT_NOT_REACHED(); - return 0; -} - -void RenderContainer::updateBeforeAfterContent(RenderStyle::PseudoId type) -{ - // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it. - if (parent() && parent()->createsAnonymousWrapper()) - return; - updateBeforeAfterContentForContainer(type, this); -} - -static RenderObject* findBeforeAfterParent(RenderObject* object) -{ - // Only table parts need to search for the :before or :after parent - if (!(object->isTable() || object->isTableSection() || object->isTableRow())) - return object; - - RenderObject* beforeAfterParent = object; - while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterParent->isImage())) - beforeAfterParent = beforeAfterParent->firstChild(); - return beforeAfterParent; -} - -void RenderContainer::updateBeforeAfterContentForContainer(RenderStyle::PseudoId type, RenderContainer* styledObject) -{ - // Double check that the document did in fact use generated content rules. Otherwise we should not have been called. - ASSERT(document()->usesBeforeAfterRules()); - - // In CSS2, before/after pseudo-content cannot nest. Check this first. - if (style()->styleType() == RenderStyle::BEFORE || style()->styleType() == RenderStyle::AFTER) - return; - - RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type); - RenderObject* child = beforeAfterContainer(type); - - // Whether or not we currently have generated content attached. - bool oldContentPresent = child; - - // Whether or not we now want generated content. - bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE; - - // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate - // :after content and not :before content. - if (newContentWanted && type == RenderStyle::BEFORE && isInlineContinuation()) - newContentWanted = false; - - // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object, - // then we don't generate the :after content. - if (newContentWanted && type == RenderStyle::AFTER && isRenderInline() && static_cast<RenderInline*>(this)->continuation()) - newContentWanted = false; - - // If we don't want generated content any longer, or if we have generated content, but it's no longer - // identical to the new content data we want to build render objects for, then we nuke all - // of the old generated content. - if (!newContentWanted || (oldContentPresent && Node::diff(child->style(), pseudoElementStyle) == Node::Detach)) { - // Nuke the child. - if (child && child->style()->styleType() == type) { - oldContentPresent = false; - child->destroy(); - child = (type == RenderStyle::BEFORE) ? m_firstChild : m_lastChild; - } - } - - // If we have no pseudo-element style or if the pseudo-element style's display type is NONE, then we - // have no generated content and can now return. - if (!newContentWanted) - return; - - if (isRenderInline() && !pseudoElementStyle->isDisplayInlineType() && pseudoElementStyle->floating() == FNONE && - !(pseudoElementStyle->position() == AbsolutePosition || pseudoElementStyle->position() == FixedPosition)) - // According to the CSS2 spec (the end of section 12.1), the only allowed - // display values for the pseudo style are NONE and INLINE for inline flows. - // FIXME: CSS2.1 lifted this restriction, but block display types will crash. - // For now we at least relax the restriction to allow all inline types like inline-block - // and inline-table. - pseudoElementStyle->setDisplay(INLINE); - - if (oldContentPresent) { - if (child && child->style()->styleType() == type) { - // We have generated content present still. We want to walk this content and update our - // style information with the new pseudo-element style. - child->setStyle(pseudoElementStyle); - - RenderObject* beforeAfterParent = findBeforeAfterParent(child); - if (!beforeAfterParent) - return; - - // Note that if we ever support additional types of generated content (which should be way off - // in the future), this code will need to be patched. - for (RenderObject* genChild = beforeAfterParent->firstChild(); genChild; genChild = genChild->nextSibling()) { - if (genChild->isText()) - // Generated text content is a child whose style also needs to be set to the pseudo-element style. - genChild->setStyle(pseudoElementStyle); - else if (genChild->isImage()) { - // Images get an empty style that inherits from the pseudo. - RefPtr<RenderStyle> style = RenderStyle::create(); - style->inheritFrom(pseudoElementStyle); - genChild->setStyle(style.release()); - } else - // Must be a first-letter container. updateFirstLetter() will take care of it. - ASSERT(genChild->style()->styleType() == RenderStyle::FIRST_LETTER); - } - } - return; // We've updated the generated content. That's all we needed to do. - } - - RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? firstChild() : 0; - - // Generated content consists of a single container that houses multiple children (specified - // by the content property). This generated content container gets the pseudo-element style set on it. - RenderObject* generatedContentContainer = 0; - - // Walk our list of generated content and create render objects for each. - for (const ContentData* content = pseudoElementStyle->contentData(); content; content = content->m_next) { - RenderObject* renderer = 0; - switch (content->m_type) { - case CONTENT_NONE: - break; - case CONTENT_TEXT: - renderer = new (renderArena()) RenderTextFragment(document() /* anonymous object */, content->m_content.m_text); - renderer->setStyle(pseudoElementStyle); - break; - case CONTENT_OBJECT: { - RenderImageGeneratedContent* image = new (renderArena()) RenderImageGeneratedContent(document()); // anonymous object - RefPtr<RenderStyle> style = RenderStyle::create(); - style->inheritFrom(pseudoElementStyle); - image->setStyle(style.release()); - if (StyleImage* styleImage = content->m_content.m_image) - image->setStyleImage(styleImage); - renderer = image; - break; - } - case CONTENT_COUNTER: - renderer = new (renderArena()) RenderCounter(document(), *content->m_content.m_counter); - renderer->setStyle(pseudoElementStyle); - break; - } - - if (renderer) { - if (!generatedContentContainer) { - // Make a generated box that might be any display type now that we are able to drill down into children - // to find the original content properly. - generatedContentContainer = RenderObject::createObject(document(), pseudoElementStyle); - generatedContentContainer->setStyle(pseudoElementStyle); - addChild(generatedContentContainer, insertBefore); - } - generatedContentContainer->addChild(renderer); - } - } -} - -bool RenderContainer::isAfterContent(RenderObject* child) const -{ - if (!child) - return false; - if (child->style()->styleType() != RenderStyle::AFTER) - return false; - // Text nodes don't have their own styles, so ignore the style on a text node. - if (child->isText() && !child->isBR()) - return false; - return true; -} - -static void invalidateCountersInContainer(RenderObject* container) -{ - if (!container) - return; - container = findBeforeAfterParent(container); - if (!container) - return; - for (RenderObject* content = container->firstChild(); content; content = content->nextSibling()) { - if (content->isCounter()) - static_cast<RenderCounter*>(content)->invalidate(); - } -} - -void RenderContainer::invalidateCounters() -{ - if (documentBeingDestroyed()) - return; - - invalidateCountersInContainer(beforeAfterContainer(RenderStyle::BEFORE)); - invalidateCountersInContainer(beforeAfterContainer(RenderStyle::AFTER)); -} - -void RenderContainer::appendChildNode(RenderObject* newChild, bool fullAppend) -{ - ASSERT(newChild->parent() == 0); - ASSERT(!isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableRow() && !newChild->isTableCell())); - - newChild->setParent(this); - RenderObject* lChild = m_lastChild; - - if (lChild) { - newChild->setPreviousSibling(lChild); - lChild->setNextSibling(newChild); - } else - m_firstChild = newChild; - - m_lastChild = newChild; - - if (fullAppend) { - // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children - // and don't have a layer attached to ourselves. - RenderLayer* layer = 0; - if (newChild->firstChild() || newChild->hasLayer()) { - layer = enclosingLayer(); - newChild->addLayers(layer, newChild); - } - - // if the new child is visible but this object was not, tell the layer it has some visible content - // that needs to be drawn and layer visibility optimization can't be used - if (style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->hasLayer()) { - if (!layer) - layer = enclosingLayer(); - if (layer) - layer->setHasVisibleContent(true); - } - - if (!newChild->isFloatingOrPositioned() && childrenInline()) - dirtyLinesFromChangedChild(newChild); - } - - newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy. - if (!normalChildNeedsLayout()) - setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. - - if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->childrenChanged(this); -} - -void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild, bool fullInsert) -{ - if (!beforeChild) { - appendChildNode(child); - return; - } - - ASSERT(!child->parent()); - while (beforeChild->parent() != this && beforeChild->parent()->isAnonymousBlock()) - beforeChild = beforeChild->parent(); - ASSERT(beforeChild->parent() == this); - - ASSERT(!isBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell())); - - if (beforeChild == m_firstChild) - m_firstChild = child; - - RenderObject* prev = beforeChild->previousSibling(); - child->setNextSibling(beforeChild); - beforeChild->setPreviousSibling(child); - if(prev) prev->setNextSibling(child); - child->setPreviousSibling(prev); - - child->setParent(this); - - if (fullInsert) { - // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children - // and don't have a layer attached to ourselves. - RenderLayer* layer = 0; - if (child->firstChild() || child->hasLayer()) { - layer = enclosingLayer(); - child->addLayers(layer, child); - } - - // if the new child is visible but this object was not, tell the layer it has some visible content - // that needs to be drawn and layer visibility optimization can't be used - if (style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) { - if (!layer) - layer = enclosingLayer(); - if (layer) - layer->setHasVisibleContent(true); - } - - - if (!child->isFloating() && childrenInline()) - dirtyLinesFromChangedChild(child); - } - - child->setNeedsLayoutAndPrefWidthsRecalc(); - if (!normalChildNeedsLayout()) - setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. - - if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->childrenChanged(this); -} - -void RenderContainer::layout() -{ - ASSERT(needsLayout()); - - LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y())); - - RenderObject* child = m_firstChild; - while (child) { - child->layoutIfNeeded(); - ASSERT(child->isRenderInline() || !child->needsLayout()); - child = child->nextSibling(); - } - - statePusher.pop(); - setNeedsLayout(false); -} - -void RenderContainer::removeLeftoverAnonymousBlock(RenderBlock* child) -{ - ASSERT(child->isAnonymousBlock()); - ASSERT(!child->childrenInline()); - - if (child->continuation()) - return; - - RenderObject* firstAnChild = child->firstChild(); - RenderObject* lastAnChild = child->lastChild(); - if (firstAnChild) { - RenderObject* o = firstAnChild; - while(o) { - o->setParent(this); - o = o->nextSibling(); - } - firstAnChild->setPreviousSibling(child->previousSibling()); - lastAnChild->setNextSibling(child->nextSibling()); - if (child->previousSibling()) - child->previousSibling()->setNextSibling(firstAnChild); - if (child->nextSibling()) - child->nextSibling()->setPreviousSibling(lastAnChild); - } else { - if (child->previousSibling()) - child->previousSibling()->setNextSibling(child->nextSibling()); - if (child->nextSibling()) - child->nextSibling()->setPreviousSibling(child->previousSibling()); - } - if (child == m_firstChild) - m_firstChild = firstAnChild; - if (child == m_lastChild) - m_lastChild = lastAnChild; - child->setParent(0); - child->setPreviousSibling(0); - child->setNextSibling(0); - if (!child->isText()) { - RenderContainer *c = static_cast<RenderContainer*>(child); - c->m_firstChild = 0; - c->m_next = 0; - } - child->destroy(); -} - -VisiblePosition RenderContainer::positionForCoordinates(int xPos, int yPos) -{ - // no children...return this render object's element, if there is one, and offset 0 - if (!m_firstChild) - return VisiblePosition(element(), 0, DOWNSTREAM); - - if (isTable() && element()) { - int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft(); - int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom(); - - if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) { - if (xPos <= right / 2) - return VisiblePosition(Position(element(), 0)); - else - return VisiblePosition(Position(element(), maxDeepOffset(element()))); - } - } - - // Pass off to the closest child. - int minDist = INT_MAX; - RenderBox* closestRenderer = 0; - int newX = xPos; - int newY = yPos; - if (isTableRow()) { - newX += x(); - newY += y(); - } - for (RenderObject* renderObject = m_firstChild; renderObject; renderObject = renderObject->nextSibling()) { - if (!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() - || renderObject->style()->visibility() != VISIBLE) - continue; - - if (!renderObject->isBox()) - continue; - - RenderBox* renderer = toRenderBox(renderObject); - - int top = borderTop() + paddingTop() + (isTableRow() ? 0 : renderer->y()); - int bottom = top + renderer->contentHeight(); - int left = borderLeft() + paddingLeft() + (isTableRow() ? 0 : renderer->x()); - int right = left + renderer->contentWidth(); - - if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) { - if (renderer->isTableRow()) - return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y()); - return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y()); - } - - // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces - // and use a different compare depending on which piece (x, y) is in. - IntPoint cmp; - if (xPos > right) { - if (yPos < top) - cmp = IntPoint(right, top); - else if (yPos > bottom) - cmp = IntPoint(right, bottom); - else - cmp = IntPoint(right, yPos); - } else if (xPos < left) { - if (yPos < top) - cmp = IntPoint(left, top); - else if (yPos > bottom) - cmp = IntPoint(left, bottom); - else - cmp = IntPoint(left, yPos); - } else { - if (yPos < top) - cmp = IntPoint(xPos, top); - else - cmp = IntPoint(xPos, bottom); - } - - int x1minusx2 = cmp.x() - xPos; - int y1minusy2 = cmp.y() - yPos; - - int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2; - if (dist < minDist) { - closestRenderer = renderer; - minDist = dist; - } - } - - if (closestRenderer) - return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y()); - - return VisiblePosition(element(), 0, DOWNSTREAM); -} - -void RenderContainer::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end, bool) -{ - if (!m_firstChild && (isInline() || isAnonymousBlock())) { - FloatPoint absPos = localToAbsolute(FloatPoint()); - absoluteRects(rects, absPos.x(), absPos.y()); - return; - } - - if (!m_firstChild) - return; - - unsigned offset = start; - for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { - if (child->isText() || child->isInline() || child->isAnonymousBlock()) { - FloatPoint absPos = child->localToAbsolute(FloatPoint()); - child->absoluteRects(rects, absPos.x(), absPos.y()); - } - } -} - -void RenderContainer::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool /*useSelectionHeight*/) -{ - if (!m_firstChild && (isInline() || isAnonymousBlock())) { - absoluteQuads(quads); - return; - } - - if (!m_firstChild) - return; - - unsigned offset = start; - for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { - if (child->isText() || child->isInline() || child->isAnonymousBlock()) - child->absoluteQuads(quads); - } -} - -#ifdef ANDROID_LAYOUT -bool RenderContainer::hasChildTable() const -{ - if (!firstChild()) - return false; - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (child->isTable()) { - return true; - } else if (child->hasChildTable() == true) { - return true; - } - } - return false; -} -#endif - -#undef DEBUG_LAYOUT - -} // namespace WebCore diff --git a/WebCore/rendering/RenderContainer.h b/WebCore/rendering/RenderContainer.h deleted file mode 100644 index 8ae5296..0000000 --- a/WebCore/rendering/RenderContainer.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2001 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007 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 RenderContainer_h -#define RenderContainer_h - -#include "RenderBox.h" - -namespace WebCore { - -// Base class for rendering objects that can have children. -class RenderContainer : public RenderBox { -public: - RenderContainer(Node*); - virtual ~RenderContainer(); - - virtual RenderObject* firstChild() const { return m_firstChild; } - virtual RenderObject* lastChild() const { return m_lastChild; } - - // Use this with caution! No type checking is done! - RenderBox* firstChildBox() const { ASSERT(!firstChild() || firstChild()->isBox()); return toRenderBox(m_firstChild); } - RenderBox* lastChildBox() const { ASSERT(!lastChild() || lastChild()->isBox()); return toRenderBox(m_lastChild); } - - virtual bool canHaveChildren() const; - virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); - virtual void removeChild(RenderObject*); - - virtual void destroy(); - void destroyLeftoverChildren(); - - virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); - virtual void appendChildNode(RenderObject*, bool fullAppend = true); - virtual void insertChildNode(RenderObject* child, RenderObject* before, bool fullInsert = true); - - // Designed for speed. Don't waste time doing a bunch of work like layer updating and repainting when we know that our - // change in parentage is not going to affect anything. - virtual void moveChildNode(RenderObject* child) { appendChildNode(child->parent()->removeChildNode(child, false), false); } - - virtual void layout(); - virtual void calcPrefWidths() { setPrefWidthsDirty(false); } - - virtual void removeLeftoverAnonymousBlock(RenderBlock* child); - - RenderObject* beforeAfterContainer(RenderStyle::PseudoId); - virtual void updateBeforeAfterContent(RenderStyle::PseudoId); - void updateBeforeAfterContentForContainer(RenderStyle::PseudoId, RenderContainer*); - bool isAfterContent(RenderObject* child) const; - virtual void invalidateCounters(); - - virtual VisiblePosition positionForCoordinates(int x, int y); -#ifdef ANDROID_LAYOUT - virtual bool hasChildTable() const; -#endif - - virtual void addLineBoxRects(Vector<IntRect>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); - virtual void collectAbsoluteLineBoxQuads(Vector<FloatQuad>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); - -protected: - RenderObject* m_firstChild; - RenderObject* m_lastChild; -}; - -} // namespace WebCore - -#endif // RenderContainer_h diff --git a/WebCore/rendering/RenderCounter.cpp b/WebCore/rendering/RenderCounter.cpp index 598f40d..fd6d80d 100644 --- a/WebCore/rendering/RenderCounter.cpp +++ b/WebCore/rendering/RenderCounter.cpp @@ -116,7 +116,7 @@ static bool planCounter(RenderObject* object, const AtomicString& counterName, b isReset = false; return true; } - if (Node* e = object->element()) { + if (Node* e = object->node()) { if (e->hasTagName(olTag)) { value = static_cast<HTMLOListElement*>(e)->start(); isReset = true; @@ -251,13 +251,6 @@ PassRefPtr<StringImpl> RenderCounter::originalText() const return text.impl(); } -void RenderCounter::dirtyLineBoxes(bool fullLayout, bool dummy) -{ - if (prefWidthsDirty()) - calcPrefWidths(0); - RenderText::dirtyLineBoxes(fullLayout, dummy); -} - void RenderCounter::calcPrefWidths(int lead) { setTextInternal(originalText()); @@ -278,7 +271,11 @@ static void destroyCounterNodeChildren(AtomicStringImpl* identifier, CounterNode child->parent()->removeChild(child); ASSERT(counterMaps().get(child->renderer())->get(identifier) == child); counterMaps().get(child->renderer())->remove(identifier); - child->renderer()->invalidateCounters(); + if (!child->renderer()->documentBeingDestroyed()) { + RenderObjectChildList* children = child->renderer()->virtualChildren(); + if (children) + children->invalidateCounters(child->renderer()); + } delete child; } } diff --git a/WebCore/rendering/RenderCounter.h b/WebCore/rendering/RenderCounter.h index 10be066..55aab73 100644 --- a/WebCore/rendering/RenderCounter.h +++ b/WebCore/rendering/RenderCounter.h @@ -37,7 +37,6 @@ public: virtual bool isCounter() const; virtual PassRefPtr<StringImpl> originalText() const; - virtual void dirtyLineBoxes(bool, bool); virtual void calcPrefWidths(int leadWidth); void invalidate(); diff --git a/WebCore/rendering/RenderFieldset.cpp b/WebCore/rendering/RenderFieldset.cpp index 5baca3e..310dbe4 100644 --- a/WebCore/rendering/RenderFieldset.cpp +++ b/WebCore/rendering/RenderFieldset.cpp @@ -107,10 +107,10 @@ RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren) RenderBox* RenderFieldset::findLegend() const { for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) { - if (!legend->isFloatingOrPositioned() && legend->element() && - legend->element()->hasTagName(legendTag) + if (!legend->isFloatingOrPositioned() && legend->node() && + legend->node()->hasTagName(legendTag) #if ENABLE(WML) - || legend->element()->hasTagName(WMLNames::insertedLegendTag) + || legend->node()->hasTagName(WMLNames::insertedLegendTag) #endif ) return toRenderBox(legend); @@ -204,17 +204,17 @@ void RenderFieldset::paintBorderMinusLegend(GraphicsContext* graphicsContext, in if (render_t) { if (lx >= borderLeftWidth) - drawBorder(graphicsContext, tx, ty, tx + min(lx, w), ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, + drawLineForBoxSide(graphicsContext, tx, ty, tx + min(lx, w), ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0), (lx >= w && render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0)); if (lx + lw <= w - borderRightWidth) - drawBorder(graphicsContext, tx + max(0, lx + lw), ty, tx + w, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, + drawLineForBoxSide(graphicsContext, tx + max(0, lx + lw), ty, tx + w, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, (lx + lw <= 0 && render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0), (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0)); } if (render_b) - drawBorder(graphicsContext, tx, ty + h - style->borderBottomWidth(), tx + w, ty + h, BSBottom, bc, style->color(), bs, + drawLineForBoxSide(graphicsContext, tx, ty + h - style->borderBottomWidth(), tx + w, ty + h, BSBottom, bc, style->color(), bs, (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? style->borderLeftWidth() : 0), (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? style->borderRightWidth() : 0)); @@ -238,7 +238,7 @@ void RenderFieldset::paintBorderMinusLegend(GraphicsContext* graphicsContext, in startY = lb; } - drawBorder(graphicsContext, tx, startY, tx + borderLeftWidth, ty + h, BSLeft, lc, style->color(), ls, + drawLineForBoxSide(graphicsContext, tx, startY, tx + borderLeftWidth, ty + h, BSLeft, lc, style->color(), ls, ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); } @@ -262,12 +262,12 @@ void RenderFieldset::paintBorderMinusLegend(GraphicsContext* graphicsContext, in startY = lb; } - drawBorder(graphicsContext, tx + w - borderRightWidth, startY, tx + w, ty + h, BSRight, rc, style->color(), rs, + drawLineForBoxSide(graphicsContext, tx + w - borderRightWidth, startY, tx + w, ty + h, BSRight, rc, style->color(), rs, ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); } } -void RenderFieldset::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderFieldset::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); diff --git a/WebCore/rendering/RenderFieldset.h b/WebCore/rendering/RenderFieldset.h index 38f3236..ed57d3a 100644 --- a/WebCore/rendering/RenderFieldset.h +++ b/WebCore/rendering/RenderFieldset.h @@ -46,7 +46,7 @@ public: RenderBox* findLegend() const; protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); private: virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); diff --git a/WebCore/rendering/RenderFileUploadControl.cpp b/WebCore/rendering/RenderFileUploadControl.cpp index cbf1409..33ffd98 100644 --- a/WebCore/rendering/RenderFileUploadControl.cpp +++ b/WebCore/rendering/RenderFileUploadControl.cpp @@ -22,6 +22,7 @@ #include "RenderFileUploadControl.h" #include "FileList.h" +#include "Frame.h" #include "FrameView.h" #include "GraphicsContext.h" #include "HTMLInputElement.h" @@ -73,7 +74,7 @@ RenderFileUploadControl::~RenderFileUploadControl() m_fileChooser->disconnectClient(); } -void RenderFileUploadControl::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderFileUploadControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); if (m_button) @@ -157,7 +158,7 @@ int RenderFileUploadControl::maxFilenameWidth() const PassRefPtr<RenderStyle> RenderFileUploadControl::createButtonStyle(const RenderStyle* parentStyle) const { - RefPtr<RenderStyle> style = getCachedPseudoStyle(RenderStyle::FILE_UPLOAD_BUTTON); + RefPtr<RenderStyle> style = getCachedPseudoStyle(FILE_UPLOAD_BUTTON); if (!style) { style = RenderStyle::create(); if (parentStyle) @@ -202,7 +203,7 @@ void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty) else textX = contentLeft + contentWidth() - buttonAndIconWidth - style()->font().width(textRun); // We want to match the button's baseline - RenderButton* buttonRenderer = static_cast<RenderButton*>(m_button->renderer()); + RenderButton* buttonRenderer = toRenderButton(m_button->renderer()); int textY = buttonRenderer->absoluteBoundingBoxRect().y() + buttonRenderer->marginTop() + buttonRenderer->borderTop() + buttonRenderer->paddingTop() + buttonRenderer->baselinePosition(true, false); diff --git a/WebCore/rendering/RenderFileUploadControl.h b/WebCore/rendering/RenderFileUploadControl.h index 60e7a7b..85bc09f 100644 --- a/WebCore/rendering/RenderFileUploadControl.h +++ b/WebCore/rendering/RenderFileUploadControl.h @@ -55,7 +55,7 @@ public: bool allowsMultipleFiles(); protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); private: int maxFilenameWidth() const; diff --git a/WebCore/rendering/RenderFlexibleBox.cpp b/WebCore/rendering/RenderFlexibleBox.cpp index e6dd91a..ff9dad4 100644 --- a/WebCore/rendering/RenderFlexibleBox.cpp +++ b/WebCore/rendering/RenderFlexibleBox.cpp @@ -205,14 +205,7 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren) if (!relayoutChildren && layoutOnlyPositionedObjects()) return; - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout(); - if (checkForRepaint) { - oldBounds = absoluteClippedOverflowRect(); - oldOutlineBox = absoluteOutlineBounds(); - } - + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasTransform() || hasReflection()); int previousWidth = width(); @@ -248,9 +241,9 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren) // For overflow:scroll blocks, ensure we have both scrollbars in place always. if (scrollsOverflow()) { if (style()->overflowX() == OSCROLL) - m_layer->setHasHorizontalScrollbar(true); + layer()->setHasHorizontalScrollbar(true); if (style()->overflowY() == OSCROLL) - m_layer->setHasVerticalScrollbar(true); + layer()->setHasVerticalScrollbar(true); } if (isHorizontal()) @@ -316,11 +309,10 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren) // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if // we overflow or not. if (hasOverflowClip()) - m_layer->updateScrollInfoAfterLayout(); + layer()->updateScrollInfoAfterLayout(); // Repaint with our new bounds if they are different from our old bounds. - if (checkForRepaint) - repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + repainter.repaintAfterLayout(); setNeedsLayout(false); } @@ -393,9 +385,10 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) // Update our height and overflow height. if (style()->boxAlign() == BBASELINE) { - int ascent = child->marginTop() + child->getBaselineOfFirstLineBox(); + int ascent = child->firstLineBoxBaseline(); if (ascent == -1) - ascent = child->marginTop() + child->height() + child->marginBottom(); + ascent = child->height() + child->marginBottom(); + ascent += child->marginTop(); int descent = (child->marginTop() + child->height() + child->marginBottom()) - ascent; // Update our maximum ascent. @@ -435,13 +428,13 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) while (child) { if (child->isPositioned()) { child->containingBlock()->insertPositionedObject(child); - if (child->hasStaticX()) { + if (child->style()->hasStaticX()) { if (style()->direction() == LTR) - child->setStaticX(xPos); - else child->setStaticX(width() - xPos); + child->layer()->setStaticX(xPos); + else child->layer()->setStaticX(width() - xPos); } - if (child->hasStaticY()) - child->setStaticY(yPos); + if (child->style()->hasStaticY()) + child->layer()->setStaticY(yPos); child = iterator.next(); continue; } @@ -450,7 +443,7 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) // fill the height of a containing box by default. // Now do a layout. int oldChildHeight = child->height(); - toRenderBox(child)->calcHeight(); + child->calcHeight(); if (oldChildHeight != child->height()) child->setChildNeedsLayout(true, false); child->layoutIfNeeded(); @@ -463,9 +456,10 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) childY += child->marginTop() + max(0, (contentHeight() - (child->height() + child->marginTop() + child->marginBottom()))/2); break; case BBASELINE: { - int ascent = child->marginTop() + child->getBaselineOfFirstLineBox(); + int ascent = child->firstLineBoxBaseline(); if (ascent == -1) - ascent = child->marginTop() + child->height() + child->marginBottom(); + ascent = child->height() + child->marginBottom(); + ascent += child->marginTop(); childY += child->marginTop() + (maxAscent - ascent); break; } @@ -480,7 +474,7 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) placeChild(child, xPos, childY); if (child->isRenderBlock()) - static_cast<RenderBlock*>(child)->addVisualOverflow(static_cast<RenderBlock*>(child)->floatRect()); + toRenderBlock(child)->addVisualOverflow(toRenderBlock(child)->floatRect()); m_overflowHeight = max(m_overflowHeight, childY + child->overflowHeight(false)); m_overflowTop = min(m_overflowTop, child->y() + child->overflowTop(false)); @@ -719,13 +713,13 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) // Dirty all the positioned objects. if (child->isRenderBlock()) { - static_cast<RenderBlock*>(child)->markPositionedObjectsForLayout(); - static_cast<RenderBlock*>(child)->clearTruncation(); + toRenderBlock(child)->markPositionedObjectsForLayout(); + toRenderBlock(child)->clearTruncation(); } } child->layoutIfNeeded(); if (child->style()->height().isAuto() && child->isBlockFlow()) - maxLineCount = max(maxLineCount, static_cast<RenderBlock*>(child)->lineCount()); + maxLineCount = max(maxLineCount, toRenderBlock(child)->lineCount()); } child = iterator.next(); } @@ -738,7 +732,7 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) if (child->isPositioned() || !child->style()->height().isAuto() || !child->isBlockFlow()) continue; - RenderBlock* blockChild = static_cast<RenderBlock*>(child); + RenderBlock* blockChild = toRenderBlock(child); int lineCount = blockChild->lineCount(); if (lineCount <= numVisibleLines) continue; @@ -767,9 +761,9 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) InlineBox* anchorBox = lastLine->lastChild(); if (!anchorBox) continue; - if (!anchorBox->object()->element()) + if (!anchorBox->renderer()->node()) continue; - if (!anchorBox->object()->element()->isLink()) + if (!anchorBox->renderer()->node()->isLink()) continue; RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines-1); @@ -786,8 +780,8 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) int totalWidth = ellipsisAndSpaceWidth + anchorBox->width(); // See if this width can be accommodated on the last visible line - RenderBlock* destBlock = static_cast<RenderBlock*>(lastVisibleLine->object()); - RenderBlock* srcBlock = static_cast<RenderBlock*>(lastLine->object()); + RenderBlock* destBlock = toRenderBlock(lastVisibleLine->renderer()); + RenderBlock* srcBlock = toRenderBlock(lastLine->renderer()); // FIXME: Directions of src/destBlock could be different from our direction and from one another. if (srcBlock->style()->direction() != LTR) @@ -795,9 +789,9 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) if (destBlock->style()->direction() != LTR) continue; - int blockEdge = destBlock->rightOffset(lastVisibleLine->yPos()); + int blockEdge = destBlock->rightOffset(lastVisibleLine->y(), false); if (!lastVisibleLine->canAccommodateEllipsis(true, blockEdge, - lastVisibleLine->xPos() + lastVisibleLine->width(), + lastVisibleLine->x() + lastVisibleLine->width(), totalWidth)) continue; @@ -826,14 +820,14 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) if (child->isPositioned()) { child->containingBlock()->insertPositionedObject(child); - if (child->hasStaticX()) { + if (child->style()->hasStaticX()) { if (style()->direction() == LTR) - child->setStaticX(borderLeft()+paddingLeft()); + child->layer()->setStaticX(borderLeft()+paddingLeft()); else - child->setStaticX(borderRight()+paddingRight()); + child->layer()->setStaticX(borderRight()+paddingRight()); } - if (child->hasStaticY()) - child->setStaticY(height()); + if (child->style()->hasStaticY()) + child->layer()->setStaticY(height()); child = iterator.next(); continue; } @@ -873,7 +867,7 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) setHeight(height() + child->height() + child->marginBottom()); if (child->isRenderBlock()) - static_cast<RenderBlock*>(child)->addVisualOverflow(static_cast<RenderBlock*>(child)->floatRect()); + toRenderBlock(child)->addVisualOverflow(toRenderBlock(child)->floatRect()); // See if this child has made our overflow need to grow. m_overflowWidth = max(child->x() + child->overflowWidth(false), m_overflowWidth); diff --git a/WebCore/rendering/RenderFlow.cpp b/WebCore/rendering/RenderFlow.cpp deleted file mode 100644 index 94ec124..0000000 --- a/WebCore/rendering/RenderFlow.cpp +++ /dev/null @@ -1,744 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007 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 "RenderFlow.h" - -#include "Document.h" -#include "GraphicsContext.h" -#include "HTMLNames.h" -#include "InlineTextBox.h" -#include "RenderArena.h" -#include "RenderInline.h" -#include "RenderLayer.h" -#include "RenderView.h" - -using namespace std; - -namespace WebCore { - -using namespace HTMLNames; - -#ifndef NDEBUG - -RenderFlow::~RenderFlow() -{ - ASSERT(!m_firstLineBox); - ASSERT(!m_lastLineBox); -} - -#endif - -RenderFlow* RenderFlow::createAnonymousFlow(Document* doc, PassRefPtr<RenderStyle> style) -{ - RenderFlow* result; - if (style->display() == INLINE) - result = new (doc->renderArena()) RenderInline(doc); - else - result = new (doc->renderArena()) RenderBlock(doc); - result->setStyle(style); - return result; -} - -RenderFlow* RenderFlow::continuationBefore(RenderObject* beforeChild) -{ - if (beforeChild && beforeChild->parent() == this) - return this; - - RenderFlow* curr = continuation(); - RenderFlow* nextToLast = this; - RenderFlow* last = this; - while (curr) { - if (beforeChild && beforeChild->parent() == curr) { - if (curr->firstChild() == beforeChild) - return last; - return curr; - } - - nextToLast = last; - last = curr; - curr = curr->continuation(); - } - - if (!beforeChild && !last->firstChild()) - return nextToLast; - return last; -} - -void RenderFlow::addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild) -{ - if (beforeChild && (beforeChild->parent()->isTableRow() || beforeChild->parent()->isTableSection() || beforeChild->parent()->isTable())) { - RenderObject* anonymousTablePart = beforeChild->parent(); - ASSERT(anonymousTablePart->isAnonymous()); - while (!anonymousTablePart->isTable()) { - anonymousTablePart = anonymousTablePart->parent(); - ASSERT(anonymousTablePart->isAnonymous()); - } - return anonymousTablePart->addChild(newChild, beforeChild); - } - - RenderFlow* flow = continuationBefore(beforeChild); - ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || - beforeChild->parent()->isRenderInline()); - RenderFlow* beforeChildParent = beforeChild ? static_cast<RenderFlow*>(beforeChild->parent()) : - (flow->continuation() ? flow->continuation() : flow); - - if (newChild->isFloatingOrPositioned()) - return beforeChildParent->addChildToFlow(newChild, beforeChild); - - // A continuation always consists of two potential candidates: an inline or an anonymous - // block box holding block children. - bool childInline = newChild->isInline(); - bool bcpInline = beforeChildParent->isInline(); - bool flowInline = flow->isInline(); - - if (flow == beforeChildParent) - return flow->addChildToFlow(newChild, beforeChild); - else { - // The goal here is to match up if we can, so that we can coalesce and create the - // minimal # of continuations needed for the inline. - if (childInline == bcpInline) - return beforeChildParent->addChildToFlow(newChild, beforeChild); - else if (flowInline == childInline) - return flow->addChildToFlow(newChild, 0); // Just treat like an append. - else - return beforeChildParent->addChildToFlow(newChild, beforeChild); - } -} - -void RenderFlow::addChild(RenderObject* newChild, RenderObject* beforeChild) -{ - if (continuation()) - return addChildWithContinuation(newChild, beforeChild); - return addChildToFlow(newChild, beforeChild); -} - -void RenderFlow::extractLineBox(InlineFlowBox* box) -{ - checkConsistency(); - - m_lastLineBox = box->prevFlowBox(); - if (box == m_firstLineBox) - m_firstLineBox = 0; - if (box->prevLineBox()) - box->prevLineBox()->setNextLineBox(0); - box->setPreviousLineBox(0); - for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox()) - curr->setExtracted(); - - checkConsistency(); -} - -void RenderFlow::attachLineBox(InlineFlowBox* box) -{ - checkConsistency(); - - if (m_lastLineBox) { - m_lastLineBox->setNextLineBox(box); - box->setPreviousLineBox(m_lastLineBox); - } else - m_firstLineBox = box; - InlineFlowBox* last = box; - for (InlineFlowBox* curr = box; curr; curr = curr->nextFlowBox()) { - curr->setExtracted(false); - last = curr; - } - m_lastLineBox = last; - - checkConsistency(); -} - -void RenderFlow::removeLineBox(InlineFlowBox* box) -{ - checkConsistency(); - - if (box == m_firstLineBox) - m_firstLineBox = box->nextFlowBox(); - if (box == m_lastLineBox) - m_lastLineBox = box->prevFlowBox(); - if (box->nextLineBox()) - box->nextLineBox()->setPreviousLineBox(box->prevLineBox()); - if (box->prevLineBox()) - box->prevLineBox()->setNextLineBox(box->nextLineBox()); - - checkConsistency(); -} - -void RenderFlow::deleteLineBoxes() -{ - if (m_firstLineBox) { - RenderArena* arena = renderArena(); - InlineRunBox* next; - for (InlineRunBox* curr = m_firstLineBox; curr; curr = next) { - next = curr->nextLineBox(); - curr->destroy(arena); - } - m_firstLineBox = 0; - m_lastLineBox = 0; - } -} - -void RenderFlow::destroy() -{ - // Detach our continuation first. - if (m_continuation) - m_continuation->destroy(); - m_continuation = 0; - - // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will - // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. - RenderContainer::destroyLeftoverChildren(); - - if (!documentBeingDestroyed()) { - if (m_firstLineBox) { - // We can't wait for RenderContainer::destroy to clear the selection, - // because by then we will have nuked the line boxes. - // FIXME: The SelectionController should be responsible for this when it - // is notified of DOM mutations. - if (isSelectionBorder()) - view()->clearSelection(); - - // If line boxes are contained inside a root, that means we're an inline. - // In that case, we need to remove all the line boxes so that the parent - // lines aren't pointing to deleted children. If the first line box does - // not have a parent that means they are either already disconnected or - // root lines that can just be destroyed without disconnecting. - if (m_firstLineBox->parent()) { - for (InlineRunBox* box = m_firstLineBox; box; box = box->nextLineBox()) - box->remove(); - } - - // If we are an anonymous block, then our line boxes might have children - // that will outlast this block. In the non-anonymous block case those - // children will be destroyed by the time we return from this function. - if (isAnonymousBlock()) { - for (InlineFlowBox* box = m_firstLineBox; box; box = box->nextFlowBox()) { - while (InlineBox* childBox = box->firstChild()) - childBox->remove(); - } - } - } else if (isInline() && parent()) - parent()->dirtyLinesFromChangedChild(this); - } - - deleteLineBoxes(); - - RenderContainer::destroy(); -} - -void RenderFlow::dirtyLinesFromChangedChild(RenderObject* child) -{ - if (!parent() || (selfNeedsLayout() && !isRenderInline()) || isTable()) - return; - - // If we have no first line box, then just bail early. - if (!firstLineBox()) { - // For an empty inline, go ahead and propagate the check up to our parent, unless the parent - // is already dirty. - if (isInline() && !parent()->selfNeedsLayout()) - parent()->dirtyLinesFromChangedChild(this); - return; - } - - // Try to figure out which line box we belong in. First try to find a previous - // line box by examining our siblings. If we didn't find a line box, then use our - // parent's first line box. - RootInlineBox* box = 0; - RenderObject* curr = 0; - for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { - if (curr->isFloatingOrPositioned()) - continue; - - if (curr->isReplaced()) { - InlineBox* wrapper = curr->inlineBoxWrapper(); - if (wrapper) - box = wrapper->root(); - } else if (curr->isText()) { - InlineTextBox* textBox = toRenderText(curr)->lastTextBox(); - if (textBox) - box = textBox->root(); - } else if (curr->isRenderInline()) { - InlineRunBox* runBox = static_cast<RenderFlow*>(curr)->lastLineBox(); - if (runBox) - box = runBox->root(); - } - - if (box) - break; - } - if (!box) - box = firstLineBox()->root(); - - // If we found a line box, then dirty it. - if (box) { - RootInlineBox* adjacentBox; - box->markDirty(); - - // dirty the adjacent lines that might be affected - // NOTE: we dirty the previous line because RootInlineBox objects cache - // the address of the first object on the next line after a BR, which we may be - // invalidating here. For more info, see how RenderBlock::layoutInlineChildren - // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak, - // despite the name, actually returns the first RenderObject after the BR. - // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize." - adjacentBox = box->prevRootBox(); - if (adjacentBox) - adjacentBox->markDirty(); - if (child->isBR() || (curr && curr->isBR())) { - adjacentBox = box->nextRootBox(); - if (adjacentBox) - adjacentBox->markDirty(); - } - } -} - -int RenderFlow::lineHeight(bool firstLine, bool /*isRootLineBox*/) const -{ - if (firstLine && document()->usesFirstLineRules()) { - RenderStyle* s = style(firstLine); - Length lh = s->lineHeight(); - if (lh.isNegative()) { - if (s == style()) { - if (m_lineHeight == -1) - m_lineHeight = RenderObject::lineHeight(false); - return m_lineHeight; - } - return s->font().lineSpacing(); - } - if (lh.isPercent()) - return lh.calcMinValue(s->fontSize()); - return lh.value(); - } - - if (m_lineHeight == -1) - m_lineHeight = RenderObject::lineHeight(false); - return m_lineHeight; -} - -void RenderFlow::dirtyLineBoxes(bool fullLayout, bool isRootLineBox) -{ - if (!isRootLineBox && isReplaced()) - return RenderContainer::dirtyLineBoxes(fullLayout, isRootLineBox); - - if (fullLayout) - deleteLineBoxes(); - else { - for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) - curr->dirtyLineBoxes(); - } -} - -InlineBox* RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool /*isOnlyRun*/) -{ - checkConsistency(); - - if (!isRootLineBox && - (isReplaced() || makePlaceHolderBox)) // Inline tables and inline blocks - return RenderContainer::createInlineBox(false, isRootLineBox); // (or positioned element placeholders). - - InlineFlowBox* flowBox = 0; - if (isRenderInline()) - flowBox = new (renderArena()) InlineFlowBox(this); - else - flowBox = new (renderArena()) RootInlineBox(this); - - if (!m_firstLineBox) - m_firstLineBox = m_lastLineBox = flowBox; - else { - m_lastLineBox->setNextLineBox(flowBox); - flowBox->setPreviousLineBox(m_lastLineBox); - m_lastLineBox = flowBox; - } - - checkConsistency(); - - return flowBox; -} - -void RenderFlow::paintLines(PaintInfo& paintInfo, int tx, int ty) -{ - // Only paint during the foreground/selection phases. - if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseOutline - && paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines && paintInfo.phase != PaintPhaseTextClip - && paintInfo.phase != PaintPhaseMask) - return; - - bool inlineFlow = isRenderInline(); - if (inlineFlow) - ASSERT(m_layer); // The only way an inline could paint like this is if it has a layer. - - // If we have no lines then we have no work to do. - if (!firstLineBox()) - return; - - // We can check the first box and last box and avoid painting if we don't - // intersect. This is a quick short-circuit that we can take to avoid walking any lines. - // FIXME: This check is flawed in the following extremely obscure way: - // if some line in the middle has a huge overflow, it might actually extend below the last line. - int yPos = firstLineBox()->root()->topOverflow() - maximalOutlineSize(paintInfo.phase); - int h = maximalOutlineSize(paintInfo.phase) + lastLineBox()->root()->bottomOverflow() - yPos; - yPos += ty; - if (yPos >= paintInfo.rect.bottom() || yPos + h <= paintInfo.rect.y()) - return; - - PaintInfo info(paintInfo); - ListHashSet<RenderFlow*> outlineObjects; - info.outlineObjects = &outlineObjects; - - // See if our root lines intersect with the dirty rect. If so, then we paint - // them. Note that boxes can easily overlap, so we can't make any assumptions - // based off positions of our first line box or our last line box. - RenderView* v = view(); - bool usePrintRect = !v->printRect().isEmpty(); - for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) { - if (usePrintRect) { - // FIXME: This is a feeble effort to avoid splitting a line across two pages. - // It is utterly inadequate, and this should not be done at paint time at all. - // The whole way objects break across pages needs to be redone. - // Try to avoid splitting a line vertically, but only if it's less than the height - // of the entire page. - if (curr->root()->bottomOverflow() - curr->root()->topOverflow() <= v->printRect().height()) { - if (ty + curr->root()->bottomOverflow() > v->printRect().bottom()) { - if (ty + curr->root()->topOverflow() < v->truncatedAt()) - v->setBestTruncatedAt(ty + curr->root()->topOverflow(), this); - // If we were able to truncate, don't paint. - if (ty + curr->root()->topOverflow() >= v->truncatedAt()) - break; - } - } - } - - int top = min(curr->root()->topOverflow(), curr->root()->selectionTop()) - maximalOutlineSize(info.phase); - int bottom = curr->root()->bottomOverflow() + maximalOutlineSize(info.phase); - h = bottom - top; - yPos = ty + top; - if (yPos < info.rect.bottom() && yPos + h > info.rect.y()) - curr->paint(info, tx, ty); - } - - if (info.phase == PaintPhaseOutline || info.phase == PaintPhaseSelfOutline || info.phase == PaintPhaseChildOutlines) { - ListHashSet<RenderFlow*>::iterator end = info.outlineObjects->end(); - for (ListHashSet<RenderFlow*>::iterator it = info.outlineObjects->begin(); it != end; ++it) { - RenderFlow* flow = *it; - flow->paintOutline(info.context, tx, ty); - } - info.outlineObjects->clear(); - } -} - -bool RenderFlow::hitTestLines(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) -{ - if (hitTestAction != HitTestForeground) - return false; - - bool inlineFlow = isRenderInline(); - if (inlineFlow) - ASSERT(m_layer); // The only way an inline can hit test like this is if it has a layer. - - // If we have no lines then we have no work to do. - if (!firstLineBox()) - return false; - - // We can check the first box and last box and avoid hit testing if we don't - // contain the point. This is a quick short-circuit that we can take to avoid walking any lines. - // FIXME: This check is flawed in the following extremely obscure way: - // if some line in the middle has a huge overflow, it might actually extend below the last line. - if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow())) - return false; - - // See if our root lines contain the point. If so, then we hit test - // them further. Note that boxes can easily overlap, so we can't make any assumptions - // based off positions of our first line box or our last line box. - for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevFlowBox()) { - if (y >= ty + curr->root()->topOverflow() && y < ty + curr->root()->bottomOverflow()) { - bool inside = curr->nodeAtPoint(request, result, x, y, tx, ty); - if (inside) { - updateHitTestResult(result, IntPoint(x - tx, y - ty)); - return true; - } - } - } - - return false; -} - -IntRect RenderFlow::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine) -{ - // Do the normal calculation in most cases. - if (firstChild() || style()->display() == INLINE) - return RenderContainer::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine); - - // This is a special case: - // The element is not an inline element, and it's empty. So we have to - // calculate a fake position to indicate where objects are to be inserted. - - // FIXME: This does not take into account either :first-line or :first-letter - // However, as soon as some content is entered, the line boxes will be - // constructed and this kludge is not called any more. So only the caret size - // of an empty :first-line'd block is wrong. I think we can live with that. - RenderStyle* currentStyle = firstLineStyle(); - int height = lineHeight(true); - const int caretWidth = 1; - - enum CaretAlignment { alignLeft, alignRight, alignCenter }; - - CaretAlignment alignment = alignLeft; - - switch (currentStyle->textAlign()) { - case TAAUTO: - case JUSTIFY: - if (currentStyle->direction() == RTL) - alignment = alignRight; - break; - case LEFT: - case WEBKIT_LEFT: - break; - case CENTER: - case WEBKIT_CENTER: - alignment = alignCenter; - break; - case RIGHT: - case WEBKIT_RIGHT: - alignment = alignRight; - break; - } - - int x = borderLeft() + paddingLeft(); - int w = width(); - - switch (alignment) { - case alignLeft: - break; - case alignCenter: - x = (x + w - (borderRight() + paddingRight())) / 2; - break; - case alignRight: - x = w - (borderRight() + paddingRight()); - break; - } - - if (extraWidthToEndOfLine) { - if (isRenderBlock()) { - *extraWidthToEndOfLine = w - (x + caretWidth); - } else { - // FIXME: This code looks wrong. - // myRight and containerRight are set up, but then clobbered. - // So *extraWidthToEndOfLine will always be 0 here. - - int myRight = x + caretWidth; - // FIXME: why call localToAbsoluteForContent() twice here, too? - FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight, 0)); - - int containerRight = containingBlock()->x() + containingBlockWidth(); - FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight, 0)); - - *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x(); - } - } - - int y = paddingTop() + borderTop(); - - return IntRect(x, y, caretWidth, height); -} - -void RenderFlow::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) -{ - if (isRenderBlock()) { - // Continuations should include their margins in the outline rect. - if (continuation()) { - bool nextInlineHasLineBox = continuation()->firstLineBox(); - bool prevInlineHasLineBox = static_cast<RenderFlow*>(continuation()->element()->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())); - } - - if (!hasOverflowClip() && !hasControlClip()) { - for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) - graphicsContext->addFocusRingRect(IntRect(tx + curr->xPos(), ty + curr->yPos(), curr->width(), curr->height())); - - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) - if (!curr->isText() && !curr->isListMarker() && curr->isBox()) { - RenderBox* box = toRenderBox(curr); - FloatPoint pos; - // FIXME: This doesn't work correctly with transforms. - if (box->layer()) - pos = curr->localToAbsolute(); - else - pos = FloatPoint(tx + box->x(), ty + box->y()); - box->addFocusRingRects(graphicsContext, pos.x(), pos.y()); - } - } - - if (continuation()) { - if (isInline()) - continuation()->addFocusRingRects(graphicsContext, - tx - containingBlock()->x() + continuation()->x(), - ty - containingBlock()->y() + continuation()->y()); - else - continuation()->addFocusRingRects(graphicsContext, - tx - x() + continuation()->containingBlock()->x(), - ty - y() + continuation()->containingBlock()->y()); - } -} - -void RenderFlow::paintOutline(GraphicsContext* graphicsContext, int tx, int ty) -{ - if (!hasOutline()) - return; - - if (style()->outlineStyleIsAuto() || hasOutlineAnnotation()) { - int ow = style()->outlineWidth(); - Color oc = style()->outlineColor(); - if (!oc.isValid()) - oc = style()->color(); - - graphicsContext->initFocusRing(ow, style()->outlineOffset()); - addFocusRingRects(graphicsContext, tx, ty); - if (style()->outlineStyleIsAuto()) - graphicsContext->drawFocusRing(oc); - else - addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); - graphicsContext->clearFocusRing(); - } - - if (style()->outlineStyleIsAuto() || style()->outlineStyle() == BNONE) - return; - - Vector<IntRect> rects; - - rects.append(IntRect()); - for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) - rects.append(IntRect(curr->xPos(), curr->yPos(), curr->width(), curr->height())); - - rects.append(IntRect()); - - for (unsigned i = 1; i < rects.size() - 1; i++) - paintOutlineForLine(graphicsContext, tx, ty, rects.at(i - 1), rects.at(i), rects.at(i + 1)); -} - -void RenderFlow::paintOutlineForLine(GraphicsContext* graphicsContext, int tx, int ty, - const IntRect& lastline, const IntRect& thisline, const IntRect& nextline) -{ - int ow = style()->outlineWidth(); - EBorderStyle os = style()->outlineStyle(); - Color oc = style()->outlineColor(); - if (!oc.isValid()) - oc = style()->color(); - - int offset = style()->outlineOffset(); - - int t = ty + thisline.y() - offset; - int l = tx + thisline.x() - offset; - int b = ty + thisline.bottom() + offset; - int r = tx + thisline.right() + offset; - - // left edge - drawBorder(graphicsContext, - l - ow, - t - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : 0), - l, - b + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : 0), - BSLeft, - oc, style()->color(), os, - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : -ow), - (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : -ow)); - - // right edge - drawBorder(graphicsContext, - r, - t - (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : 0), - r + ow, - b + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : 0), - BSRight, - oc, style()->color(), os, - (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : -ow), - (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : -ow)); - // upper edge - if (thisline.x() < lastline.x()) - drawBorder(graphicsContext, - l - ow, - t - ow, - min(r+ow, (lastline.isEmpty() ? 1000000 : tx + lastline.x())), - t , - BSTop, oc, style()->color(), os, - ow, - (!lastline.isEmpty() && tx + lastline.x() + 1 < r + ow) ? -ow : ow); - - if (lastline.right() < thisline.right()) - drawBorder(graphicsContext, - max(lastline.isEmpty() ? -1000000 : tx + lastline.right(), l - ow), - t - ow, - r + ow, - t , - BSTop, oc, style()->color(), os, - (!lastline.isEmpty() && l - ow < tx + lastline.right()) ? -ow : ow, - ow); - - // lower edge - if (thisline.x() < nextline.x()) - drawBorder(graphicsContext, - l - ow, - b, - min(r + ow, !nextline.isEmpty() ? tx + nextline.x() + 1 : 1000000), - b + ow, - BSBottom, oc, style()->color(), os, - ow, - (!nextline.isEmpty() && tx + nextline.x() + 1 < r + ow) ? -ow : ow); - - if (nextline.right() < thisline.right()) - drawBorder(graphicsContext, - max(!nextline.isEmpty() ? tx + nextline.right() : -1000000, l - ow), - b, - r + ow, - b + ow, - BSBottom, oc, style()->color(), os, - (!nextline.isEmpty() && l - ow < tx + nextline.right()) ? -ow : ow, - ow); -} - -void RenderFlow::calcMargins(int containerWidth) -{ - m_marginLeft = style()->marginLeft().calcMinValue(containerWidth); - m_marginRight = style()->marginRight().calcMinValue(containerWidth); -} - -#ifndef NDEBUG - -void RenderFlow::checkConsistency() const -{ -#ifdef CHECK_CONSISTENCY - const InlineFlowBox* prev = 0; - for (const InlineFlowBox* child = m_firstLineBox; child != 0; child = child->nextFlowBox()) { - ASSERT(child->object() == this); - ASSERT(child->prevFlowBox() == prev); - prev = child; - } - ASSERT(prev == m_lastLineBox); -#endif -} - -#endif - -} // namespace WebCore diff --git a/WebCore/rendering/RenderFlow.h b/WebCore/rendering/RenderFlow.h deleted file mode 100644 index 897e40a..0000000 --- a/WebCore/rendering/RenderFlow.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007 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 RenderFlow_h -#define RenderFlow_h - -#include "RenderContainer.h" - -namespace WebCore { - -/** - * all geometry managing stuff is only in the block elements. - * - * Inline elements don't layout themselves, but the whole paragraph - * gets flowed by the surrounding block element. This is, because - * one needs to know the whole paragraph to calculate bidirectional - * behaviour of text, so putting the layouting routines in the inline - * elements is impossible. - */ -class RenderFlow : public RenderContainer { -public: - RenderFlow(Node* node) - : RenderContainer(node) - , m_continuation(0) - , m_firstLineBox(0) - , m_lastLineBox(0) - , m_lineHeight(-1) - , m_childrenInline(true) - , m_firstLine(false) - , m_topMarginQuirk(false) - , m_bottomMarginQuirk(false) - , m_hasMarkupTruncation(false) - , m_selectionState(SelectionNone) - , m_hasColumns(false) - , m_isContinuation(false) - , m_cellWidthChanged(false) - { - } -#ifndef NDEBUG - virtual ~RenderFlow(); -#endif - - virtual RenderFlow* virtualContinuation() const { return continuation(); } - RenderFlow* continuation() const { return m_continuation; } - void setContinuation(RenderFlow* c) { m_continuation = c; } - RenderFlow* continuationBefore(RenderObject* beforeChild); - - void addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild); - virtual void addChildToFlow(RenderObject* newChild, RenderObject* beforeChild) = 0; - virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); - - static RenderFlow* createAnonymousFlow(Document*, PassRefPtr<RenderStyle>); - - void extractLineBox(InlineFlowBox*); - void attachLineBox(InlineFlowBox*); - void removeLineBox(InlineFlowBox*); - void deleteLineBoxes(); - virtual void destroy(); - - virtual void dirtyLinesFromChangedChild(RenderObject* child); - - virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; - - InlineFlowBox* firstLineBox() const { return m_firstLineBox; } - InlineFlowBox* lastLineBox() const { return m_lastLineBox; } - - virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun=false); - virtual void dirtyLineBoxes(bool fullLayout, bool isRootLineBox = false); - - void paintLines(PaintInfo&, int tx, int ty); - bool hitTestLines(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); - - virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); - - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); - void paintOutlineForLine(GraphicsContext*, int tx, int ty, const IntRect& prevLine, const IntRect& thisLine, const IntRect& nextLine); - void paintOutline(GraphicsContext*, int tx, int ty); - - virtual bool hasColumns() const { return m_hasColumns; } - - void calcMargins(int containerWidth); - - void checkConsistency() const; - -private: - // An inline can be split with blocks occurring in between the inline content. - // When this occurs we need a pointer to our next object. We can basically be - // split into a sequence of inlines and blocks. The continuation will either be - // an anonymous block (that houses other blocks) or it will be an inline flow. - RenderFlow* m_continuation; - -protected: - // For block flows, each box represents the root inline box for a line in the - // paragraph. - // For inline flows, each box represents a portion of that inline. - InlineFlowBox* m_firstLineBox; - InlineFlowBox* m_lastLineBox; - - mutable int m_lineHeight; - - // These bitfields are moved here from subclasses to pack them together - // from RenderBlock - bool m_childrenInline : 1; - bool m_firstLine : 1; - bool m_topMarginQuirk : 1; - bool m_bottomMarginQuirk : 1; - bool m_hasMarkupTruncation : 1; - unsigned m_selectionState : 3; // SelectionState - bool m_hasColumns : 1; - - // from RenderInline - bool m_isContinuation : 1; // Whether or not we're a continuation of an inline. - - // from RenderTableCell - bool m_cellWidthChanged : 1; -}; - -#ifdef NDEBUG -inline void RenderFlow::checkConsistency() const -{ -} -#endif - -} // namespace WebCore - -#endif // RenderFlow_h diff --git a/WebCore/rendering/RenderForeignObject.cpp b/WebCore/rendering/RenderForeignObject.cpp index 523601c..0584c1c 100644 --- a/WebCore/rendering/RenderForeignObject.cpp +++ b/WebCore/rendering/RenderForeignObject.cpp @@ -40,7 +40,7 @@ RenderForeignObject::RenderForeignObject(SVGForeignObjectElement* node) TransformationMatrix RenderForeignObject::translationForAttributes() { - SVGForeignObjectElement* foreign = static_cast<SVGForeignObjectElement*>(element()); + SVGForeignObjectElement* foreign = static_cast<SVGForeignObjectElement*>(node()); return TransformationMatrix().translate(foreign->x().value(foreign), foreign->y().value(foreign)); } @@ -53,7 +53,7 @@ void RenderForeignObject::paint(PaintInfo& paintInfo, int parentX, int parentY) paintInfo.context->concatCTM(TransformationMatrix().translate(parentX, parentY)); paintInfo.context->concatCTM(localTransform()); paintInfo.context->concatCTM(translationForAttributes()); - paintInfo.context->clip(getClipRect(parentX, parentY)); + paintInfo.context->clip(clipRect(parentX, parentY)); float opacity = style()->opacity(); if (opacity < 1.0f) @@ -70,18 +70,18 @@ void RenderForeignObject::paint(PaintInfo& paintInfo, int parentX, int parentY) paintInfo.context->restore(); } -void RenderForeignObject::computeRectForRepaint(IntRect& rect, RenderBox* repaintContainer, bool fixed) +void RenderForeignObject::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) { TransformationMatrix transform = translationForAttributes() * localTransform(); rect = transform.mapRect(rect); - RenderBlock::computeRectForRepaint(rect, repaintContainer, fixed); + RenderBlock::computeRectForRepaint(repaintContainer, rect, fixed); } bool RenderForeignObject::calculateLocalTransform() { TransformationMatrix oldTransform = m_localTransform; - m_localTransform = static_cast<SVGForeignObjectElement*>(element())->animatedLocalTransform(); + m_localTransform = static_cast<SVGForeignObjectElement*>(node())->animatedLocalTransform(); return (oldTransform != m_localTransform); } @@ -92,13 +92,8 @@ void RenderForeignObject::layout() // Arbitrary affine transforms are incompatible with LayoutState. view()->disableLayoutState(); - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout(); - if (checkForRepaint) { - oldBounds = m_absoluteBounds; - oldOutlineBox = absoluteOutlineBounds(); - } + // FIXME: using m_absoluteBounds breaks if containerForRepaint() is not the root + LayoutRepainter repainter(*this, checkForRepaintDuringLayout(), &m_absoluteBounds); calculateLocalTransform(); @@ -106,8 +101,7 @@ void RenderForeignObject::layout() m_absoluteBounds = absoluteClippedOverflowRect(); - if (checkForRepaint) - repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + repainter.repaintAfterLayout(); view()->enableLayoutState(); setNeedsLayout(false); @@ -118,7 +112,7 @@ bool RenderForeignObject::nodeAtPoint(const HitTestRequest& request, HitTestResu TransformationMatrix totalTransform = absoluteTransform(); totalTransform *= translationForAttributes(); double localX, localY; - totalTransform.inverse().map(x, y, &localX, &localY); + totalTransform.inverse().map(x, y, localX, localY); return RenderBlock::nodeAtPoint(request, result, static_cast<int>(localX), static_cast<int>(localY), tx, ty, hitTestAction); } diff --git a/WebCore/rendering/RenderForeignObject.h b/WebCore/rendering/RenderForeignObject.h index 28f4ddb..ab96785 100644 --- a/WebCore/rendering/RenderForeignObject.h +++ b/WebCore/rendering/RenderForeignObject.h @@ -42,7 +42,7 @@ public: virtual TransformationMatrix localTransform() const { return m_localTransform; } virtual bool calculateLocalTransform(); - virtual void computeRectForRepaint(IntRect&, RenderBox* repaintContainer, bool fixed = false); + virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); virtual bool requiresLayer() const { return false; } virtual void layout(); diff --git a/WebCore/rendering/RenderFrame.h b/WebCore/rendering/RenderFrame.h index d7c8c5a..9050077 100644 --- a/WebCore/rendering/RenderFrame.h +++ b/WebCore/rendering/RenderFrame.h @@ -41,7 +41,7 @@ public: #ifdef FLATTEN_FRAMESET virtual void layout(); #endif - HTMLFrameElement* element() const { return static_cast<HTMLFrameElement*>(RenderPart::element()); } + HTMLFrameElement* element() const { return static_cast<HTMLFrameElement*>(RenderPart::node()); } FrameEdgeInfo edgeInfo() const; diff --git a/WebCore/rendering/RenderFrameSet.cpp b/WebCore/rendering/RenderFrameSet.cpp index f6cd4df..0353bb1 100644 --- a/WebCore/rendering/RenderFrameSet.cpp +++ b/WebCore/rendering/RenderFrameSet.cpp @@ -42,7 +42,7 @@ namespace WebCore { RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet) - : RenderContainer(frameSet) + : RenderBox(frameSet) , m_isResizing(false) , m_isChildResizing(false) #ifdef FLATTEN_FRAMESET @@ -164,11 +164,11 @@ bool RenderFrameSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& r if (action != HitTestForeground) return false; - bool inside = RenderContainer::nodeAtPoint(request, result, x, y, tx, ty, action) - || m_isResizing || canResize(IntPoint(x, y)); + bool inside = RenderBox::nodeAtPoint(request, result, x, y, tx, ty, action) + || m_isResizing; if (inside && frameSet()->noResize() - && !request.readonly && !result.innerNode()) { + && !request.readOnly() && !result.innerNode()) { result.setInnerNode(node()); result.setInnerNonSharedNode(node()); } @@ -498,7 +498,7 @@ void RenderFrameSet::layout() positionFrames(); - RenderContainer::layout(); + RenderBox::layout(); computeEdgeInfo(); @@ -668,8 +668,8 @@ bool RenderFrameSet::userResize(MouseEvent* evt) return false; if (evt->type() == eventNames().mousedownEvent && evt->button() == LeftButton) { FloatPoint pos = localToAbsolute(); - startResizing(m_cols, evt->pageX() - pos.x()); - startResizing(m_rows, evt->pageY() - pos.y()); + startResizing(m_cols, evt->absoluteLocation().x() - pos.x()); + startResizing(m_rows, evt->absoluteLocation().y() - pos.y()); if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) { setIsResizing(true); return true; @@ -678,8 +678,8 @@ bool RenderFrameSet::userResize(MouseEvent* evt) } else { if (evt->type() == eventNames().mousemoveEvent || (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton)) { FloatPoint pos = localToAbsolute(); - continueResizing(m_cols, evt->pageX() - pos.x()); - continueResizing(m_rows, evt->pageY() - pos.y()); + continueResizing(m_cols, evt->absoluteLocation().x() - pos.x()); + continueResizing(m_rows, evt->absoluteLocation().y() - pos.y()); if (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton) { setIsResizing(false); return true; @@ -710,11 +710,6 @@ bool RenderFrameSet::isResizingColumn() const return m_isResizing && m_cols.m_splitBeingResized != noSplit; } -bool RenderFrameSet::canResize(const IntPoint& p) const -{ - return hitTestSplit(m_cols, p.x()) != noSplit || hitTestSplit(m_rows, p.y()) != noSplit; -} - bool RenderFrameSet::canResizeRow(const IntPoint& p) const { int r = hitTestSplit(m_rows, p.y()); diff --git a/WebCore/rendering/RenderFrameSet.h b/WebCore/rendering/RenderFrameSet.h index 066dbab..0c80ad9 100644 --- a/WebCore/rendering/RenderFrameSet.h +++ b/WebCore/rendering/RenderFrameSet.h @@ -23,7 +23,7 @@ #ifndef RenderFrameSet_h #define RenderFrameSet_h -#include "RenderContainer.h" +#include "RenderBox.h" namespace WebCore { @@ -53,11 +53,16 @@ private: Vector<bool> m_allowBorder; }; -class RenderFrameSet : public RenderContainer { +class RenderFrameSet : public RenderBox { public: RenderFrameSet(HTMLFrameSetElement*); virtual ~RenderFrameSet(); + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + virtual const char* renderName() const { return "RenderFrameSet"; } virtual bool isFrameSet() const { return true; } @@ -97,7 +102,6 @@ private: inline HTMLFrameSetElement* frameSet() const; - bool canResize(const IntPoint&) const; void setIsResizing(bool); void layOutAxis(GridAxis&, const Length*, int availableSpace); @@ -114,6 +118,8 @@ private: void paintRowBorder(const PaintInfo& paintInfo, const IntRect& rect); void paintColumnBorder(const PaintInfo& paintInfo, const IntRect& rect); + RenderObjectChildList m_children; + GridAxis m_rows; GridAxis m_cols; diff --git a/WebCore/rendering/RenderHTMLCanvas.cpp b/WebCore/rendering/RenderHTMLCanvas.cpp index f4d88d8..1fc07f0 100644 --- a/WebCore/rendering/RenderHTMLCanvas.cpp +++ b/WebCore/rendering/RenderHTMLCanvas.cpp @@ -31,6 +31,7 @@ #include "HTMLCanvasElement.h" #include "HTMLNames.h" #include "RenderView.h" +#include "FrameView.h" namespace WebCore { diff --git a/WebCore/rendering/RenderImage.cpp b/WebCore/rendering/RenderImage.cpp index e67a4ca..f48a219 100644 --- a/WebCore/rendering/RenderImage.cpp +++ b/WebCore/rendering/RenderImage.cpp @@ -38,6 +38,7 @@ #include "Page.h" #include "RenderView.h" #include <wtf/CurrentTime.h> +#include <wtf/UnusedParam.h> #ifdef ANDROID_LAYOUT #include "Settings.h" @@ -315,9 +316,32 @@ void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect) repaintRect = contentBoxRect(); repaintRectangle(repaintRect); + +#if USE(ACCELERATED_COMPOSITING) + if (hasLayer()) { + // Tell any potential compositing layers that the image needs updating. + layer()->rendererContentChanged(); + } +#endif } } +void RenderImage::notifyFinished(CachedResource* newImage) +{ + if (documentBeingDestroyed()) + return; + +#if USE(ACCELERATED_COMPOSITING) + if ((newImage == m_cachedImage) && hasLayer()) { + // tell any potential compositing layers + // that the image is done and they can reference it directly. + layer()->rendererContentChanged(); + } +#else + UNUSED_PARAM(newImage); +#endif +} + void RenderImage::resetAnimation() { if (m_cachedImage) { @@ -406,7 +430,7 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty) IntSize contentSize(cWidth, cHeight); bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, contentSize); IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), contentSize); - HTMLImageElement* imageElt = (element() && element()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(element()) : 0; + HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(node()) : 0; CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; context->drawImage(image(cWidth, cHeight), rect, compositeOperator, useLowQualityScaling); } @@ -419,41 +443,44 @@ int RenderImage::minimumReplacedHeight() const HTMLMapElement* RenderImage::imageMap() { - HTMLImageElement* i = element() && element()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(element()) : 0; + HTMLImageElement* i = node() && node()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(node()) : 0; return i ? i->document()->getImageMap(i->useMap()) : 0; } bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) { - bool inside = RenderReplaced::nodeAtPoint(request, result, _x, _y, _tx, _ty, hitTestAction); + HitTestResult tempResult(result.point()); + bool inside = RenderReplaced::nodeAtPoint(request, tempResult, _x, _y, _tx, _ty, hitTestAction); - if (inside && element()) { + if (inside && node()) { int tx = _tx + x(); int ty = _ty + y(); HTMLMapElement* map = imageMap(); if (map) { // we're a client side image map - inside = map->mapMouseEvent(_x - tx, _y - ty, IntSize(contentWidth(), contentHeight()), result); - result.setInnerNonSharedNode(element()); + inside = map->mapMouseEvent(_x - tx, _y - ty, IntSize(contentWidth(), contentHeight()), tempResult); + tempResult.setInnerNonSharedNode(node()); } } + if (inside) + result = tempResult; return inside; } void RenderImage::updateAltText() { - if (!element()) + if (!node()) return; - if (element()->hasTagName(inputTag)) - m_altText = static_cast<HTMLInputElement*>(element())->altText(); - else if (element()->hasTagName(imgTag)) - m_altText = static_cast<HTMLImageElement*>(element())->altText(); + if (node()->hasTagName(inputTag)) + m_altText = static_cast<HTMLInputElement*>(node())->altText(); + else if (node()->hasTagName(imgTag)) + m_altText = static_cast<HTMLImageElement*>(node())->altText(); #if ENABLE(WML) - else if (element()->hasTagName(WMLNames::imgTag)) - m_altText = static_cast<WMLImageElement*>(element())->altText(); + else if (node()->hasTagName(WMLNames::imgTag)) + m_altText = static_cast<WMLImageElement*>(node())->altText(); #endif } @@ -516,7 +543,7 @@ int RenderImage::calcReplacedWidth(bool includeMaxWidth) const width = max(minW, min(width, maxW)); // in SSR mode, we will fit the image to its container width if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { - int cw = containingBlockWidth(); + int cw = containingBlockWidthForContent(); if (cw && width>cw) width = cw; } @@ -555,7 +582,7 @@ int RenderImage::calcReplacedHeight() const calcReplacedWidthUsing(style()->maxWidth()); width = max(minW, min(width, maxW)); - int cw = containingBlockWidth(); + int cw = containingBlockWidthForContent(); if (cw && width && width>cw) height = cw * height / width; // preserve aspect ratio } diff --git a/WebCore/rendering/RenderImage.h b/WebCore/rendering/RenderImage.h index 71896d6..042452f 100644 --- a/WebCore/rendering/RenderImage.h +++ b/WebCore/rendering/RenderImage.h @@ -48,6 +48,7 @@ public: virtual int minimumReplacedHeight() const; virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + virtual void notifyFinished(CachedResource*); bool setImageSizeForAltText(CachedImage* newImage = 0); @@ -102,6 +103,21 @@ protected: friend class RenderImageScaleObserver; }; +inline RenderImage* toRenderImage(RenderObject* o) +{ + ASSERT(!o || o->isRenderImage()); + return static_cast<RenderImage*>(o); +} + +inline const RenderImage* toRenderImage(const RenderObject* o) +{ + ASSERT(!o || o->isRenderImage()); + return static_cast<const RenderImage*>(o); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderImage(const RenderImage*); + } // namespace WebCore #endif // RenderImage_h diff --git a/WebCore/rendering/RenderImageGeneratedContent.h b/WebCore/rendering/RenderImageGeneratedContent.h index cab0192..9f8330d 100644 --- a/WebCore/rendering/RenderImageGeneratedContent.h +++ b/WebCore/rendering/RenderImageGeneratedContent.h @@ -27,16 +27,14 @@ #define RenderImageGeneratedContent_h #include "RenderImage.h" +#include "StyleImage.h" #include <wtf/RefPtr.h> -#include "RenderStyle.h" - namespace WebCore { class StyleImage; -class RenderImageGeneratedContent : public RenderImage -{ +class RenderImageGeneratedContent : public RenderImage { public: RenderImageGeneratedContent(Node*); virtual ~RenderImageGeneratedContent(); @@ -53,12 +51,14 @@ protected: virtual bool imageHasRelativeWidth() const { return m_styleImage->imageHasRelativeWidth(); } virtual bool imageHasRelativeHeight() const { return m_styleImage->imageHasRelativeHeight(); } virtual IntSize imageSize(float multiplier) const { return m_styleImage->imageSize(this, multiplier); } - virtual WrappedImagePtr imagePtr() const { return m_styleImage->data(); } + + // |m_styleImage| can be 0 if we get a callback for a background image from RenderObject::setStyle. + virtual WrappedImagePtr imagePtr() const { return m_styleImage ? m_styleImage->data() : 0; } private: RefPtr<StyleImage> m_styleImage; }; -} +} // namespace WebCore -#endif +#endif // RenderImageGeneratedContent_h diff --git a/WebCore/rendering/RenderInline.cpp b/WebCore/rendering/RenderInline.cpp index 4f4412d..8f98427 100644 --- a/WebCore/rendering/RenderInline.cpp +++ b/WebCore/rendering/RenderInline.cpp @@ -26,28 +26,94 @@ #include "RenderInline.h" #include "FloatQuad.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" +#include "Page.h" #include "RenderArena.h" #include "RenderBlock.h" #include "RenderView.h" #include "VisiblePosition.h" +#if ENABLE(DASHBOARD_SUPPORT) +#include "Frame.h" +#endif + +using namespace std; + namespace WebCore { RenderInline::RenderInline(Node* node) - : RenderFlow(node) + : RenderBoxModelObject(node) + , m_continuation(0) + , m_lineHeight(-1) + , m_verticalPosition(PositionUndefined) { + setChildrenInline(true); } RenderInline::~RenderInline() { } -void RenderInline::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderInline::destroy() { - RenderFlow::styleDidChange(diff, oldStyle); + // Detach our continuation first. + if (m_continuation) + m_continuation->destroy(); + m_continuation = 0; + + // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will + // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. + children()->destroyLeftoverChildren(); + + if (!documentBeingDestroyed()) { + if (firstLineBox()) { + // We can't wait for RenderBoxModelObject::destroy to clear the selection, + // because by then we will have nuked the line boxes. + // FIXME: The SelectionController should be responsible for this when it + // is notified of DOM mutations. + if (isSelectionBorder()) + view()->clearSelection(); + + // If line boxes are contained inside a root, that means we're an inline. + // In that case, we need to remove all the line boxes so that the parent + // lines aren't pointing to deleted children. If the first line box does + // not have a parent that means they are either already disconnected or + // root lines that can just be destroyed without disconnecting. + if (firstLineBox()->parent()) { + for (InlineRunBox* box = firstLineBox(); box; box = box->nextLineBox()) + box->remove(); + } + } else if (isInline() && parent()) + parent()->dirtyLinesFromChangedChild(this); + } - setInline(true); - setHasReflection(false); + m_lineBoxes.deleteLineBoxes(renderArena()); + + RenderBoxModelObject::destroy(); +} + +RenderInline* RenderInline::inlineContinuation() const +{ + if (!m_continuation || m_continuation->isInline()) + return toRenderInline(m_continuation); + return toRenderBlock(m_continuation)->inlineContinuation(); +} + +void RenderInline::updateBoxModelInfoFromStyle() +{ + RenderBoxModelObject::updateBoxModelInfoFromStyle(); + + setInline(true); // Needed for run-ins, since run-in is considered a block display type. + + // FIXME: Support transforms and reflections on inline flows someday. + setHasTransform(false); + setHasReflection(false); +} + +void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBoxModelObject::styleDidChange(diff, oldStyle); // Ensure that all of the split inlines pick up the new style. We // only do this if we're an inline, since we don't want to propagate @@ -55,36 +121,27 @@ void RenderInline::styleDidChange(RenderStyle::Diff diff, const RenderStyle* old // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before // and after the block share the same style, but the block doesn't // need to pass its style on to anyone else. - RenderFlow* currCont = continuation(); - while (currCont) { - if (currCont->isInline()) { - RenderFlow* nextCont = currCont->continuation(); - currCont->setContinuation(0); - currCont->setStyle(style()); - currCont->setContinuation(nextCont); - } - currCont = currCont->continuation(); + for (RenderInline* currCont = inlineContinuation(); currCont; currCont = currCont->inlineContinuation()) { + RenderBoxModelObject* nextCont = currCont->continuation(); + currCont->setContinuation(0); + currCont->setStyle(style()); + currCont->setContinuation(nextCont); } m_lineHeight = -1; // Update pseudos for :before and :after now. if (!isAnonymous() && document()->usesBeforeAfterRules()) { - updateBeforeAfterContent(RenderStyle::BEFORE); - updateBeforeAfterContent(RenderStyle::AFTER); + children()->updateBeforeAfterContent(this, BEFORE); + children()->updateBeforeAfterContent(this, AFTER); } } -bool RenderInline::isInlineContinuation() const -{ - return m_isContinuation; -} - static inline bool isAfterContent(RenderObject* child) { if (!child) return false; - if (child->style()->styleType() != RenderStyle::AFTER) + if (child->style()->styleType() != AFTER) return false; // Text nodes don't have their own styles, so ignore the style on a text node. if (child->isText() && !child->isBR()) @@ -92,7 +149,46 @@ static inline bool isAfterContent(RenderObject* child) return true; } -void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild) +void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + if (continuation()) + return addChildToContinuation(newChild, beforeChild); + return addChildIgnoringContinuation(newChild, beforeChild); +} + +static RenderBoxModelObject* nextContinuation(RenderObject* renderer) +{ + if (renderer->isInline() && !renderer->isReplaced()) + return toRenderInline(renderer)->continuation(); + return toRenderBlock(renderer)->inlineContinuation(); +} + +RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild) +{ + if (beforeChild && beforeChild->parent() == this) + return this; + + RenderBoxModelObject* curr = nextContinuation(this); + RenderBoxModelObject* nextToLast = this; + RenderBoxModelObject* last = this; + while (curr) { + if (beforeChild && beforeChild->parent() == curr) { + if (curr->firstChild() == beforeChild) + return last; + return curr; + } + + nextToLast = last; + last = curr; + curr = nextContinuation(curr); + } + + if (!beforeChild && !last->firstChild()) + return nextToLast; + return last; +} + +void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) { // Make sure we don't append things after :after-generated content if we have it. if (!beforeChild && isAfterContent(lastChild())) @@ -109,7 +205,7 @@ void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeCh RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); newBox->setStyle(newStyle.release()); - RenderFlow* oldContinuation = continuation(); + RenderBoxModelObject* oldContinuation = continuation(); setContinuation(newBox); // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content @@ -117,7 +213,7 @@ void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeCh // content gets properly destroyed. bool isLastChild = (beforeChild == lastChild()); if (document()->usesBeforeAfterRules()) - updateBeforeAfterContent(RenderStyle::AFTER); + children()->updateBeforeAfterContent(this, AFTER); if (isLastChild && beforeChild != lastChild()) beforeChild = 0; // We destroyed the last child, so now we need to update our insertion // point to be 0. It's just a straight append now. @@ -126,22 +222,21 @@ void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeCh return; } - RenderContainer::addChild(newChild, beforeChild); + RenderBoxModelObject::addChild(newChild, beforeChild); newChild->setNeedsLayoutAndPrefWidthsRecalc(); } -RenderInline* RenderInline::cloneInline(RenderFlow* src) +RenderInline* RenderInline::cloneInline(RenderInline* src) { - RenderInline* o = new (src->renderArena()) RenderInline(src->element()); - o->m_isContinuation = true; + RenderInline* o = new (src->renderArena()) RenderInline(src->node()); o->setStyle(src->style()); return o; } void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock, - RenderObject* beforeChild, RenderFlow* oldCont) + RenderObject* beforeChild, RenderBoxModelObject* oldCont) { // Create a clone of this inline. RenderInline* clone = cloneInline(this); @@ -153,18 +248,18 @@ void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, while (o) { RenderObject* tmp = o; o = tmp->nextSibling(); - clone->addChildToFlow(removeChildNode(tmp), 0); + clone->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0); tmp->setNeedsLayoutAndPrefWidthsRecalc(); } // Hook |clone| up as the continuation of the middle block. - middleBlock->setContinuation(clone); + middleBlock->setInlineContinuation(clone); // We have been reparented and are now under the fromBlock. We need // to walk up our inline parent chain until we hit the containing block. // Once we hit the containing block we're done. - RenderFlow* curr = static_cast<RenderFlow*>(parent()); - RenderFlow* currChild = this; + RenderBoxModelObject* curr = static_cast<RenderBoxModelObject*>(parent()); + RenderBoxModelObject* currChild = this; // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone. // There will eventually be a better approach to this problem that will let us nest to a much @@ -173,24 +268,26 @@ void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, unsigned splitDepth = 1; const unsigned cMaxSplitDepth = 200; while (curr && curr != fromBlock) { + ASSERT(curr->isRenderInline()); if (splitDepth < cMaxSplitDepth) { // Create a new clone. RenderInline* cloneChild = clone; - clone = cloneInline(curr); + clone = cloneInline(toRenderInline(curr)); // Insert our child clone as the first child. - clone->addChildToFlow(cloneChild, 0); + clone->addChildIgnoringContinuation(cloneChild, 0); // Hook the clone up as a continuation of |curr|. - RenderFlow* oldCont = curr->continuation(); - curr->setContinuation(clone); + RenderInline* inlineCurr = toRenderInline(curr); + oldCont = inlineCurr->continuation(); + inlineCurr->setContinuation(clone); clone->setContinuation(oldCont); // Someone may have indirectly caused a <q> to split. When this happens, the :after content // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after // content gets properly destroyed. if (document()->usesBeforeAfterRules()) - curr->updateBeforeAfterContent(RenderStyle::AFTER); + inlineCurr->children()->updateBeforeAfterContent(this, AFTER); // Now we need to take all of the children starting from the first child // *after* currChild and append them all to the clone. @@ -198,19 +295,19 @@ void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, while (o) { RenderObject* tmp = o; o = tmp->nextSibling(); - clone->addChildToFlow(curr->removeChildNode(tmp), 0); + clone->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0); tmp->setNeedsLayoutAndPrefWidthsRecalc(); } } // Keep walking up the chain. currChild = curr; - curr = static_cast<RenderFlow*>(curr->parent()); + curr = static_cast<RenderBoxModelObject*>(curr->parent()); splitDepth++; } // Now we are at the block level. We need to put the clone into the toBlock. - toBlock->appendChildNode(clone); + toBlock->children()->appendChildNode(toBlock, clone); // Now take all the children after currChild and remove them from the fromBlock // and put them in the toBlock. @@ -218,12 +315,12 @@ void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, while (o) { RenderObject* tmp = o; o = tmp->nextSibling(); - toBlock->appendChildNode(fromBlock->removeChildNode(tmp)); + toBlock->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp)); } } void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, - RenderObject* newChild, RenderFlow* oldCont) + RenderObject* newChild, RenderBoxModelObject* oldCont) { RenderBlock* pre = 0; RenderBlock* block = containingBlock(); @@ -235,6 +332,7 @@ void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) { // We can reuse this block and make it the preBlock of the next continuation. pre = block; + pre->removePositionedObjects(0); block = block->containingBlock(); } else { // No anonymous block available for use. Make one. @@ -246,9 +344,9 @@ void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); if (madeNewBeforeBlock) - block->insertChildNode(pre, boxFirst); - block->insertChildNode(newBlockBox, boxFirst); - block->insertChildNode(post, boxFirst); + block->children()->insertChildNode(block, pre, boxFirst); + block->children()->insertChildNode(block, newBlockBox, boxFirst); + block->children()->insertChildNode(block, post, boxFirst); block->setChildrenInline(false); if (madeNewBeforeBlock) { @@ -256,7 +354,7 @@ void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox while (o) { RenderObject* no = o; o = no->nextSibling(); - pre->appendChildNode(block->removeChildNode(no)); + pre->children()->appendChildNode(pre, block->children()->removeChildNode(block, no)); no->setNeedsLayoutAndPrefWidthsRecalc(); } } @@ -267,12 +365,10 @@ void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox // time in makeChildrenNonInline by just setting this explicitly up front. newBlockBox->setChildrenInline(false); - // We don't just call addChild, since it would pass things off to the - // continuation, so we call addChildToFlow explicitly instead. We delayed - // adding the newChild until now so that the |newBlockBox| would be fully + // We delayed adding the newChild until now so that the |newBlockBox| would be fully // connected, thus allowing newChild access to a renderArena should it need // to wrap itself in additional boxes (e.g., table construction). - newBlockBox->addChildToFlow(newChild, 0); + newBlockBox->addChild(newChild); // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) // get deleted properly. Because objects moves from the pre block into the post block, we want to @@ -282,41 +378,78 @@ void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox post->setNeedsLayoutAndPrefWidthsRecalc(); } +void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) +{ + RenderBoxModelObject* flow = continuationBefore(beforeChild); + ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline()); + RenderBoxModelObject* beforeChildParent = 0; + if (beforeChild) + beforeChildParent = static_cast<RenderBoxModelObject*>(beforeChild->parent()); + else { + RenderBoxModelObject* cont = nextContinuation(flow); + if (cont) + beforeChildParent = cont; + else + beforeChildParent = flow; + } + + if (newChild->isFloatingOrPositioned()) + return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); + + // A continuation always consists of two potential candidates: an inline or an anonymous + // block box holding block children. + bool childInline = newChild->isInline(); + bool bcpInline = beforeChildParent->isInline(); + bool flowInline = flow->isInline(); + + if (flow == beforeChildParent) + return flow->addChildIgnoringContinuation(newChild, beforeChild); + else { + // The goal here is to match up if we can, so that we can coalesce and create the + // minimal # of continuations needed for the inline. + if (childInline == bcpInline) + return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); + else if (flowInline == childInline) + return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append. + else + return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); + } +} + void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty) { - paintLines(paintInfo, tx, ty); + m_lineBoxes.paint(this, paintInfo, tx, ty); } void RenderInline::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel) { - for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) - rects.append(IntRect(tx + curr->xPos(), ty + curr->yPos(), curr->width(), curr->height())); + if (InlineRunBox* curr = firstLineBox()) { + for (; curr; curr = curr->nextLineBox()) + rects.append(IntRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())); + } else + rects.append(IntRect(tx, ty, 0, 0)); - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { - if (curr->isBox()) { - RenderBox* box = toRenderBox(curr); - curr->absoluteRects(rects, tx + box->x(), ty + box->y(), false); - } + if (continuation() && topLevel) { + if (continuation()->isBox()) { + RenderBox* box = toRenderBox(continuation()); + continuation()->absoluteRects(rects, + tx - containingBlock()->x() + box->x(), + ty - containingBlock()->y() + box->y(), + topLevel); + } else + continuation()->absoluteRects(rects, tx - containingBlock()->x(), ty - containingBlock()->y(), topLevel); } - - if (continuation() && topLevel) - continuation()->absoluteRects(rects, - tx - containingBlock()->x() + continuation()->x(), - ty - containingBlock()->y() + continuation()->y(), - topLevel); } void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool topLevel) { - for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { - FloatRect localRect(curr->xPos(), curr->yPos(), curr->width(), curr->height()); - quads.append(localToAbsoluteQuad(localRect)); - } - - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { - if (!curr->isText()) - curr->absoluteQuads(quads, false); - } + if (InlineRunBox* curr = firstLineBox()) { + for (; curr; curr = curr->nextLineBox()) { + FloatRect localRect(curr->x(), curr->y(), curr->width(), curr->height()); + quads.append(localToAbsoluteQuad(localRect)); + } + } else + quads.append(localToAbsoluteQuad(FloatRect())); if (continuation() && topLevel) continuation()->absoluteQuads(quads, topLevel); @@ -324,20 +457,44 @@ void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool topLevel) int RenderInline::offsetLeft() const { - int x = RenderFlow::offsetLeft(); + int x = RenderBoxModelObject::offsetLeft(); if (firstLineBox()) - x += firstLineBox()->xPos(); + x += firstLineBox()->x(); return x; } int RenderInline::offsetTop() const { - int y = RenderFlow::offsetTop(); + int y = RenderBoxModelObject::offsetTop(); if (firstLineBox()) - y += firstLineBox()->yPos(); + y += firstLineBox()->y(); return y; } +int RenderInline::marginLeft() const +{ + Length margin = style()->marginLeft(); + if (margin.isAuto()) + return 0; + if (margin.isFixed()) + return margin.value(); + if (margin.isPercent()) + return margin.calcMinValue(max(0, containingBlock()->availableWidth())); + return 0; +} + +int RenderInline::marginRight() const +{ + Length margin = style()->marginRight(); + if (margin.isAuto()) + return 0; + if (margin.isFixed()) + return margin.value(); + if (margin.isPercent()) + return margin.calcMinValue(max(0, containingBlock()->availableWidth())); + return 0; +} + const char* RenderInline::renderName() const { if (isRelPositioned()) @@ -352,24 +509,31 @@ const char* RenderInline::renderName() const bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) { - return hitTestLines(request, result, x, y, tx, ty, hitTestAction); + return m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction); } -VisiblePosition RenderInline::positionForCoordinates(int x, int y) +VisiblePosition RenderInline::positionForPoint(const IntPoint& point) { - // Translate the coords from the pre-anonymous block to the post-anonymous block. + // FIXME: Does not deal with relative positioned inlines (should it?) RenderBlock* cb = containingBlock(); - int parentBlockX = cb->x() + x; - int parentBlockY = cb->y() + y; - for (RenderFlow* c = continuation(); c; c = c->continuation()) { - RenderFlow* contBlock = c; - if (c->isInline()) - contBlock = c->containingBlock(); + if (firstLineBox()) { + // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We + // should try to find a result by asking our containing block. + return cb->positionForPoint(point); + } + + // Translate the coords from the pre-anonymous block to the post-anonymous block. + int parentBlockX = cb->x() + point.x(); + int parentBlockY = cb->y() + point.y(); + RenderBoxModelObject* c = continuation(); + while (c) { + RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c); if (c->isInline() || c->firstChild()) return c->positionForCoordinates(parentBlockX - contBlock->x(), parentBlockY - contBlock->y()); + c = toRenderBlock(c)->inlineContinuation(); } - - return RenderFlow::positionForCoordinates(x, y); + + return RenderBoxModelObject::positionForPoint(point); } IntRect RenderInline::linesBoundingBox() const @@ -385,21 +549,21 @@ IntRect RenderInline::linesBoundingBox() const int leftSide = 0; int rightSide = 0; for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { - if (curr == firstLineBox() || curr->xPos() < leftSide) - leftSide = curr->xPos(); - if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide) - rightSide = curr->xPos() + curr->width(); + if (curr == firstLineBox() || curr->x() < leftSide) + leftSide = curr->x(); + if (curr == firstLineBox() || curr->x() + curr->width() > rightSide) + rightSide = curr->x() + curr->width(); } result.setWidth(rightSide - leftSide); result.setX(leftSide); - result.setHeight(lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos()); - result.setY(firstLineBox()->yPos()); + result.setHeight(lastLineBox()->y() + lastLineBox()->height() - firstLineBox()->y()); + result.setY(firstLineBox()->y()); } return result; } -IntRect RenderInline::clippedOverflowRectForRepaint(RenderBox* repaintContainer) +IntRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { // Only run-ins are allowed in here during layout. ASSERT(!view() || !view()->layoutStateEnabled() || isRunIn()); @@ -421,7 +585,7 @@ IntRect RenderInline::clippedOverflowRectForRepaint(RenderBox* repaintContainer) for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb; inlineFlow = inlineFlow->parent()) { if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer()) - toRenderBox(inlineFlow)->layer()->relativePositionOffset(left, top); + toRenderInline(inlineFlow)->layer()->relativePositionOffset(left, top); } IntRect r(-ow + left, -ow + top, boundingBox.width() + ow * 2, boundingBox.height() + ow * 2); @@ -439,8 +603,11 @@ IntRect RenderInline::clippedOverflowRectForRepaint(RenderBox* repaintContainer) IntRect repaintRect(x, y, r.width(), r.height()); r = intersection(repaintRect, boxRect); } - ASSERT(repaintContainer != this); - cb->computeRectForRepaint(r, repaintContainer); + + // FIXME: need to ensure that we compute the correct repaint rect when the repaint container + // is an inline. + if (repaintContainer != this) + cb->computeRectForRepaint(repaintContainer, r); if (ow) { for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { @@ -459,4 +626,409 @@ IntRect RenderInline::clippedOverflowRectForRepaint(RenderBox* repaintContainer) return r; } +IntRect RenderInline::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth) +{ + IntRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText()) + r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth)); + } + return r; +} + +void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) +{ + if (RenderView* v = view()) { + // LayoutState is only valid for root-relative repainting + if (v->layoutStateEnabled() && !repaintContainer) { + LayoutState* layoutState = v->layoutState(); + if (style()->position() == RelativePosition && layer()) + rect.move(layer()->relativePositionOffset()); + rect.move(layoutState->m_offset); + if (layoutState->m_clipped) + rect.intersect(layoutState->m_clipRect); + return; + } + } + + if (repaintContainer == this) + return; + + RenderObject* o = container(); + if (!o) + return; + + IntPoint topLeft = rect.location(); + + if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) { + RenderBlock* cb = toRenderBlock(o); + if (cb->hasColumns()) { + IntRect repaintRect(topLeft, rect.size()); + cb->adjustRectForColumns(repaintRect); + topLeft = repaintRect.location(); + rect = repaintRect; + } + } + + if (style()->position() == RelativePosition && layer()) { + // Apply the relative position offset when invalidating a rectangle. The layer + // is translated, but the render box isn't, so we need to do this to get the + // right dirty rect. Since this is called from RenderObject::setStyle, the relative position + // flag on the RenderObject has been cleared, so use the one on the style(). + topLeft += layer()->relativePositionOffset(); + } + + // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, + // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer. + if (o->hasOverflowClip()) { + RenderBox* containerBox = toRenderBox(o); + + // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the + // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint + // anyway if its size does change. + topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden. + + IntRect repaintRect(topLeft, rect.size()); + IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height()); + rect = intersection(repaintRect, boxRect); + if (rect.isEmpty()) + return; + } else + rect.setLocation(topLeft); + + o->computeRectForRepaint(repaintContainer, rect, fixed); +} + +void RenderInline::updateDragState(bool dragOn) +{ + RenderBoxModelObject::updateDragState(dragOn); + if (continuation()) + continuation()->updateDragState(dragOn); +} + +void RenderInline::childBecameNonInline(RenderObject* child) +{ + // We have to split the parent flow. + RenderBlock* newBox = containingBlock()->createAnonymousBlock(); + RenderBoxModelObject* oldContinuation = continuation(); + setContinuation(newBox); + RenderObject* beforeChild = child->nextSibling(); + children()->removeChildNode(this, child); + splitFlow(beforeChild, newBox, child, oldContinuation); +} + +void RenderInline::updateHitTestResult(HitTestResult& result, const IntPoint& point) +{ + if (result.innerNode()) + return; + + Node* n = node(); + IntPoint localPoint(point); + if (n) { + if (isInlineContinuation()) { + // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space + // of the principal renderer's containing block. This will end up being the innerNonSharedNode. + RenderBlock* firstBlock = n->renderer()->containingBlock(); + + // Get our containing block. + RenderBox* block = containingBlock(); + localPoint.move(block->x() - firstBlock->x(), block->y() - firstBlock->y()); + } + + result.setInnerNode(n); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(n); + result.setLocalPoint(localPoint); + } +} + +void RenderInline::dirtyLineBoxes(bool fullLayout) +{ + if (fullLayout) + m_lineBoxes.deleteLineBoxes(renderArena()); + else + m_lineBoxes.dirtyLineBoxes(); +} + +InlineFlowBox* RenderInline::createFlowBox() +{ + return new (renderArena()) InlineFlowBox(this); +} + +InlineFlowBox* RenderInline::createInlineFlowBox() +{ + InlineFlowBox* flowBox = createFlowBox(); + m_lineBoxes.appendLineBox(flowBox); + return flowBox; +} + +int RenderInline::lineHeight(bool firstLine, bool /*isRootLineBox*/) const +{ + if (firstLine && document()->usesFirstLineRules()) { + RenderStyle* s = style(firstLine); + if (s != style()) + return s->computedLineHeight(); + } + + if (m_lineHeight == -1) + m_lineHeight = style()->computedLineHeight(); + + return m_lineHeight; +} + +int RenderInline::verticalPositionFromCache(bool firstLine) const +{ + if (firstLine) // We're only really a first-line style if the document actually uses first-line rules. + firstLine = document()->usesFirstLineRules(); + int vpos = m_verticalPosition; + if (m_verticalPosition == PositionUndefined || firstLine) { + vpos = verticalPosition(firstLine); + if (!firstLine) + m_verticalPosition = vpos; + } + return vpos; +} + +IntSize RenderInline::relativePositionedInlineOffset(const RenderBox* child) const +{ + ASSERT(isRelPositioned()); + if (!isRelPositioned()) + return IntSize(); + + // When we have an enclosing relpositioned inline, we need to add in the offset of the first line + // box from the rest of the content, but only in the cases where we know we're positioned + // relative to the inline itself. + + IntSize offset; + int sx; + int sy; + if (firstLineBox()) { + sx = firstLineBox()->x(); + sy = firstLineBox()->y(); + } else { + sx = layer()->staticX(); + sy = layer()->staticY(); + } + + if (!child->style()->hasStaticX()) + offset.setWidth(sx); + // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside + // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct + // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers + // do. + else if (!child->style()->isOriginalDisplayInlineType()) + // Avoid adding in the left border/padding of the containing block twice. Subtract it out. + offset.setWidth(sx - (child->containingBlock()->borderLeft() + child->containingBlock()->paddingLeft())); + + if (!child->style()->hasStaticY()) + offset.setHeight(sy); + + return offset; +} + +void RenderInline::imageChanged(WrappedImagePtr, const IntRect*) +{ + if (!parent()) + return; + + // FIXME: We can do better. + repaint(); +} + +void RenderInline::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +{ + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + graphicsContext->addFocusRingRect(IntRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())); + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText() && !curr->isListMarker()) { + FloatPoint pos(tx, ty); + // FIXME: This doesn't work correctly with transforms. + if (curr->hasLayer()) + pos = curr->localToAbsolute(); + else if (curr->isBox()) + pos.move(toRenderBox(curr)->x(), toRenderBox(curr)->y()); + curr->addFocusRingRects(graphicsContext, pos.x(), pos.y()); + } + } + + if (continuation()) { + if (continuation()->isInline()) + continuation()->addFocusRingRects(graphicsContext, + tx - containingBlock()->x() + continuation()->containingBlock()->x(), + ty - containingBlock()->y() + continuation()->containingBlock()->y()); + else + continuation()->addFocusRingRects(graphicsContext, + tx - containingBlock()->x() + toRenderBox(continuation())->x(), + ty - containingBlock()->y() + toRenderBox(continuation())->y()); + } +} + +void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty) +{ + if (!hasOutline()) + return; + + if (style()->outlineStyleIsAuto() || hasOutlineAnnotation()) { + int ow = style()->outlineWidth(); + Color oc = style()->outlineColor(); + if (!oc.isValid()) + oc = style()->color(); + + graphicsContext->initFocusRing(ow, style()->outlineOffset()); + addFocusRingRects(graphicsContext, tx, ty); + if (style()->outlineStyleIsAuto()) + graphicsContext->drawFocusRing(oc); + else + addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); + graphicsContext->clearFocusRing(); + } + + if (style()->outlineStyleIsAuto() || style()->outlineStyle() == BNONE) + return; + + Vector<IntRect> rects; + + rects.append(IntRect()); + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + rects.append(IntRect(curr->x(), curr->y(), curr->width(), curr->height())); + + rects.append(IntRect()); + + for (unsigned i = 1; i < rects.size() - 1; i++) + paintOutlineForLine(graphicsContext, tx, ty, rects.at(i - 1), rects.at(i), rects.at(i + 1)); +} + +void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, int tx, int ty, + const IntRect& lastline, const IntRect& thisline, const IntRect& nextline) +{ + int ow = style()->outlineWidth(); + EBorderStyle os = style()->outlineStyle(); + Color oc = style()->outlineColor(); + if (!oc.isValid()) + oc = style()->color(); + + int offset = style()->outlineOffset(); + + int t = ty + thisline.y() - offset; + int l = tx + thisline.x() - offset; + int b = ty + thisline.bottom() + offset; + int r = tx + thisline.right() + offset; + + // left edge + drawLineForBoxSide(graphicsContext, + l - ow, + t - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : 0), + l, + b + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : 0), + BSLeft, + oc, style()->color(), os, + (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : -ow), + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : -ow)); + + // right edge + drawLineForBoxSide(graphicsContext, + r, + t - (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : 0), + r + ow, + b + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : 0), + BSRight, + oc, style()->color(), os, + (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : -ow), + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : -ow)); + // upper edge + if (thisline.x() < lastline.x()) + drawLineForBoxSide(graphicsContext, + l - ow, + t - ow, + min(r+ow, (lastline.isEmpty() ? 1000000 : tx + lastline.x())), + t , + BSTop, oc, style()->color(), os, + ow, + (!lastline.isEmpty() && tx + lastline.x() + 1 < r + ow) ? -ow : ow); + + if (lastline.right() < thisline.right()) + drawLineForBoxSide(graphicsContext, + max(lastline.isEmpty() ? -1000000 : tx + lastline.right(), l - ow), + t - ow, + r + ow, + t , + BSTop, oc, style()->color(), os, + (!lastline.isEmpty() && l - ow < tx + lastline.right()) ? -ow : ow, + ow); + + // lower edge + if (thisline.x() < nextline.x()) + drawLineForBoxSide(graphicsContext, + l - ow, + b, + min(r + ow, !nextline.isEmpty() ? tx + nextline.x() + 1 : 1000000), + b + ow, + BSBottom, oc, style()->color(), os, + ow, + (!nextline.isEmpty() && tx + nextline.x() + 1 < r + ow) ? -ow : ow); + + if (nextline.right() < thisline.right()) + drawLineForBoxSide(graphicsContext, + max(!nextline.isEmpty() ? tx + nextline.right() : -1000000, l - ow), + b, + r + ow, + b + ow, + BSBottom, oc, style()->color(), os, + (!nextline.isEmpty() && l - ow < tx + nextline.right()) ? -ow : ow, + ow); +} + +#if ENABLE(DASHBOARD_SUPPORT) +void RenderInline::addDashboardRegions(Vector<DashboardRegionValue>& regions) +{ + // Convert the style regions to absolute coordinates. + if (style()->visibility() != VISIBLE) + return; + + const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions(); + unsigned i, count = styleRegions.size(); + for (i = 0; i < count; i++) { + StyleDashboardRegion styleRegion = styleRegions[i]; + + IntRect linesBoundingBox = this->linesBoundingBox(); + int w = linesBoundingBox.width(); + int h = linesBoundingBox.height(); + + DashboardRegionValue region; + region.label = styleRegion.label; + region.bounds = IntRect(linesBoundingBox.x() + styleRegion.offset.left().value(), + linesBoundingBox.y() + styleRegion.offset.top().value(), + w - styleRegion.offset.left().value() - styleRegion.offset.right().value(), + h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value()); + region.type = styleRegion.type; + + RenderObject* container = containingBlock(); + if (!container) + container = this; + + region.clip = region.bounds; + container->computeAbsoluteRepaintRect(region.clip); + if (region.clip.height() < 0) { + region.clip.setHeight(0); + region.clip.setWidth(0); + } + + FloatPoint absPos = container->localToAbsolute(); + region.bounds.setX(absPos.x() + region.bounds.x()); + region.bounds.setY(absPos.y() + region.bounds.y()); + + if (document()->frame()) { + float pageScaleFactor = document()->frame()->page()->chrome()->scaleFactor(); + if (pageScaleFactor != 1.0f) { + region.bounds.scale(pageScaleFactor); + region.clip.scale(pageScaleFactor); + } + } + + regions.append(region); + } +} +#endif + } // namespace WebCore diff --git a/WebCore/rendering/RenderInline.h b/WebCore/rendering/RenderInline.h index 83b8506..f3be72a 100644 --- a/WebCore/rendering/RenderInline.h +++ b/WebCore/rendering/RenderInline.h @@ -25,31 +25,39 @@ #ifndef RenderInline_h #define RenderInline_h -#include "RenderFlow.h" +#include "RenderBoxModelObject.h" +#include "RenderLineBoxList.h" namespace WebCore { class Position; -class RenderInline : public RenderFlow { +class RenderInline : public RenderBoxModelObject { public: RenderInline(Node*); virtual ~RenderInline(); + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + + virtual void destroy(); + virtual const char* renderName() const; virtual bool isRenderInline() const { return true; } - virtual bool childrenInline() const { return true; } - virtual bool isInlineContinuation() const; + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + void addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild); + virtual void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild = 0); - virtual void addChildToFlow(RenderObject* newChild, RenderObject* beforeChild); void splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock, - RenderObject* beforeChild, RenderFlow* oldCont); + RenderObject* beforeChild, RenderBoxModelObject* oldCont); void splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, - RenderObject* newChild, RenderFlow* oldCont); + RenderObject* newChild, RenderBoxModelObject* oldCont); - virtual void layout() { } // Do nothing for layout() + virtual void layout() { ASSERT_NOT_REACHED(); } // Do nothing for layout() virtual void paint(PaintInfo&, int tx, int ty); @@ -62,12 +70,20 @@ public: virtual int offsetWidth() const { return linesBoundingBox().width(); } virtual int offsetHeight() const { return linesBoundingBox().height(); } - void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); + // Just ignore top/bottom margins on RenderInlines. + virtual int marginTop() const { return 0; } + virtual int marginBottom() const { return 0; } + virtual int marginLeft() const; + virtual int marginRight() const; + + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); + virtual IntRect rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth); + virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed); - virtual VisiblePosition positionForCoordinates(int x, int y); + virtual VisiblePosition positionForPoint(const IntPoint&); IntRect linesBoundingBox() const; @@ -77,13 +93,79 @@ public: return IntRect(0, 0, boundingBox.width(), boundingBox.height()); } + InlineFlowBox* createInlineFlowBox(); + void dirtyLineBoxes(bool fullLayout); + virtual void dirtyLinesFromChangedChild(RenderObject* child) { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } + + RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } + const RenderLineBoxList* lineBoxes() const { return &m_lineBoxes; } + + InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); } + InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); } + + virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; + + RenderBoxModelObject* continuation() const { return m_continuation; } + RenderInline* inlineContinuation() const; + void setContinuation(RenderBoxModelObject* c) { m_continuation = c; } + + virtual void updateDragState(bool dragOn); + + virtual void childBecameNonInline(RenderObject* child); + + virtual void updateHitTestResult(HitTestResult&, const IntPoint&); + + IntSize relativePositionedInlineOffset(const RenderBox* child) const; + + virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + void paintOutline(GraphicsContext*, int tx, int ty); + + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + + int verticalPositionFromCache(bool firstLine) const; + void invalidateVerticalPosition() { m_verticalPosition = PositionUndefined; } + +#if ENABLE(DASHBOARD_SUPPORT) + virtual void addDashboardRegions(Vector<DashboardRegionValue>&); +#endif + protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + virtual void updateBoxModelInfoFromStyle(); + virtual InlineFlowBox* createFlowBox(); // Subclassed by SVG + + static RenderInline* cloneInline(RenderInline* src); - static RenderInline* cloneInline(RenderFlow* src); +private: + void paintOutlineForLine(GraphicsContext*, int tx, int ty, const IntRect& prevLine, const IntRect& thisLine, const IntRect& nextLine); + RenderBoxModelObject* continuationBefore(RenderObject* beforeChild); +protected: + RenderObjectChildList m_children; + RenderLineBoxList m_lineBoxes; // All of the line boxes created for this inline flow. For example, <i>Hello<br>world.</i> will have two <i> line boxes. + +private: + RenderBoxModelObject* m_continuation; // Can be either a block or an inline. <b><i><p>Hello</p></i></b>. In this example the <i> will have a block as its continuation but the + // <b> will just have an inline as its continuation. + mutable int m_lineHeight; + mutable int m_verticalPosition; }; +inline RenderInline* toRenderInline(RenderObject* o) +{ + ASSERT(!o || o->isRenderInline()); + return static_cast<RenderInline*>(o); +} + +inline const RenderInline* toRenderInline(const RenderObject* o) +{ + ASSERT(!o || o->isRenderInline()); + return static_cast<const RenderInline*>(o); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderInline(const RenderInline*); + } // namespace WebCore #endif // RenderInline_h diff --git a/WebCore/rendering/RenderLayer.cpp b/WebCore/rendering/RenderLayer.cpp index 1a538c6..fd88120 100644 --- a/WebCore/rendering/RenderLayer.cpp +++ b/WebCore/rendering/RenderLayer.cpp @@ -44,10 +44,13 @@ #include "config.h" #include "RenderLayer.h" +#include "CString.h" #include "CSSPropertyNames.h" +#include "CSSStyleSelector.h" #include "Document.h" #include "EventHandler.h" #include "EventNames.h" +#include "FloatPoint3D.h" #include "FloatRect.h" #include "FocusController.h" #include "Frame.h" @@ -73,8 +76,16 @@ #include "Scrollbar.h" #include "ScrollbarTheme.h" #include "SelectionController.h" +#include "TransformationMatrix.h" +#include "TransformState.h" #include "TranslateTransformOperation.h" #include <wtf/StdLibExtras.h> +#include <wtf/UnusedParam.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerBacking.h" +#include "RenderLayerCompositor.h" +#endif #if ENABLE(SVG) #include "SVGNames.h" @@ -88,12 +99,6 @@ namespace WebCore { using namespace HTMLNames; -const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterIfNeeded = { RenderLayer::noScroll, RenderLayer::alignCenter, RenderLayer::alignToClosestEdge }; -const RenderLayer::ScrollAlignment RenderLayer::gAlignToEdgeIfNeeded = { RenderLayer::noScroll, RenderLayer::alignToClosestEdge, RenderLayer::alignToClosestEdge }; -const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterAlways = { RenderLayer::alignCenter, RenderLayer::alignCenter, RenderLayer::alignCenter }; -const RenderLayer::ScrollAlignment RenderLayer::gAlignTopAlways = { RenderLayer::alignTop, RenderLayer::alignTop, RenderLayer::alignTop }; -const RenderLayer::ScrollAlignment RenderLayer::gAlignBottomAlways = { RenderLayer::alignBottom, RenderLayer::alignBottom, RenderLayer::alignBottom }; - const int MinimumWidthWhileResizing = 100; const int MinimumHeightWhileResizing = 40; @@ -116,7 +121,7 @@ void ClipRects::destroy(RenderArena* renderArena) renderArena->free(*(size_t *)this, this); } -RenderLayer::RenderLayer(RenderBox* renderer) +RenderLayer::RenderLayer(RenderBoxModelObject* renderer) : m_renderer(renderer) , m_parent(0) , m_previous(0) @@ -138,15 +143,15 @@ RenderLayer::RenderLayer(RenderBox* renderer) , m_inResizeMode(false) , m_posZOrderList(0) , m_negZOrderList(0) - , m_overflowList(0) + , m_normalFlowList(0) , m_clipRects(0) #ifndef NDEBUG , m_clipRectsRoot(0) #endif , m_scrollDimensionsDirty(true) , m_zOrderListsDirty(true) - , m_overflowListDirty(true) - , m_isOverflowOnly(shouldBeOverflowOnly()) + , m_normalFlowListDirty(true) + , m_isNormalFlowOnly(shouldBeNormalFlowOnly()) , m_usedTransparency(false) , m_paintingInsideReflection(false) , m_inOverflowRelayout(false) @@ -156,10 +161,15 @@ RenderLayer::RenderLayer(RenderBox* renderer) , m_hasVisibleContent(false) , m_visibleDescendantStatusDirty(false) , m_hasVisibleDescendant(false) + , m_3DTransformedDescendantStatusDirty(true) + , m_has3DTransformedDescendant(false) +#if USE(ACCELERATED_COMPOSITING) + , m_hasCompositingDescendant(false) + , m_mustOverlayCompositedLayers(false) +#endif , m_marquee(0) , m_staticX(0) , m_staticY(0) - , m_transform(0) , m_reflection(0) , m_scrollCorner(0) , m_resizer(0) @@ -185,9 +195,13 @@ RenderLayer::~RenderLayer() delete m_posZOrderList; delete m_negZOrderList; - delete m_overflowList; + delete m_normalFlowList; delete m_marquee; +#if USE(ACCELERATED_COMPOSITING) + clearBacking(); +#endif + // Make sure we have no lingering clip rects. ASSERT(!m_clipRects); @@ -204,11 +218,40 @@ RenderLayer::~RenderLayer() m_resizer->destroy(); } +#if USE(ACCELERATED_COMPOSITING) +RenderLayerCompositor* RenderLayer::compositor() const +{ + ASSERT(renderer()->view()); + return renderer()->view()->compositor(); +} + +void RenderLayer::rendererContentChanged() +{ + if (m_backing) + m_backing->rendererContentChanged(); +} +#endif // USE(ACCELERATED_COMPOSITING) + +void RenderLayer::setStaticY(int staticY) +{ + if (m_staticY == staticY) + return; + m_staticY = staticY; + renderer()->setChildNeedsLayout(true, false); +} + void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) { if (doFullRepaint) { renderer()->repaint(); +#if USE(ACCELERATED_COMPOSITING) + checkForRepaint = false; + // We need the full repaint to propagate to child layers if we are hardware compositing. + if (!compositor()->inCompositingMode()) + doFullRepaint = false; +#else checkForRepaint = doFullRepaint = false; +#endif } updateLayerPosition(); // For relpositioned layers or non-positioned layers, @@ -231,16 +274,17 @@ void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) // from updateScrollInfoAfterLayout(). ASSERT(!view->layoutStateEnabled()); - IntRect newRect = renderer()->absoluteClippedOverflowRect(); - IntRect newOutlineBox = renderer()->absoluteOutlineBounds(); + RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); + IntRect newRect = renderer()->clippedOverflowRectForRepaint(repaintContainer); + IntRect newOutlineBox = renderer()->outlineBoundsForRepaint(repaintContainer); if (checkForRepaint) { if (view && !view->printing()) { if (m_needsFullRepaint) { - view->repaintViewRectangle(m_repaintRect); + renderer()->repaintUsingContainer(repaintContainer, m_repaintRect); if (newRect != m_repaintRect) - view->repaintViewRectangle(newRect); + renderer()->repaintUsingContainer(repaintContainer, newRect); } else - renderer()->repaintAfterLayoutIfNeeded(m_repaintRect, m_outlineBox); + renderer()->repaintAfterLayoutIfNeeded(repaintContainer, m_repaintRect, m_outlineBox); } } m_repaintRect = newRect; @@ -258,6 +302,14 @@ void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) child->updateLayerPositions(doFullRepaint, checkForRepaint); + +#if USE(ACCELERATED_COMPOSITING) + if (!parent()) + compositor()->updateRootLayerPosition(); + + if (isComposited()) + backing()->updateAfterLayout(); +#endif // With all our children positioned, now update our marquee if we need to. if (m_marquee) @@ -266,7 +318,11 @@ void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) void RenderLayer::updateTransform() { - bool hasTransform = renderer()->hasTransform(); + // hasTransform() on the renderer is also true when there is transform-style: preserve-3d or perspective set, + // so check style too. + bool hasTransform = renderer()->hasTransform() && renderer()->style()->hasTransform(); + bool had3DTransform = has3DTransform(); + bool hadTransform = m_transform; if (hasTransform != hadTransform) { if (hasTransform) @@ -276,9 +332,33 @@ void RenderLayer::updateTransform() } if (hasTransform) { - m_transform->reset(); - renderer()->style()->applyTransform(*m_transform, renderer()->borderBoxRect().size()); + RenderBox* box = renderBox(); + ASSERT(box); + m_transform->makeIdentity(); + box->style()->applyTransform(*m_transform, box->borderBoxRect().size(), RenderStyle::IncludeTransformOrigin); + makeMatrixRenderable(*m_transform); } + + if (had3DTransform != has3DTransform()) + dirty3DTransformedDescendantStatus(); +} + +TransformationMatrix RenderLayer::currentTransform() const +{ + if (!m_transform) + return TransformationMatrix(); + +#if USE(ACCELERATED_COMPOSITING) + if (renderer()->style()->isRunningAcceleratedAnimation()) { + TransformationMatrix currTransform; + RefPtr<RenderStyle> style = renderer()->animation()->getAnimatedStyleForRenderer(renderer()); + style->applyTransform(currTransform, renderBox()->borderBoxRect().size(), RenderStyle::IncludeTransformOrigin); + makeMatrixRenderable(currTransform); + return currTransform; + } +#endif + + return *m_transform; } void RenderLayer::setHasVisibleContent(bool b) @@ -288,9 +368,10 @@ void RenderLayer::setHasVisibleContent(bool b) m_visibleContentStatusDirty = false; m_hasVisibleContent = b; if (m_hasVisibleContent) { - m_repaintRect = renderer()->absoluteClippedOverflowRect(); - m_outlineBox = renderer()->absoluteOutlineBounds(); - if (!isOverflowOnly()) + RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); + m_repaintRect = renderer()->clippedOverflowRectForRepaint(repaintContainer); + m_outlineBox = renderer()->outlineBoundsForRepaint(repaintContainer); + if (!isNormalFlowOnly()) dirtyStackingContextZOrderLists(); } if (parent()) @@ -372,38 +453,84 @@ void RenderLayer::updateVisibilityStatus() } } +void RenderLayer::dirty3DTransformedDescendantStatus() +{ + RenderLayer* curr = stackingContext(); + if (curr) + curr->m_3DTransformedDescendantStatusDirty = true; + + // This propagates up through preserve-3d hierarchies to the enclosing flattening layer. + // Note that preserves3D() creates stacking context, so we can just run up the stacking contexts. + while (curr && curr->preserves3D()) { + curr->m_3DTransformedDescendantStatusDirty = true; + curr = curr->stackingContext(); + } +} + +// Return true if this layer or any preserve-3d descendants have 3d. +bool RenderLayer::update3DTransformedDescendantStatus() +{ + if (m_3DTransformedDescendantStatusDirty) { + m_has3DTransformedDescendant = false; + + // Transformed or preserve-3d descendants can only be in the z-order lists, not + // in the normal flow list, so we only need to check those. + if (m_posZOrderList) { + for (unsigned i = 0; i < m_posZOrderList->size(); ++i) + m_has3DTransformedDescendant |= m_posZOrderList->at(i)->update3DTransformedDescendantStatus(); + } + + // Now check our negative z-index children. + if (m_negZOrderList) { + for (unsigned i = 0; i < m_negZOrderList->size(); ++i) + m_has3DTransformedDescendant |= m_negZOrderList->at(i)->update3DTransformedDescendantStatus(); + } + } + + // If we live in a 3d hierarchy, then the layer at the root of that hierarchy needs + // the m_has3DTransformedDescendant set. + if (preserves3D()) + return has3DTransform() || m_has3DTransformedDescendant; + + return has3DTransform(); +} + void RenderLayer::updateLayerPosition() { // Clear our cached clip rect information. clearClipRects(); - int x = renderer()->x(); - int y = renderer()->y(); + RenderBox* rendererBox = renderBox(); + + int x = rendererBox ? rendererBox->x() : 0; + int y = rendererBox ? rendererBox->y() : 0; if (!renderer()->isPositioned() && renderer()->parent()) { // We must adjust our position by walking up the render tree looking for the // nearest enclosing object with a layer. - RenderBox* curr = renderer()->parentBox(); + RenderObject* curr = renderer()->parent(); while (curr && !curr->hasLayer()) { - if (!curr->isTableRow()) { + if (curr->isBox() && !curr->isTableRow()) { // Rows and cells share the same coordinate space (that of the section). // Omit them when computing our xpos/ypos. - x += curr->x(); - y += curr->y(); + RenderBox* currBox = toRenderBox(curr); + x += currBox->x(); + y += currBox->y(); } - curr = curr->parentBox(); + curr = curr->parent(); } - if (curr->isTableRow()) { + if (curr->isBox() && curr->isTableRow()) { // Put ourselves into the row coordinate space. - x -= curr->x(); - y -= curr->y(); + RenderBox* currBox = toRenderBox(curr); + x -= currBox->x(); + y -= currBox->y(); } } m_relX = m_relY = 0; if (renderer()->isRelPositioned()) { - m_relX = toRenderBox(renderer())->relativePositionOffsetX(); - m_relY = toRenderBox(renderer())->relativePositionOffsetY(); + m_relX = renderer()->relativePositionOffsetX(); + m_relY = renderer()->relativePositionOffsetY(); x += m_relX; y += m_relY; } @@ -414,8 +541,8 @@ void RenderLayer::updateLayerPosition() // For positioned layers, we subtract out the enclosing positioned layer's scroll offset. positionedParent->subtractScrolledContentOffset(x, y); - if (renderer()->isPositioned()) { - IntSize offset = toRenderBox(renderer())->offsetForPositionedInContainer(positionedParent->renderer()); + if (renderer()->isPositioned() && positionedParent->renderer()->isRelPositioned() && positionedParent->renderer()->isRenderInline()) { + IntSize offset = toRenderInline(positionedParent->renderer())->relativePositionedInlineOffset(toRenderBox(renderer())); x += offset.width(); y += offset.height(); } @@ -424,33 +551,74 @@ void RenderLayer::updateLayerPosition() // FIXME: We'd really like to just get rid of the concept of a layer rectangle and rely on the renderers. - setPos(x, y); + setLocation(x, y); if (renderer()->isRenderInline()) { - RenderInline* inlineFlow = static_cast<RenderInline*>(renderer()); + RenderInline* inlineFlow = toRenderInline(renderer()); IntRect lineBox = inlineFlow->linesBoundingBox(); setWidth(lineBox.width()); setHeight(lineBox.height()); - } else { - setWidth(renderer()->width()); - setHeight(renderer()->height()); - - if (!renderer()->hasOverflowClip()) { - if (renderer()->overflowWidth() > renderer()->width()) - setWidth(renderer()->overflowWidth()); - if (renderer()->overflowHeight() > renderer()->height()) - setHeight(renderer()->overflowHeight()); + } else if (RenderBox* box = renderBox()) { + setWidth(box->width()); + setHeight(box->height()); + + if (!box->hasOverflowClip()) { + if (box->overflowWidth() > box->width()) + setWidth(box->overflowWidth()); + if (box->overflowHeight() > box->height()) + setHeight(box->overflowHeight()); } } } -RenderLayer *RenderLayer::stackingContext() const +TransformationMatrix RenderLayer::perspectiveTransform() const { - RenderLayer* curr = parent(); - for ( ; curr && !curr->renderer()->isRenderView() && !curr->renderer()->isRoot() && - curr->renderer()->style()->hasAutoZIndex(); - curr = curr->parent()) { } - return curr; + if (!renderer()->hasTransform()) + return TransformationMatrix(); + + RenderStyle* style = renderer()->style(); + if (!style->hasPerspective()) + return TransformationMatrix(); + + // Maybe fetch the perspective from the backing? + const IntRect borderBox = toRenderBox(renderer())->borderBoxRect(); + const float boxWidth = borderBox.width(); + const float boxHeight = borderBox.height(); + + float perspectiveOriginX = style->perspectiveOriginX().calcFloatValue(boxWidth); + float perspectiveOriginY = style->perspectiveOriginY().calcFloatValue(boxHeight); + + // A perspective origin of 0,0 makes the vanishing point in the center of the element. + // We want it to be in the top-left, so subtract half the height and width. + perspectiveOriginX -= boxWidth / 2.0f; + perspectiveOriginY -= boxHeight / 2.0f; + + TransformationMatrix t; + t.translate(perspectiveOriginX, perspectiveOriginY); + t.applyPerspective(style->perspective()); + t.translate(-perspectiveOriginX, -perspectiveOriginY); + + return t; +} + +FloatPoint RenderLayer::perspectiveOrigin() const +{ + if (!renderer()->hasTransform()) + return FloatPoint(); + + const IntRect borderBox = toRenderBox(renderer())->borderBoxRect(); + RenderStyle* style = renderer()->style(); + + return FloatPoint(style->perspectiveOriginX().calcFloatValue(borderBox.width()), + style->perspectiveOriginY().calcFloatValue(borderBox.height())); +} + +RenderLayer* RenderLayer::stackingContext() const +{ + RenderLayer* layer = parent(); + while (layer && !layer->renderer()->isRenderView() && !layer->renderer()->isRoot() && layer->renderer()->style()->hasAutoZIndex()) + layer = layer->parent(); + return layer; } RenderLayer* RenderLayer::enclosingPositionedAncestor() const @@ -469,6 +637,27 @@ RenderLayer* RenderLayer::enclosingTransformedAncestor() const return curr; } +#if USE(ACCELERATED_COMPOSITING) +RenderLayer* RenderLayer::enclosingCompositingLayer(bool includeSelf) const +{ + if (includeSelf && isComposited()) + return const_cast<RenderLayer*>(this); + + // Compositing layers are parented according to stacking order and overflow list, + // so we have to check whether the parent is a stacking context, or whether + // the child is overflow-only. + bool inNormalFlowList = isNormalFlowOnly(); + for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { + if (curr->isComposited() && (inNormalFlowList || curr->isStackingContext())) + return curr; + + inNormalFlowList = curr->isNormalFlowOnly(); + } + + return 0; +} +#endif + IntPoint RenderLayer::absoluteToContents(const IntPoint& absolutePoint) const { // We don't use convertToLayerCoords because it doesn't know about transforms @@ -487,18 +676,24 @@ bool RenderLayer::requiresSlowRepaints() const bool RenderLayer::isTransparent() const { #if ENABLE(SVG) - if (renderer()->node()->namespaceURI() == SVGNames::svgNamespaceURI) + if (renderer()->node() && renderer()->node()->namespaceURI() == SVGNames::svgNamespaceURI) return false; #endif return renderer()->isTransparent() || renderer()->hasMask(); } -RenderLayer* -RenderLayer::transparentAncestor() +RenderLayer* RenderLayer::transparentPaintingAncestor() { - RenderLayer* curr = parent(); - for ( ; curr && !curr->isTransparent(); curr = curr->parent()) { } - return curr; + if (isComposited()) + return 0; + + for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { + if (curr->isComposited()) + return 0; + if (curr->isTransparent()) + return curr; + } + return 0; } static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransform, const RenderLayer* l, const RenderLayer* rootLayer) @@ -507,16 +702,16 @@ static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransfor // paintDirtyRect, and that should cut down on the amount we have to paint. Still it // would be better to respect clips. - TransformationMatrix* t = l->transform(); - if (t && rootLayer != l) { + if (rootLayer != l && l->paintsWithTransform()) { // The best we can do here is to use enclosed bounding boxes to establish a "fuzzy" enough clip to encompass // the transformed layer and all of its children. int x = 0; int y = 0; l->convertToLayerCoords(rootLayer, x, y); + TransformationMatrix transform; transform.translate(x, y); - transform = *t * transform; + transform = *l->transform() * transform; transform = transform * enclosingTransform; // We now have a transform that will produce a rectangle in our view's space. @@ -550,14 +745,14 @@ static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransfor void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const RenderLayer* rootLayer) { - if (p->paintingDisabled() || (isTransparent() && m_usedTransparency)) + if (p->paintingDisabled() || (paintsWithTransparency() && m_usedTransparency)) return; - RenderLayer* ancestor = transparentAncestor(); + RenderLayer* ancestor = transparentPaintingAncestor(); if (ancestor) ancestor->beginTransparencyLayers(p, rootLayer); - if (isTransparent()) { + if (paintsWithTransparency()) { m_usedTransparency = true; p->save(); p->clip(transparencyClipBox(TransformationMatrix(), this, rootLayer)); @@ -577,7 +772,7 @@ void RenderLayer::operator delete(void* ptr, size_t sz) } void RenderLayer::destroy(RenderArena* renderArena) -{ +{ delete this; // Recover the size left there for us by operator delete and free the memory. @@ -601,10 +796,10 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) child->setParent(this); - if (child->isOverflowOnly()) - dirtyOverflowList(); + if (child->isNormalFlowOnly()) + dirtyNormalFlowList(); - if (!child->isOverflowOnly() || child->firstChild()) { + if (!child->isNormalFlowOnly() || child->firstChild()) { // Dirty the z-order list in which we are contained. The stackingContext() can be null in the // case where we're building up generated content layers. This is ok, since the lists will start // off dirty in that case anyway. @@ -614,10 +809,19 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) child->updateVisibilityStatus(); if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) childVisibilityChanged(true); + +#if USE(ACCELERATED_COMPOSITING) + compositor()->layerWasAdded(this, child); +#endif } RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) { +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed()) + compositor()->layerWillBeRemoved(this, oldChild); +#endif + // remove the child if (oldChild->previousSibling()) oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); @@ -629,9 +833,9 @@ RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) if (m_last == oldChild) m_last = oldChild->previousSibling(); - if (oldChild->isOverflowOnly()) - dirtyOverflowList(); - if (!oldChild->isOverflowOnly() || oldChild->firstChild()) { + if (oldChild->isNormalFlowOnly()) + dirtyNormalFlowList(); + if (!oldChild->isNormalFlowOnly() || oldChild->firstChild()) { // Dirty the z-order list in which we are contained. When called via the // reattachment process in removeOnlyThisLayer, the layer may already be disconnected // from the main layer tree, so we need to null-check the |stackingContext| value. @@ -654,6 +858,10 @@ void RenderLayer::removeOnlyThisLayer() if (!m_parent) return; +#if USE(ACCELERATED_COMPOSITING) + compositor()->layerWillBeRemoved(m_parent, this); +#endif + // Dirty the clip rects. clearClipRectsIncludingDescendants(); @@ -674,8 +882,8 @@ void RenderLayer::removeOnlyThisLayer() current->updateLayerPositions(); current = next; } - - destroy(renderer()->renderArena()); + + m_renderer->destroyLayer(); } void RenderLayer::insertOnlyThisLayer() @@ -684,21 +892,21 @@ void RenderLayer::insertOnlyThisLayer() // We need to connect ourselves when our renderer() has a parent. // Find our enclosingLayer and add ourselves. RenderLayer* parentLayer = renderer()->parent()->enclosingLayer(); + ASSERT(parentLayer); RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer()->parent()->findNextLayer(parentLayer, renderer()) : 0; - if (parentLayer) - parentLayer->addChild(this, beforeChild); + parentLayer->addChild(this, beforeChild); } - + // Remove all descendant layers from the hierarchy and add them to the new position. for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling()) curr->moveLayers(m_parent, this); - + // Clear out all the clip rects. clearClipRectsIncludingDescendants(); } void -RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const +RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& xPos, int& yPos) const { if (ancestorLayer == this) return; @@ -707,8 +915,8 @@ RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& // Add in the offset of the view. We can obtain this by calling // localToAbsolute() on the RenderView. FloatPoint absPos = renderer()->localToAbsolute(FloatPoint(), true); - x += absPos.x(); - y += absPos.y(); + xPos += absPos.x(); + yPos += absPos.y(); return; } @@ -720,10 +928,10 @@ RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& if (!parentLayer) return; - parentLayer->convertToLayerCoords(ancestorLayer, x, y); + parentLayer->convertToLayerCoords(ancestorLayer, xPos, yPos); - x += xPos(); - y += yPos(); + xPos += x(); + yPos += y(); } void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint) @@ -804,14 +1012,18 @@ RenderLayer::subtractScrolledContentOffset(int& x, int& y) const void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint) { - if (renderer()->style()->overflowX() != OMARQUEE) { + RenderBox* box = renderBox(); + if (!box) + return; + + if (box->style()->overflowX() != OMARQUEE) { if (x < 0) x = 0; if (y < 0) y = 0; // Call the scrollWidth/Height functions so that the dimensions will be computed if they need // to be (for overflow:hidden blocks). - int maxX = scrollWidth() - renderer()->clientWidth(); - int maxY = scrollHeight() - renderer()->clientHeight(); + int maxX = scrollWidth() - box->clientWidth(); + int maxY = scrollHeight() - box->clientHeight(); if (x > maxX) x = maxX; if (y > maxY) y = maxY; @@ -831,6 +1043,11 @@ void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repai // Update the positions of our child layers. for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) child->updateLayerPositions(false, false); + +#if USE(ACCELERATED_COMPOSITING) + if (isComposited()) + m_backing->updateGraphicsLayerGeometry(); +#endif RenderView* view = renderer()->view(); @@ -865,7 +1082,7 @@ void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repai // Schedule the scroll DOM event. if (view) { if (FrameView* frameView = view->frameView()) - frameView->scheduleEvent(Event::create(eventNames().scrollEvent, false, false), EventTargetNodeCast(renderer()->element())); + frameView->scheduleEvent(Event::create(eventNames().scrollEvent, false, false), renderer()->node()); } } @@ -890,10 +1107,12 @@ void RenderLayer::scrollRectToVisible(const IntRect &rect, bool scrollToAnchor, if (renderer()->hasOverflowClip() && !restrictedByLineClamp) { // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property. // This will prevent us from revealing text hidden by the slider in Safari RSS. - FloatPoint absPos = renderer()->localToAbsolute(); - absPos.move(renderer()->borderLeft(), renderer()->borderTop()); + RenderBox* box = renderBox(); + ASSERT(box); + FloatPoint absPos = box->localToAbsolute(); + absPos.move(box->borderLeft(), box->borderTop()); - IntRect layerBounds = IntRect(absPos.x() + scrollXOffset(), absPos.y() + scrollYOffset(), renderer()->clientWidth(), renderer()->clientHeight()); + IntRect layerBounds = IntRect(absPos.x() + scrollXOffset(), absPos.y() + scrollYOffset(), box->clientWidth(), box->clientHeight()); IntRect exposeRect = IntRect(rect.x() + scrollXOffset(), rect.y() + scrollYOffset(), rect.width(), rect.height()); IntRect r = getRectToExpose(layerBounds, exposeRect, alignX, alignY); @@ -912,7 +1131,7 @@ void RenderLayer::scrollRectToVisible(const IntRect &rect, bool scrollToAnchor, newRect.setX(rect.x() - diffX); newRect.setY(rect.y() - diffY); } - } else if (!parentLayer && renderer()->canBeProgramaticallyScrolled(scrollToAnchor)) { + } else if (!parentLayer && renderer()->isBox() && renderBox()->canBeProgramaticallyScrolled(scrollToAnchor)) { if (frameView) { if (renderer()->document() && renderer()->document()->ownerElement() && renderer()->document()->ownerElement()->renderer()) { IntRect viewRect = frameView->visibleContentRect(); @@ -956,17 +1175,17 @@ IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect & // If the rectangle is fully visible, use the specified visible behavior. // If the rectangle is partially visible, but over a certain threshold, // then treat it as fully visible to avoid unnecessary horizontal scrolling - scrollX = getVisibleBehavior(alignX); + scrollX = ScrollAlignment::getVisibleBehavior(alignX); else if (intersectWidth == visibleRect.width()) { // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. - scrollX = getVisibleBehavior(alignX); + scrollX = ScrollAlignment::getVisibleBehavior(alignX); if (scrollX == alignCenter) scrollX = noScroll; } else if (intersectWidth > 0) // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior - scrollX = getPartialBehavior(alignX); + scrollX = ScrollAlignment::getPartialBehavior(alignX); else - scrollX = getHiddenBehavior(alignX); + scrollX = ScrollAlignment::getHiddenBehavior(alignX); // If we're trying to align to the closest edge, and the exposeRect is further right // than the visibleRect, and not bigger than the visible area, then align with the right. if (scrollX == alignToClosestEdge && exposeRect.right() > visibleRect.right() && exposeRect.width() < visibleRect.width()) @@ -989,17 +1208,17 @@ IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect & int intersectHeight = intersection(visibleRect, exposeRectY).height(); if (intersectHeight == exposeRect.height()) // If the rectangle is fully visible, use the specified visible behavior. - scrollY = getVisibleBehavior(alignY); + scrollY = ScrollAlignment::getVisibleBehavior(alignY); else if (intersectHeight == visibleRect.height()) { // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. - scrollY = getVisibleBehavior(alignY); + scrollY = ScrollAlignment::getVisibleBehavior(alignY); if (scrollY == alignCenter) scrollY = noScroll; } else if (intersectHeight > 0) // If the rectangle is partially visible, use the specified partial behavior - scrollY = getPartialBehavior(alignY); + scrollY = ScrollAlignment::getPartialBehavior(alignY); else - scrollY = getHiddenBehavior(alignY); + scrollY = ScrollAlignment::getHiddenBehavior(alignY); // If we're trying to align to the closest edge, and the exposeRect is further down // than the visibleRect, and not bigger than the visible area, then align with the bottom. if (scrollY == alignToClosestEdge && exposeRect.bottom() > visibleRect.bottom() && exposeRect.height() < visibleRect.height()) @@ -1032,12 +1251,13 @@ void RenderLayer::autoscroll() frame->eventHandler()->updateSelectionForMouseDrag(); IntPoint currentDocumentPosition = frameView->windowToContents(frame->eventHandler()->currentMousePosition()); - scrollRectToVisible(IntRect(currentDocumentPosition, IntSize(1, 1)), false, gAlignToEdgeIfNeeded, gAlignToEdgeIfNeeded); + scrollRectToVisible(IntRect(currentDocumentPosition, IntSize(1, 1)), false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); } void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset) { - if (!inResizeMode() || !renderer()->hasOverflowClip()) + // FIXME: This should be possible on generated content but is not right now. + if (!inResizeMode() || !renderer()->hasOverflowClip() || !renderer()->node()) return; // Set the width and height of the shadow ancestor node if there is one. @@ -1073,7 +1293,7 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset ExceptionCode ec; if (difference.width()) { - if (element && element->isControl()) { + if (element->isFormControlElement()) { // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). style->setProperty(CSSPropertyMarginLeft, String::number(renderer->marginLeft() / zoomFactor) + "px", false, ec); style->setProperty(CSSPropertyMarginRight, String::number(renderer->marginRight() / zoomFactor) + "px", false, ec); @@ -1085,7 +1305,7 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset } if (difference.height()) { - if (element && element->isControl()) { + if (element->isFormControlElement()) { // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). style->setProperty(CSSPropertyMarginTop, String::number(renderer->marginTop() / zoomFactor) + "px", false, ec); style->setProperty(CSSPropertyMarginBottom, String::number(renderer->marginBottom() / zoomFactor) + "px", false, ec); @@ -1172,6 +1392,7 @@ static IntRect scrollCornerRect(const RenderLayer* layer, const IntRect& bounds) static IntRect resizerCornerRect(const RenderLayer* layer, const IntRect& bounds) { + ASSERT(layer->renderer()->isBox()); if (layer->renderer()->style()->resize() == RESIZE_NONE) return IntRect(); return cornerRect(layer, bounds); @@ -1179,25 +1400,29 @@ static IntRect resizerCornerRect(const RenderLayer* layer, const IntRect& bounds bool RenderLayer::scrollbarCornerPresent() const { - return !scrollCornerRect(this, renderer()->borderBoxRect()).isEmpty(); + ASSERT(renderer()->isBox()); + return !scrollCornerRect(this, renderBox()->borderBoxRect()).isEmpty(); } void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) { IntRect scrollRect = rect; + RenderBox* box = renderBox(); + ASSERT(box); if (scrollbar == m_vBar.get()) - scrollRect.move(renderer()->width() - renderer()->borderRight() - scrollbar->width(), renderer()->borderTop()); + scrollRect.move(box->width() - box->borderRight() - scrollbar->width(), box->borderTop()); else - scrollRect.move(renderer()->borderLeft(), renderer()->height() - renderer()->borderBottom() - scrollbar->height()); + scrollRect.move(box->borderLeft(), box->height() - box->borderBottom() - scrollbar->height()); renderer()->repaintRectangle(scrollRect); } PassRefPtr<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation) { RefPtr<Scrollbar> widget; - bool hasCustomScrollbarStyle = renderer()->node()->shadowAncestorNode()->renderer()->style()->hasPseudoStyle(RenderStyle::SCROLLBAR); + RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + bool hasCustomScrollbarStyle = actualRenderer->isBox() && actualRenderer->style()->hasPseudoStyle(SCROLLBAR); if (hasCustomScrollbarStyle) - widget = RenderScrollbar::createCustomScrollbar(this, orientation, renderer()->node()->shadowAncestorNode()->renderBox()); + widget = RenderScrollbar::createCustomScrollbar(this, orientation, toRenderBox(actualRenderer)); else widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); renderer()->document()->view()->addChild(widget.get()); @@ -1287,19 +1512,23 @@ void RenderLayer::positionOverflowControls(int tx, int ty) if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) return; - IntRect borderBox = renderer()->borderBoxRect(); + RenderBox* box = renderBox(); + if (!box) + return; + + IntRect borderBox = box->borderBoxRect(); IntRect scrollCorner(scrollCornerRect(this, borderBox)); IntRect absBounds(borderBox.x() + tx, borderBox.y() + ty, borderBox.width(), borderBox.height()); if (m_vBar) - m_vBar->setFrameRect(IntRect(absBounds.right() - renderer()->borderRight() - m_vBar->width(), - absBounds.y() + renderer()->borderTop(), + m_vBar->setFrameRect(IntRect(absBounds.right() - box->borderRight() - m_vBar->width(), + absBounds.y() + box->borderTop(), m_vBar->width(), - absBounds.height() - (renderer()->borderTop() + renderer()->borderBottom()) - scrollCorner.height())); + absBounds.height() - (box->borderTop() + box->borderBottom()) - scrollCorner.height())); if (m_hBar) - m_hBar->setFrameRect(IntRect(absBounds.x() + renderer()->borderLeft(), - absBounds.bottom() - renderer()->borderBottom() - m_hBar->height(), - absBounds.width() - (renderer()->borderLeft() + renderer()->borderRight()) - scrollCorner.width(), + m_hBar->setFrameRect(IntRect(absBounds.x() + box->borderLeft(), + absBounds.bottom() - box->borderBottom() - m_hBar->height(), + absBounds.width() - (box->borderLeft() + box->borderRight()) - scrollCorner.width(), m_hBar->height())); if (m_scrollCorner) @@ -1324,19 +1553,22 @@ int RenderLayer::scrollHeight() void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar) { + RenderBox* box = renderBox(); + ASSERT(box); + m_scrollDimensionsDirty = false; bool ltr = renderer()->style()->direction() == LTR; - int clientWidth = renderer()->clientWidth(); - int clientHeight = renderer()->clientHeight(); + int clientWidth = box->clientWidth(); + int clientHeight = box->clientHeight(); - m_scrollLeftOverflow = ltr ? 0 : min(0, renderer()->leftmostPosition(true, false) - renderer()->borderLeft()); + m_scrollLeftOverflow = ltr ? 0 : min(0, box->leftmostPosition(true, false) - box->borderLeft()); int rightPos = ltr ? - renderer()->rightmostPosition(true, false) - renderer()->borderLeft() : + box->rightmostPosition(true, false) - box->borderLeft() : clientWidth - m_scrollLeftOverflow; - int bottomPos = renderer()->lowestPosition(true, false) - renderer()->borderTop(); + int bottomPos = box->lowestPosition(true, false) - box->borderTop(); m_scrollWidth = max(rightPos, clientWidth); m_scrollHeight = max(bottomPos, clientHeight); @@ -1368,7 +1600,7 @@ void RenderLayer::updateOverflowStatus(bool horizontalOverflow, bool verticalOve if (FrameView* frameView = renderer()->document()->view()) { frameView->scheduleEvent(OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow), - EventTargetNodeCast(renderer()->element())); + renderer()->node()); } } } @@ -1376,16 +1608,20 @@ void RenderLayer::updateOverflowStatus(bool horizontalOverflow, bool verticalOve void RenderLayer::updateScrollInfoAfterLayout() { + RenderBox* box = renderBox(); + if (!box) + return; + m_scrollDimensionsDirty = true; bool horizontalOverflow, verticalOverflow; computeScrollDimensions(&horizontalOverflow, &verticalOverflow); - if (renderer()->style()->overflowX() != OMARQUEE) { + if (box->style()->overflowX() != OMARQUEE) { // Layout may cause us to be in an invalid scroll position. In this case we need // to pull our scroll offsets back to the max (or push them up to the min). - int newX = max(0, min(scrollXOffset(), scrollWidth() - renderer()->clientWidth())); - int newY = max(0, min(m_scrollY, scrollHeight() - renderer()->clientHeight())); + int newX = max(0, min(scrollXOffset(), scrollWidth() - box->clientWidth())); + int newY = max(0, min(m_scrollY, scrollHeight() - box->clientHeight())); if (newX != scrollXOffset() || newY != m_scrollY) { RenderView* view = renderer()->view(); ASSERT(view); @@ -1417,12 +1653,12 @@ RenderLayer::updateScrollInfoAfterLayout() setHasVerticalScrollbar(false); // overflow:auto may need to lay out again if scrollbars got added/removed. - bool scrollbarsChanged = (renderer()->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) || - (renderer()->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); + bool scrollbarsChanged = (box->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) || + (box->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); if (scrollbarsChanged) { - if (renderer()->hasAutoHorizontalScrollbar()) + if (box->hasAutoHorizontalScrollbar()) setHasHorizontalScrollbar(horizontalOverflow); - if (renderer()->hasAutoVerticalScrollbar()) + if (box->hasAutoVerticalScrollbar()) setHasVerticalScrollbar(verticalOverflow); #if ENABLE(DASHBOARD_SUPPORT) @@ -1439,7 +1675,7 @@ RenderLayer::updateScrollInfoAfterLayout() m_inOverflowRelayout = true; renderer()->setNeedsLayout(true, false); if (renderer()->isRenderBlock()) - static_cast<RenderBlock*>(renderer())->layoutBlock(true); + toRenderBlock(renderer())->layoutBlock(true); else renderer()->layout(); m_inOverflowRelayout = false; @@ -1448,14 +1684,14 @@ RenderLayer::updateScrollInfoAfterLayout() } // If overflow:scroll is turned into overflow:auto a bar might still be disabled (Bug 11985). - if (m_hBar && renderer()->hasAutoHorizontalScrollbar()) + if (m_hBar && box->hasAutoHorizontalScrollbar()) m_hBar->setEnabled(true); - if (m_vBar && renderer()->hasAutoVerticalScrollbar()) + if (m_vBar && box->hasAutoVerticalScrollbar()) m_vBar->setEnabled(true); // Set up the range (and page step/line step). if (m_hBar) { - int clientWidth = renderer()->clientWidth(); + int clientWidth = box->clientWidth(); int pageStep = (clientWidth - cAmountToKeepWhenPaging); if (pageStep < 0) pageStep = clientWidth; m_hBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); @@ -1463,14 +1699,14 @@ RenderLayer::updateScrollInfoAfterLayout() m_hBar->setValue(scrollXOffset()); } if (m_vBar) { - int clientHeight = renderer()->clientHeight(); + int clientHeight = box->clientHeight(); int pageStep = (clientHeight - cAmountToKeepWhenPaging); if (pageStep < 0) pageStep = clientHeight; m_vBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); m_vBar->setProportion(clientHeight, m_scrollHeight); } - if (renderer()->element() && renderer()->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) + if (renderer()->node() && renderer()->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) updateOverflowStatus(horizontalOverflow, verticalOverflow); } @@ -1501,7 +1737,10 @@ void RenderLayer::paintOverflowControls(GraphicsContext* context, int tx, int ty void RenderLayer::paintScrollCorner(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) { - IntRect cornerRect = scrollCornerRect(this, renderer()->borderBoxRect()); + RenderBox* box = renderBox(); + ASSERT(box); + + IntRect cornerRect = scrollCornerRect(this, box->borderBoxRect()); IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height()); if (!absRect.intersects(damageRect)) return; @@ -1524,7 +1763,10 @@ void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const I if (renderer()->style()->resize() == RESIZE_NONE) return; - IntRect cornerRect = resizerCornerRect(this, renderer()->borderBoxRect()); + RenderBox* box = renderBox(); + ASSERT(box); + + IntRect cornerRect = resizerCornerRect(this, box->borderBoxRect()); IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height()); if (!absRect.intersects(damageRect)) return; @@ -1548,6 +1790,7 @@ void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const I // Clipping will exclude the right and bottom edges of this frame. if (m_hBar || m_vBar) { context->save(); + context->clip(absRect); IntRect largerCorner = absRect; largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1)); context->setStrokeColor(Color(makeRGB(217, 217, 217))); @@ -1563,9 +1806,12 @@ bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const if (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE) return false; + RenderBox* box = renderBox(); + ASSERT(box); + IntPoint localPoint = absoluteToContents(absolutePoint); - IntRect localBounds(0, 0, renderer()->width(), renderer()->height()); + IntRect localBounds(0, 0, box->width(), box->height()); return resizerCornerRect(this, localBounds).contains(localPoint); } @@ -1574,10 +1820,13 @@ bool RenderLayer::hitTestOverflowControls(HitTestResult& result) if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) return false; + RenderBox* box = renderBox(); + ASSERT(box); + int x = 0; int y = 0; convertToLayerCoords(root(), x, y); - IntRect absBounds(x, y, renderer()->width(), renderer()->height()); + IntRect absBounds(x, y, box->width(), box->height()); IntRect resizeControlRect; if (renderer()->style()->resize() != RESIZE_NONE) { @@ -1589,7 +1838,10 @@ bool RenderLayer::hitTestOverflowControls(HitTestResult& result) int resizeControlSize = max(resizeControlRect.height(), 0); if (m_vBar) { - IntRect vBarRect(absBounds.right() - renderer()->borderRight() - m_vBar->width(), absBounds.y() + renderer()->borderTop(), m_vBar->width(), absBounds.height() - (renderer()->borderTop() + renderer()->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize)); + IntRect vBarRect(absBounds.right() - box->borderRight() - m_vBar->width(), + absBounds.y() + box->borderTop(), + m_vBar->width(), + absBounds.height() - (box->borderTop() + box->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize)); if (vBarRect.contains(result.point())) { result.setScrollbar(m_vBar.get()); return true; @@ -1598,7 +1850,10 @@ bool RenderLayer::hitTestOverflowControls(HitTestResult& result) resizeControlSize = max(resizeControlRect.width(), 0); if (m_hBar) { - IntRect hBarRect(absBounds.x() + renderer()->borderLeft(), absBounds.bottom() - renderer()->borderBottom() - m_hBar->height(), absBounds.width() - (renderer()->borderLeft() + renderer()->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize), m_hBar->height()); + IntRect hBarRect(absBounds.x() + box->borderLeft(), + absBounds.bottom() - box->borderBottom() - m_hBar->height(), + absBounds.width() - (box->borderLeft() + box->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize), + m_hBar->height()); if (hBarRect.contains(result.point())) { result.setScrollbar(m_hBar.get()); return true; @@ -1654,6 +1909,12 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, const IntRect& paintDirtyRect, bool haveTransparency, PaintRestriction paintRestriction, RenderObject* paintingRoot, bool appliedTransform, bool temporaryClipRects) { +#if USE(ACCELERATED_COMPOSITING) + // Composited RenderLayers are painted via the backing's paintIntoLayer(). + if (isComposited() && !backing()->paintingGoesToWindow()) + return; +#endif + // Avoid painting layers when stylesheets haven't loaded. This eliminates FOUC. // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document // will do a full repaint(). @@ -1664,11 +1925,11 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, if (!renderer()->opacity()) return; - if (isTransparent()) + if (paintsWithTransparency()) haveTransparency = true; // Apply a transform if we have one. A reflection is considered to be a transform, since it is a flip and a translate. - if (m_transform && !appliedTransform) { + if (paintsWithTransform() && !appliedTransform) { // If the transform can't be inverted, then don't paint anything. if (!m_transform->isInvertible()) return; @@ -1681,14 +1942,9 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, // Make sure the parent's clip rects have been calculated. IntRect clipRect = paintDirtyRect; if (parent()) { - if (temporaryClipRects) { - ClipRects parentClipRects; - parent()->calculateClipRects(rootLayer, parentClipRects); - clipRect = parentClipRects.overflowClipRect(); - } else { - parent()->updateClipRects(rootLayer); - clipRect = parent()->clipRects()->overflowClipRect(); - } + ClipRects parentRects; + parentClipRects(rootLayer, parentRects, temporaryClipRects); + clipRect = parentRects.overflowClipRect(); clipRect.intersect(paintDirtyRect); } @@ -1710,7 +1966,7 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, // Now do a paint with the root layer shifted to be us. paintLayer(this, p, transform.inverse().mapRect(paintDirtyRect), haveTransparency, paintRestriction, paintingRoot, true, temporaryClipRects); - + p->restore(); // Restore the clip. @@ -1732,12 +1988,11 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, temporaryClipRects); int x = layerBounds.x(); int y = layerBounds.y(); - int tx = x - renderer()->x(); - int ty = y - renderer()->y(); + int tx = x - renderBoxX(); + int ty = y - renderBoxY(); // Ensure our lists are up-to-date. - updateZOrderLists(); - updateOverflowList(); + updateLayerListsIfNeeded(); bool selectionOnly = paintRestriction == PaintRestrictionSelectionOnly || paintRestriction == PaintRestrictionSelectionOnlyBlackText; bool forceBlackText = paintRestriction == PaintRestrictionSelectionOnlyBlackText; @@ -1765,11 +2020,6 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, RenderObject::PaintInfo paintInfo(p, damageRect, PaintPhaseBlockBackground, false, paintingRootForRenderer, 0); renderer()->paint(paintInfo, tx, ty); - // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with - // z-index. We paint after we painted the background/border, so that the scrollbars will - // sit above the background/border. - paintOverflowControls(p, x, y, damageRect); - // Restore the clip. restoreClip(p, paintDirtyRect, damageRect); } @@ -1813,10 +2063,12 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, } // Paint any child layers that have overflow. - if (m_overflowList) - for (Vector<RenderLayer*>::iterator it = m_overflowList->begin(); it != m_overflowList->end(); ++it) - it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects); - + if (m_normalFlowList) + for (Vector<RenderLayer*>::iterator it = m_normalFlowList->begin(); it != m_normalFlowList->end(); ++it) { + if (it[0]->isSelfPaintingLayer()) + it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects); + } + // Now walk the sorted list of children with positive z-indices. if (m_posZOrderList) for (Vector<RenderLayer*>::iterator it = m_posZOrderList->begin(); it != m_posZOrderList->end(); ++it) @@ -1834,7 +2086,7 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, } // End our transparency layer - if (isTransparent() && m_usedTransparency) { + if (haveTransparency && m_usedTransparency && !m_paintingInsideReflection) { p->endTransparencyLayer(); p->restore(); m_usedTransparency = false; @@ -1855,9 +2107,19 @@ bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) renderer()->document()->updateLayout(); IntRect boundsRect(m_x, m_y, width(), height()); - boundsRect.intersect(frameVisibleRect(renderer())); - - RenderLayer* insideLayer = hitTestLayer(this, request, result, boundsRect, result.point()); + if (!request.ignoreClipping()) + boundsRect.intersect(frameVisibleRect(renderer())); + + RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, boundsRect, result.point(), false); + if (!insideLayer) { + // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, + // return ourselves. We do this so mouse events continue getting delivered after a drag has + // exited the WebView, and so hit testing over a scrollbar hits the content document. + if ((request.active() || request.mouseUp()) && renderer()->isRenderView()) { + renderer()->updateHitTestResult(result, result.point()); + insideLayer = this; + } + } // 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. @@ -1880,135 +2142,302 @@ bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) Node* RenderLayer::enclosingElement() const { for (RenderObject* r = renderer(); r; r = r->parent()) { - if (Node* e = r->element()) + if (Node* e = r->node()) return e; } ASSERT_NOT_REACHED(); return 0; } -RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, - const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform) +// Compute the z-offset of the point in the transformState. +// This is effectively projecting a ray normal to the plane of ancestor, finding where that +// ray intersects target, and computing the z delta between those two points. +static double computeZOffset(const HitTestingTransformState& transformState) +{ + // We got an affine transform, so no z-offset + if (transformState.m_accumulatedTransform.isAffine()) + return 0; + + // Flatten the point into the target plane + FloatPoint targetPoint = transformState.mappedPoint(); + + // Now map the point back through the transform, which computes Z. + FloatPoint3D backmappedPoint = transformState.m_accumulatedTransform.mapPoint(FloatPoint3D(targetPoint)); + return backmappedPoint.z(); +} + +PassRefPtr<HitTestingTransformState> RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, + const HitTestingTransformState* containerTransformState) const +{ + RefPtr<HitTestingTransformState> transformState; + int offsetX = 0; + int offsetY = 0; + if (containerTransformState) { + // If we're already computing transform state, then it's relative to the container (which we know is non-null). + transformState = HitTestingTransformState::create(*containerTransformState); + convertToLayerCoords(containerLayer, offsetX, offsetY); + } else { + // If this is the first time we need to make transform state, then base it off of hitTestPoint, + // which is relative to rootLayer. + transformState = HitTestingTransformState::create(hitTestPoint, FloatQuad(hitTestRect)); + convertToLayerCoords(rootLayer, offsetX, offsetY); + } + + TransformationMatrix containerTransform = renderer()->transformFromContainer(containerLayer ? containerLayer->renderer() : 0, IntSize(offsetX, offsetY)); + transformState->applyTransform(containerTransform, true); + return transformState; +} + + +static bool isHitCandidate(const RenderLayer* hitLayer, bool canDepthSort, double* zOffset, const HitTestingTransformState* transformState) +{ + if (!hitLayer) + return false; + + // The hit layer is depth-sorting with other layers, so just say that it was hit. + if (canDepthSort) + return true; + + // We need to look at z-depth to decide if this layer was hit. + if (zOffset) { + ASSERT(transformState); + // This is actually computing our z, but that's OK because the hitLayer is coplanar with us. + double childZOffset = computeZOffset(*transformState); + if (childZOffset > *zOffset) { + *zOffset = childZOffset; + return true; + } + return false; + } + + return true; +} + +// hitTestPoint and hitTestRect are relative to rootLayer. +// A 'flattening' layer is one preserves3D() == false. +// transformState.m_accumulatedTransform holds the transform from the containing flattening layer. +// transformState.m_lastPlanarPoint is the hitTestPoint in the plane of the containing flattening layer. +// transformState.m_lastPlanarQuad is the hitTestRect as a quad in the plane of the containing flattening layer. +// +// If zOffset is non-null (which indicates that the caller wants z offset information), +// *zOffset on return is the z offset of the hit point relative to the containing flattening layer. +RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform, + const HitTestingTransformState* transformState, double* zOffset) { + // The natural thing would be to keep HitTestingTransformState on the stack, but it's big, so we heap-allocate. + + bool useTemporaryClipRects = false; +#if USE(ACCELERATED_COMPOSITING) + useTemporaryClipRects = compositor()->inCompositingMode(); +#endif + // Apply a transform if we have one. - if (m_transform && !appliedTransform) { - // If the transform can't be inverted, then don't hit test this layer at all. - if (!m_transform->isInvertible()) - return 0; - + if (transform() && !appliedTransform) { // Make sure the parent's clip rects have been calculated. if (parent()) { - parent()->updateClipRects(rootLayer); - + ClipRects parentRects; + parentClipRects(rootLayer, parentRects, useTemporaryClipRects); + IntRect clipRect = parentRects.overflowClipRect(); // Go ahead and test the enclosing clip now. - IntRect clipRect = parent()->clipRects()->overflowClipRect(); if (!clipRect.contains(hitTestPoint)) return 0; } - // Adjust the transform such that the renderer's upper left corner is at (0,0) in user space. - // This involves subtracting out the position of the layer in our current coordinate space. - int x = 0; - int y = 0; - convertToLayerCoords(rootLayer, x, y); - TransformationMatrix transform; - transform.translate(x, y); - transform = *m_transform * transform; - - // Map the hit test point into the transformed space and then do a hit test with the root layer shifted to be us. - return hitTestLayer(this, request, result, transform.inverse().mapRect(hitTestRect), transform.inverse().mapPoint(hitTestPoint), true); + // Create a transform state to accumulate this transform. + RefPtr<HitTestingTransformState> newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestPoint, transformState); + + // If the transform can't be inverted, then don't hit test this layer at all. + if (!newTransformState->m_accumulatedTransform.isInvertible()) + return 0; + + // Compute the point and the hit test rect in the coords of this layer by using the values + // from the transformState, which store the point and quad in the coords of the last flattened + // layer, and the accumulated transform which lets up map through preserve-3d layers. + // + // We can't just map hitTestPoint and hitTestRect because they may have been flattened (losing z) + // by our container. + IntPoint localPoint = roundedIntPoint(newTransformState->mappedPoint()); + IntRect localHitTestRect; +#if USE(ACCELERATED_COMPOSITING) + if (isComposited()) { + // It doesn't make sense to project hitTestRect into the plane of this layer, so use the same bounds we use for painting. + localHitTestRect = compositor()->calculateCompositedBounds(this, this); + } else +#endif + localHitTestRect = newTransformState->mappedQuad().enclosingBoundingBox(); + + // Now do a hit test with the root layer shifted to be us. + return hitTestLayer(this, containerLayer, request, result, localHitTestRect, localPoint, true, newTransformState.get(), zOffset); } + // Ensure our lists and 3d status are up-to-date. + updateLayerListsIfNeeded(); + update3DTransformedDescendantStatus(); + + RefPtr<HitTestingTransformState> localTransformState; + if (appliedTransform) { + // We computed the correct state in the caller (above code), so just reference it. + ASSERT(transformState); + localTransformState = const_cast<HitTestingTransformState*>(transformState); + } else if (transformState || m_has3DTransformedDescendant || preserves3D()) { + // We need transform state for the first time, or to offset the container state, so create it here. + localTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestPoint, transformState); + } + + // Check for hit test on backface if backface-visibility is 'hidden' + if (localTransformState && renderer()->style()->backfaceVisibility() == BackfaceVisibilityHidden) { + TransformationMatrix invertedMatrix = localTransformState->m_accumulatedTransform.inverse(); + // If the z-vector of the matrix is negative, the back is facing towards the viewer. + if (invertedMatrix.m33() < 0) + return 0; + } + + RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformState; + if (localTransformState && !preserves3D()) { + // Keep a copy of the pre-flattening state, for computing z-offsets for the container + unflattenedTransformState = HitTestingTransformState::create(*localTransformState); + // This layer is flattening, so flatten the state passed to descendants. + localTransformState->flatten(); + } + // Calculate the clip rects we should use. IntRect layerBounds; IntRect bgRect; IntRect fgRect; IntRect outlineRect; - calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect); - - // Ensure our lists are up-to-date. - updateZOrderLists(); - updateOverflowList(); - - // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer, - // we are done and can return it. - RenderLayer* insideLayer = 0; - - // Begin by walking our list of positive layers from highest z-index down to the lowest - // z-index. + calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect, useTemporaryClipRects); + + // The following are used for keeping track of the z-depth of the hit point of 3d-transformed + // descendants. + double localZOffset = -numeric_limits<double>::infinity(); + double* zOffsetForDescendantsPtr = 0; + double* zOffsetForContentsPtr = 0; + + bool depthSortDescendants = false; + if (preserves3D()) { + depthSortDescendants = true; + // Our layers can depth-test with our container, so share the z depth pointer with the container, if it passed one down. + zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; + zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; + } else if (m_has3DTransformedDescendant) { + // Flattening layer with 3d children; use a local zOffset pointer to depth-test children and foreground. + depthSortDescendants = true; + zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; + zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; + } else if (zOffset) { + zOffsetForDescendantsPtr = 0; + // Container needs us to give back a z offset for the hit layer. + zOffsetForContentsPtr = zOffset; + } + + // This variable tracks which layer the mouse ends up being inside. + RenderLayer* candidateLayer = 0; + + // Begin by walking our list of positive layers from highest z-index down to the lowest z-index. if (m_posZOrderList) { for (int i = m_posZOrderList->size() - 1; i >= 0; --i) { - insideLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint); - if (insideLayer) - return insideLayer; + HitTestResult tempResult(result.point()); + RenderLayer* hitLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestPoint, false, localTransformState.get(), zOffsetForDescendantsPtr); + if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState.get())) { + result = tempResult; + if (!depthSortDescendants) + return hitLayer; + + candidateLayer = hitLayer; + } } } // Now check our overflow objects. - if (m_overflowList) { - for (int i = m_overflowList->size() - 1; i >= 0; --i) { - insideLayer = m_overflowList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint); - if (insideLayer) - return insideLayer; + if (m_normalFlowList) { + for (int i = m_normalFlowList->size() - 1; i >= 0; --i) { + RenderLayer* currLayer = m_normalFlowList->at(i); + if (!currLayer->isSelfPaintingLayer()) + continue; + + HitTestResult tempResult(result.point()); + RenderLayer* hitLayer = currLayer->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestPoint, false, localTransformState.get(), zOffsetForDescendantsPtr); + if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState.get())) { + result = tempResult; + if (!depthSortDescendants) + return hitLayer; + + candidateLayer = hitLayer; + } } } // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. - if (fgRect.contains(hitTestPoint) && - renderer()->hitTest(request, result, hitTestPoint, - layerBounds.x() - renderer()->x(), - layerBounds.y() - renderer()->y(), - HitTestDescendants)) { - // For positioned generated content, we might still not have a - // node by the time we get to the layer level, since none of - // the content in the layer has an element. So just walk up - // the tree. - if (!result.innerNode() || !result.innerNonSharedNode()) { - Node* e = enclosingElement(); - if (!result.innerNode()) - result.setInnerNode(e); - if (!result.innerNonSharedNode()) - result.setInnerNonSharedNode(e); + if (fgRect.contains(hitTestPoint)) { + // Hit test with a temporary HitTestResult, because we onlyl 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())) { + result = tempResult; + if (!depthSortDescendants) + return this; + // Foreground can depth-sort with descendant layers, so keep this as a candidate. + candidateLayer = this; } - - return this; } - + // Now check our negative z-index children. if (m_negZOrderList) { for (int i = m_negZOrderList->size() - 1; i >= 0; --i) { - insideLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint); - if (insideLayer) - return insideLayer; + HitTestResult tempResult(result.point()); + RenderLayer* hitLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestPoint, false, localTransformState.get(), zOffsetForDescendantsPtr); + if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState.get())) { + result = tempResult; + if (!depthSortDescendants) + return hitLayer; + + candidateLayer = hitLayer; + } } } + + // If we found a layer, return. Child layers, and foreground always render in front of background. + if (candidateLayer) + return candidateLayer; - // Next we want to see if the mouse is inside this layer but not any of its children. - if (bgRect.contains(hitTestPoint) && - renderer()->hitTest(request, result, hitTestPoint, - layerBounds.x() - renderer()->x(), - layerBounds.y() - renderer()->y(), - HitTestSelf)) { - if (!result.innerNode() || !result.innerNonSharedNode()) { - Node* e = enclosingElement(); - if (!result.innerNode()) - result.setInnerNode(e); - if (!result.innerNonSharedNode()) - result.setInnerNonSharedNode(e); + if (bgRect.contains(hitTestPoint)) { + HitTestResult tempResult(result.point()); + if (hitTestContents(request, tempResult, layerBounds, hitTestPoint, HitTestSelf) && + isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { + result = tempResult; + return this; } - - return this; } + + return 0; +} - // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, - // return ourselves. We do this so mouse events continue getting delivered after a drag has - // exited the WebView, and so hit testing over a scrollbar hits the content document. - if ((request.active || request.mouseUp) && renderer()->isRenderView()) { - renderer()->updateHitTestResult(result, hitTestPoint); - return this; +bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& result, const IntRect& layerBounds, const IntPoint& hitTestPoint, HitTestFilter hitTestFilter) const +{ + if (!renderer()->hitTest(request, result, hitTestPoint, + layerBounds.x() - renderBoxX(), + layerBounds.y() - renderBoxY(), + hitTestFilter)) { + // It's wrong to set innerNode, but then claim that you didn't hit anything. + ASSERT(!result.innerNode()); + return false; } - return 0; + // For positioned generated content, we might still not have a + // node by the time we get to the layer level, since none of + // the content in the layer has an element. So just walk up + // the tree. + if (!result.innerNode() || !result.innerNonSharedNode()) { + Node* e = enclosingElement(); + if (!result.innerNode()) + result.setInnerNode(e); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(e); + } + + return true; } void RenderLayer::updateClipRects(const RenderLayer* rootLayer) @@ -2039,10 +2468,9 @@ void RenderLayer::updateClipRects(const RenderLayer* rootLayer) void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& clipRects, bool useCached) const { - IntRect infiniteRect(INT_MIN/2, INT_MIN/2, INT_MAX, INT_MAX); if (!parent()) { // The root layer's clip rect is always infinite. - clipRects.reset(infiniteRect); + clipRects.reset(ClipRects::infiniteRect()); return; } @@ -2058,7 +2486,7 @@ void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& cl parentLayer->calculateClipRects(rootLayer, clipRects); } else - clipRects.reset(infiniteRect); + clipRects.reset(ClipRects::infiniteRect()); // A fixed object is essentially the root of its containing block hierarchy, so when // we encounter such an object, we reset our clip rects to the fixedClipRect. @@ -2086,13 +2514,13 @@ void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& cl } if (renderer()->hasOverflowClip()) { - IntRect newOverflowClip = renderer()->getOverflowClipRect(x,y); + IntRect newOverflowClip = toRenderBox(renderer())->overflowClipRect(x,y); clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); if (renderer()->isPositioned() || renderer()->isRelPositioned()) clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); } if (renderer()->hasClip()) { - IntRect newPosClip = renderer()->getClipRect(x,y); + IntRect newPosClip = toRenderBox(renderer())->clipRect(x,y); clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect())); clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect())); clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect())); @@ -2100,24 +2528,30 @@ void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& cl } } +void RenderLayer::parentClipRects(const RenderLayer* rootLayer, ClipRects& clipRects, bool temporaryClipRects) const +{ + ASSERT(parent()); + if (temporaryClipRects) { + parent()->calculateClipRects(rootLayer, clipRects); + return; + } + + parent()->updateClipRects(rootLayer); + clipRects = *parent()->clipRects(); +} + void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds, IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect, bool temporaryClipRects) const { if (rootLayer != this && parent()) { - ClipRects parentClipRects; - if (temporaryClipRects) - parent()->calculateClipRects(rootLayer, parentClipRects); - else { - parent()->updateClipRects(rootLayer); - parentClipRects = *parent()->clipRects(); - } - - backgroundRect = renderer()->style()->position() == FixedPosition ? parentClipRects.fixedClipRect() : - (renderer()->isPositioned() ? parentClipRects.posClipRect() : - parentClipRects.overflowClipRect()); + ClipRects parentRects; + parentClipRects(rootLayer, parentRects, temporaryClipRects); + backgroundRect = renderer()->style()->position() == FixedPosition ? parentRects.fixedClipRect() : + (renderer()->isPositioned() ? parentRects.posClipRect() : + parentRects.overflowClipRect()); RenderView* view = renderer()->view(); ASSERT(view); - if (view && parentClipRects.fixed() && rootLayer->renderer() == view) + if (view && parentRects.fixed() && rootLayer->renderer() == view) backgroundRect.move(view->frameView()->scrollX(), view->frameView()->scrollY()); backgroundRect.intersect(paintDirtyRect); @@ -2136,10 +2570,10 @@ void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& pa if (renderer()->hasOverflowClip() || renderer()->hasClip()) { // This layer establishes a clip of some kind. if (renderer()->hasOverflowClip()) - foregroundRect.intersect(renderer()->getOverflowClipRect(x,y)); + foregroundRect.intersect(toRenderBox(renderer())->overflowClipRect(x,y)); if (renderer()->hasClip()) { // Clip applies to *us* as well, so go ahead and update the damageRect. - IntRect newPosClip = renderer()->getClipRect(x,y); + IntRect newPosClip = toRenderBox(renderer())->clipRect(x,y); backgroundRect.intersect(newPosClip); foregroundRect.intersect(newPosClip); outlineRect.intersect(newPosClip); @@ -2202,7 +2636,7 @@ bool RenderLayer::intersectsDamageRect(const IntRect& layerBounds, const IntRect return boundingBox(rootLayer).intersects(damageRect); } -IntRect RenderLayer::boundingBox(const RenderLayer* rootLayer) const +IntRect RenderLayer::localBoundingBox() const { // There are three special cases we need to consider. // (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the @@ -2216,56 +2650,64 @@ IntRect RenderLayer::boundingBox(const RenderLayer* rootLayer) const IntRect result; if (renderer()->isRenderInline()) { // Go from our first line box to our last line box. - RenderInline* inlineFlow = static_cast<RenderInline*>(renderer()); + RenderInline* inlineFlow = toRenderInline(renderer()); InlineFlowBox* firstBox = inlineFlow->firstLineBox(); if (!firstBox) return result; int top = firstBox->root()->topOverflow(); int bottom = inlineFlow->lastLineBox()->root()->bottomOverflow(); - int left = firstBox->xPos(); + int left = firstBox->x(); for (InlineRunBox* curr = firstBox->nextLineBox(); curr; curr = curr->nextLineBox()) - left = min(left, curr->xPos()); - result = IntRect(m_x + left, m_y + (top - renderer()->y()), width(), bottom - top); + left = min(left, curr->x()); + result = IntRect(left, top, width(), bottom - top); } else if (renderer()->isTableRow()) { // Our bounding box is just the union of all of our cells' border/overflow rects. for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) { if (child->isTableCell()) { IntRect bbox = toRenderBox(child)->borderBoxRect(); result.unite(bbox); - IntRect overflowRect = renderer()->overflowRect(false); + IntRect overflowRect = renderBox()->overflowRect(false); if (bbox != overflowRect) result.unite(overflowRect); } } - result.move(m_x, m_y); } else { - if (renderer()->hasMask()) - result = renderer()->maskClipRect(); + RenderBox* box = renderBox(); + ASSERT(box); + if (box->hasMask()) + result = box->maskClipRect(); else { - IntRect bbox = renderer()->borderBoxRect(); + IntRect bbox = box->borderBoxRect(); result = bbox; - IntRect overflowRect = renderer()->overflowRect(false); + IntRect overflowRect = box->overflowRect(false); if (bbox != overflowRect) result.unite(overflowRect); } - - // We have to adjust the x/y of this result so that it is in the coordinate space of the layer. - result.move(m_x, m_y); } - - // Convert the bounding box to an absolute position. We can do this easily by looking at the delta - // between the bounding box's xpos and our layer's xpos and then applying that to the absolute layerBounds - // passed in. - int absX = 0, absY = 0; - convertToLayerCoords(rootLayer, absX, absY); - result.move(absX - m_x, absY - m_y); + RenderView* view = renderer()->view(); ASSERT(view); if (view) - result.inflate(view->maximalOutlineSize()); + result.inflate(view->maximalOutlineSize()); // Used to apply a fudge factor to dirty-rect checks on blocks/tables. + return result; } +IntRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer) const +{ + IntRect result = localBoundingBox(); + + int deltaX = 0, deltaY = 0; + convertToLayerCoords(ancestorLayer, deltaX, deltaY); + result.move(deltaX, deltaY); + return result; +} + +IntRect RenderLayer::absoluteBoundingBox() const +{ + return boundingBox(root()); +} + void RenderLayer::clearClipRectsIncludingDescendants() { if (!m_clipRects) @@ -2288,6 +2730,38 @@ void RenderLayer::clearClipRects() } } +#if USE(ACCELERATED_COMPOSITING) +RenderLayerBacking* RenderLayer::ensureBacking() +{ + if (!m_backing) + m_backing.set(new RenderLayerBacking(this)); + return m_backing.get(); +} + +void RenderLayer::clearBacking() +{ + m_backing.clear(); +} +#endif + +void RenderLayer::setParent(RenderLayer* parent) +{ + if (parent == m_parent) + return; + +#if USE(ACCELERATED_COMPOSITING) + if (m_parent && !renderer()->documentBeingDestroyed()) + compositor()->layerWillBeRemoved(m_parent, this); +#endif + + m_parent = parent; + +#if USE(ACCELERATED_COMPOSITING) + if (m_parent && !renderer()->documentBeingDestroyed()) + compositor()->layerWasAdded(m_parent, this); +#endif +} + static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2) { if (!obj1 || !obj2) @@ -2303,28 +2777,28 @@ static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2) void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestResult& result) { - // We don't update :hover/:active state when the result is marked as readonly. - if (request.readonly) + // We don't update :hover/:active state when the result is marked as readOnly. + if (request.readOnly()) return; Document* doc = renderer()->document(); Node* activeNode = doc->activeNode(); - if (activeNode && !request.active) { + if (activeNode && !request.active()) { // We are clearing the :active chain because the mouse has been released. for (RenderObject* curr = activeNode->renderer(); curr; curr = curr->parent()) { - if (curr->element() && !curr->isText()) - curr->element()->setInActiveChain(false); + if (curr->node() && !curr->isText()) + curr->node()->setInActiveChain(false); } doc->setActiveNode(0); } else { Node* newActiveNode = result.innerNode(); - if (!activeNode && newActiveNode && request.active) { + if (!activeNode && newActiveNode && request.active()) { // We are setting the :active chain and freezing it. If future moves happen, they // will need to reference this chain. for (RenderObject* curr = newActiveNode->renderer(); curr; curr = curr->parent()) { - if (curr->element() && !curr->isText()) { - curr->element()->setInActiveChain(true); + if (curr->node() && !curr->isText()) { + curr->node()->setInActiveChain(true); } } doc->setActiveNode(newActiveNode); @@ -2334,7 +2808,7 @@ void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestR // If the mouse is down and if this is a mouse move event, we want to restrict changes in // :hover/:active to only apply to elements that are in the :active chain that we froze // at the time the mouse went down. - bool mustBeInActiveChain = request.active && request.mouseMove; + bool mustBeInActiveChain = request.active() && request.mouseMove(); // Check to see if the hovered node has changed. If not, then we don't need to // do anything. @@ -2354,18 +2828,18 @@ void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestR if (oldHoverObj != newHoverObj) { // The old hover path only needs to be cleared up to (and not including) the common ancestor; for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = curr->hoverAncestor()) { - if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) { - curr->element()->setActive(false); - curr->element()->setHovered(false); + if (curr->node() && !curr->isText() && (!mustBeInActiveChain || curr->node()->inActiveChain())) { + curr->node()->setActive(false); + curr->node()->setHovered(false); } } } // Now set the hover state for our new object up to the root. for (RenderObject* curr = newHoverObj; curr; curr = curr->hoverAncestor()) { - if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) { - curr->element()->setActive(request.active); - curr->element()->setHovered(true); + if (curr->node() && !curr->isText() && (!mustBeInActiveChain || curr->node()->inActiveChain())) { + curr->node()->setActive(request.active()); + curr->node()->setHovered(true); } } } @@ -2383,6 +2857,11 @@ void RenderLayer::dirtyZOrderLists() if (m_negZOrderList) m_negZOrderList->clear(); m_zOrderListsDirty = true; + +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed()) + compositor()->setCompositingLayersNeedUpdate(); +#endif } void RenderLayer::dirtyStackingContextZOrderLists() @@ -2392,18 +2871,23 @@ void RenderLayer::dirtyStackingContextZOrderLists() sc->dirtyZOrderLists(); } -void RenderLayer::dirtyOverflowList() +void RenderLayer::dirtyNormalFlowList() { - if (m_overflowList) - m_overflowList->clear(); - m_overflowListDirty = true; + if (m_normalFlowList) + m_normalFlowList->clear(); + m_normalFlowListDirty = true; + +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed()) + compositor()->setCompositingLayersNeedUpdate(); +#endif } void RenderLayer::updateZOrderLists() { if (!isStackingContext() || !m_zOrderListsDirty) return; - + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) if (!m_reflection || reflectionLayer() != child) child->collectLayers(m_posZOrderList, m_negZOrderList); @@ -2411,27 +2895,28 @@ void RenderLayer::updateZOrderLists() // Sort the two lists. if (m_posZOrderList) std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex); + if (m_negZOrderList) std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex); m_zOrderListsDirty = false; } -void RenderLayer::updateOverflowList() +void RenderLayer::updateNormalFlowList() { - if (!m_overflowListDirty) + if (!m_normalFlowListDirty) return; for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { // Ignore non-overflow layers and reflections. - if (child->isOverflowOnly() && (!m_reflection || reflectionLayer() != child)) { - if (!m_overflowList) - m_overflowList = new Vector<RenderLayer*>; - m_overflowList->append(child); + if (child->isNormalFlowOnly() && (!m_reflection || reflectionLayer() != child)) { + if (!m_normalFlowList) + m_normalFlowList = new Vector<RenderLayer*>; + m_normalFlowList->append(child); } } - m_overflowListDirty = false; + m_normalFlowListDirty = false; } void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderLayer*>*& negBuffer) @@ -2439,7 +2924,7 @@ void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderL updateVisibilityStatus(); // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists. - if ((m_hasVisibleContent || (m_hasVisibleDescendant && isStackingContext())) && !isOverflowOnly()) { + if ((m_hasVisibleContent || (m_hasVisibleDescendant && isStackingContext())) && !isNormalFlowOnly()) { // Determine which buffer the child should be in. Vector<RenderLayer*>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer; @@ -2462,6 +2947,19 @@ void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderL } } +void RenderLayer::updateLayerListsIfNeeded() +{ +#if USE(ACCELERATED_COMPOSITING) + if (compositor()->inCompositingMode()) { + if ((isStackingContext() && m_zOrderListsDirty) || m_normalFlowListDirty) + compositor()->updateCompositingLayers(this); + return; + } +#endif + updateZOrderLists(); + updateNormalFlowList(); +} + void RenderLayer::repaintIncludingDescendants() { renderer()->repaint(); @@ -2469,23 +2967,62 @@ void RenderLayer::repaintIncludingDescendants() curr->repaintIncludingDescendants(); } -bool RenderLayer::shouldBeOverflowOnly() const +#if USE(ACCELERATED_COMPOSITING) +void RenderLayer::setBackingNeedsRepaint() +{ + ASSERT(isComposited()); + if (backing()->paintingGoesToWindow()) { + // If we're trying to repaint the placeholder document layer, propagate the + // repaint to the native view system. + RenderView* view = renderer()->view(); + if (view) + view->repaintViewRectangle(absoluteBoundingBox()); + } else + backing()->setContentsNeedDisplay(); +} + +void RenderLayer::setBackingNeedsRepaintInRect(const IntRect& r) { - return (renderer()->hasOverflowClip() || renderer()->hasReflection()) && + ASSERT(isComposited()); + if (backing()->paintingGoesToWindow()) { + // If we're trying to repaint the placeholder document layer, propagate the + // repaint to the native view system. + IntRect absRect(r); + int x = 0; + int y = 0; + convertToLayerCoords(root(), x, y); + absRect.move(x, y); + + RenderView* view = renderer()->view(); + if (view) + view->repaintViewRectangle(absRect); + } else + backing()->setContentsNeedDisplayInRect(r); +} +#endif + +bool RenderLayer::shouldBeNormalFlowOnly() const +{ + return (renderer()->hasOverflowClip() || renderer()->hasReflection() || renderer()->hasMask()) && !renderer()->isPositioned() && !renderer()->isRelPositioned() && !renderer()->hasTransform() && !isTransparent(); } -void RenderLayer::styleChanged(RenderStyle::Diff, const RenderStyle*) +bool RenderLayer::isSelfPaintingLayer() const +{ + return !isNormalFlowOnly() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isTableRow(); +} + +void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle*) { - bool isOverflowOnly = shouldBeOverflowOnly(); - if (isOverflowOnly != m_isOverflowOnly) { - m_isOverflowOnly = isOverflowOnly; + bool isNormalFlowOnly = shouldBeNormalFlowOnly(); + if (isNormalFlowOnly != m_isNormalFlowOnly) { + m_isNormalFlowOnly = isNormalFlowOnly; RenderLayer* p = parent(); if (p) - p->dirtyOverflowList(); + p->dirtyNormalFlowList(); dirtyStackingContextZOrderLists(); } @@ -2516,12 +3053,21 @@ void RenderLayer::styleChanged(RenderStyle::Diff, const RenderStyle*) updateScrollCornerStyle(); updateResizerStyle(); + +#if USE(ACCELERATED_COMPOSITING) + updateTransform(); + + if (compositor()->updateLayerCompositingState(this, diff)) + compositor()->setCompositingLayersNeedUpdate(); +#else + UNUSED_PARAM(diff); +#endif } void RenderLayer::updateScrollCornerStyle() { - RenderObject* actualRenderer = renderer()->node()->isElementNode() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); - RefPtr<RenderStyle> corner = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RenderStyle::SCROLLBAR_CORNER, actualRenderer->style()) : 0; + RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + RefPtr<RenderStyle> corner = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(SCROLLBAR_CORNER, actualRenderer->style()) : 0; if (corner) { if (!m_scrollCorner) { m_scrollCorner = new (renderer()->renderArena()) RenderScrollbarPart(renderer()->document()); @@ -2536,8 +3082,8 @@ void RenderLayer::updateScrollCornerStyle() void RenderLayer::updateResizerStyle() { - RenderObject* actualRenderer = renderer()->node()->isElementNode() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); - RefPtr<RenderStyle> resizer = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RenderStyle::RESIZER, actualRenderer->style()) : 0; + RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + RefPtr<RenderStyle> resizer = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RESIZER, actualRenderer->style()) : 0; if (resizer) { if (!m_resizer) { m_resizer = new (renderer()->renderArena()) RenderScrollbarPart(renderer()->document()); diff --git a/WebCore/rendering/RenderLayer.h b/WebCore/rendering/RenderLayer.h index 54c15e9..6e70a4b 100644 --- a/WebCore/rendering/RenderLayer.h +++ b/WebCore/rendering/RenderLayer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003 Apple Computer, Inc. + * Copyright (C) 2003, 2009 Apple Inc. All rights reserved. * * Portions are Copyright (C) 1998 Netscape Communications Corporation. * @@ -44,16 +44,18 @@ #ifndef RenderLayer_h #define RenderLayer_h -#include "ScrollbarClient.h" #include "RenderBox.h" +#include "ScrollBehavior.h" +#include "ScrollbarClient.h" #include "Timer.h" #include <wtf/OwnPtr.h> namespace WebCore { -class TransformationMatrix; class CachedResource; +class HitTestRequest; class HitTestResult; +class HitTestingTransformState; class RenderFrameSet; class RenderMarquee; class RenderReplica; @@ -63,8 +65,12 @@ class RenderTable; class RenderText; class RenderView; class Scrollbar; +class TransformationMatrix; -struct HitTestRequest; +#if USE(ACCELERATED_COMPOSITING) +class RenderLayerBacking; +class RenderLayerCompositor; +#endif class ClipRects { public: @@ -139,7 +145,9 @@ public: m_fixed = other.fixed(); return *this; } - + + static IntRect infiniteRect() { return IntRect(INT_MIN/2, INT_MIN/2, INT_MAX, INT_MAX); } + private: // The normal operator new is disallowed on all render objects. void* operator new(size_t) throw(); @@ -154,38 +162,13 @@ private: class RenderLayer : public ScrollbarClient { public: - enum ScrollBehavior { - noScroll, - alignCenter, - alignTop, - alignBottom, - alignLeft, - alignRight, - alignToClosestEdge - }; - - struct ScrollAlignment { - ScrollBehavior m_rectVisible; - ScrollBehavior m_rectHidden; - ScrollBehavior m_rectPartial; - }; - friend class RenderReplica; - static const ScrollAlignment gAlignCenterIfNeeded; - static const ScrollAlignment gAlignToEdgeIfNeeded; - static const ScrollAlignment gAlignCenterAlways; - static const ScrollAlignment gAlignTopAlways; - static const ScrollAlignment gAlignBottomAlways; - - static ScrollBehavior getVisibleBehavior(const ScrollAlignment& s) { return s.m_rectVisible; } - static ScrollBehavior getPartialBehavior(const ScrollAlignment& s) { return s.m_rectPartial; } - static ScrollBehavior getHiddenBehavior(const ScrollAlignment& s) { return s.m_rectHidden; } - - RenderLayer(RenderBox*); + RenderLayer(RenderBoxModelObject*); ~RenderLayer(); - RenderBox* renderer() const { return m_renderer; } + RenderBoxModelObject* renderer() const { return m_renderer; } + RenderBox* renderBox() const { return m_renderer && m_renderer->isBox() ? toRenderBox(m_renderer) : 0; } RenderLayer* parent() const { return m_parent; } RenderLayer* previousSibling() const { return m_previous; } RenderLayer* nextSibling() const { return m_next; } @@ -200,17 +183,25 @@ public: void repaintIncludingDescendants(); - void styleChanged(RenderStyle::Diff, const RenderStyle*); +#if USE(ACCELERATED_COMPOSITING) + // Indicate that the layer contents need to be repainted. Only has an effect + // if layer compositing is being used, + void setBackingNeedsRepaint(); + void setBackingNeedsRepaintInRect(const IntRect& r); // r is in the coordinate space of the layer's render object +#endif + + void styleChanged(StyleDifference, const RenderStyle*); RenderMarquee* marquee() const { return m_marquee; } void suspendMarquees(); - bool isOverflowOnly() const { return m_isOverflowOnly; } + bool isNormalFlowOnly() const { return m_isNormalFlowOnly; } + bool isSelfPaintingLayer() const; bool requiresSlowRepaints() const; bool isTransparent() const; - RenderLayer* transparentAncestor(); + RenderLayer* transparentPaintingAncestor(); void beginTransparencyLayers(GraphicsContext*, const RenderLayer* rootLayer); bool hasReflection() const { return renderer()->hasReflection(); } @@ -225,12 +216,12 @@ public: return curr; } - int xPos() const { return m_x; } - int yPos() const { return m_y; } - void setPos(int xPos, int yPos) + int x() const { return m_x; } + int y() const { return m_y; } + void setLocation(int x, int y) { - m_x = xPos; - m_y = yPos; + m_x = x; + m_y = y; } int width() const { return m_width; } @@ -255,9 +246,9 @@ public: void scrollToOffset(int x, int y, bool updateScrollbars = true, bool repaint = true); void scrollToXOffset(int x) { scrollToOffset(x, m_scrollY); } void scrollToYOffset(int y) { scrollToOffset(m_scrollX + m_scrollOriginX, y); } - void scrollRectToVisible(const IntRect&, bool scrollToAnchor = false, const ScrollAlignment& alignX = gAlignCenterIfNeeded, const ScrollAlignment& alignY = gAlignCenterIfNeeded); + void scrollRectToVisible(const IntRect&, bool scrollToAnchor = false, const ScrollAlignment& alignX = ScrollAlignment::alignCenterIfNeeded, const ScrollAlignment& alignY = ScrollAlignment::alignCenterIfNeeded); - IntRect getRectToExpose(const IntRect& visibleRect, const IntRect& exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY); + IntRect getRectToExpose(const IntRect& visibleRect, const IntRect& exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY); void setHasHorizontalScrollbar(bool); void setHasVerticalScrollbar(bool); @@ -288,6 +279,16 @@ public: void resize(const PlatformMouseEvent&, const IntSize&); bool inResizeMode() const { return m_inResizeMode; } void setInResizeMode(bool b) { m_inResizeMode = b; } + + bool isRootLayer() const { return renderer()->isRenderView(); } + +#if USE(ACCELERATED_COMPOSITING) + RenderLayerCompositor* compositor() const; + + // Notification from the renderer that its content changed (e.g. current frame of image changed). + // Allows updates of layer content without repainting. + void rendererContentChanged(); +#endif void updateLayerPosition(); void updateLayerPositions(bool doFullRepaint = false, bool checkForRepaint = true); @@ -311,9 +312,9 @@ public: Vector<RenderLayer*>* posZOrderList() const { return m_posZOrderList; } Vector<RenderLayer*>* negZOrderList() const { return m_negZOrderList; } - void dirtyOverflowList(); - void updateOverflowList(); - Vector<RenderLayer*>* overflowList() const { return m_overflowList; } + void dirtyNormalFlowList(); + void updateNormalFlowList(); + Vector<RenderLayer*>* normalFlowList() const { return m_normalFlowList; } bool hasVisibleContent() const { return m_hasVisibleContent; } void setHasVisibleContent(bool); @@ -323,6 +324,13 @@ public: // the <html> layer and the root layer). RenderLayer* enclosingPositionedAncestor() const; +#if USE(ACCELERATED_COMPOSITING) + // Enclosing compositing layer; if includeSelf is true, may return this. + RenderLayer* enclosingCompositingLayer(bool includeSelf = true) const; + // Ancestor compositing layer, excluding this. + RenderLayer* ancestorCompositingLayer() const { return enclosingCompositingLayer(false); } +#endif + void convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const; bool hasAutoZIndex() const { return renderer()->style()->hasAutoZIndex(); } @@ -353,23 +361,39 @@ public: bool intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect, const RenderLayer* rootLayer) const; - // Returns a bounding box for this layer only. + // Bounding box relative to some ancestor layer. IntRect boundingBox(const RenderLayer* rootLayer) const; + // Bounding box in the coordinates of this layer. + IntRect localBoundingBox() const; + // Bounding box relative to the root. + IntRect absoluteBoundingBox() const; void updateHoverActiveState(const HitTestRequest&, HitTestResult&); + // Return a cached repaint rect, computed relative to the layer renderer's containerForRepaint. IntRect repaintRect() const { return m_repaintRect; } void setNeedsFullRepaint(bool f = true) { m_needsFullRepaint = f; } int staticX() const { return m_staticX; } int staticY() const { return m_staticY; } void setStaticX(int staticX) { m_staticX = staticX; } - void setStaticY(int staticY) { m_staticY = staticY; } + void setStaticY(int staticY); bool hasTransform() const { return renderer()->hasTransform(); } + // Note that this transform has the transform-origin baked in. TransformationMatrix* transform() const { return m_transform.get(); } - - void destroy(RenderArena*); + // currentTransform computes a transform which takes accelerated animations into account. The + // resulting transform has transform-origin baked in. If the layer does not have a transform, + // returns the identity matrix. + TransformationMatrix currentTransform() const; + + // Get the perspective transform, which is applied to transformed sublayers. + // Returns true if the layer has a -webkit-perspective. + // Note that this transform has the perspective-origin baked in. + TransformationMatrix perspectiveTransform() const; + FloatPoint perspectiveOrigin() const; + bool preserves3D() const { return renderer()->style()->transformStyle3D() == TransformStyle3DPreserve3D; } + bool has3DTransform() const { return m_transform && !m_transform->isAffine(); } // Overloaded new operator. Derived classes must override operator new // in order to allocate out of the RenderArena. @@ -378,6 +402,25 @@ public: // Overridden to prevent the normal delete from being called. void operator delete(void*, size_t); +#if USE(ACCELERATED_COMPOSITING) + bool isComposited() const { return m_backing != 0; } + RenderLayerBacking* backing() const { return m_backing.get(); } + RenderLayerBacking* ensureBacking(); + void clearBacking(); +#else + bool isComposited() const { return false; } +#endif + + bool paintsWithTransparency() const + { + return isTransparent() && !isComposited(); + } + + bool paintsWithTransform() const + { + return transform() && !isComposited(); + } + private: // The normal operator new is disallowed on all render objects. void* operator new(size_t) throw(); @@ -385,19 +428,34 @@ private: private: void setNextSibling(RenderLayer* next) { m_next = next; } void setPreviousSibling(RenderLayer* prev) { m_previous = prev; } - void setParent(RenderLayer* parent) { m_parent = parent; } + void setParent(RenderLayer* parent); void setFirstChild(RenderLayer* first) { m_first = first; } void setLastChild(RenderLayer* last) { m_last = last; } + int renderBoxX() const { return renderer()->isBox() ? toRenderBox(renderer())->x() : 0; } + int renderBoxY() const { return renderer()->isBox() ? toRenderBox(renderer())->y() : 0; } + void collectLayers(Vector<RenderLayer*>*&, Vector<RenderLayer*>*&); + void updateLayerListsIfNeeded(); + void paintLayer(RenderLayer* rootLayer, GraphicsContext*, const IntRect& paintDirtyRect, bool haveTransparency, PaintRestriction, RenderObject* paintingRoot, bool appliedTransform = false, bool temporaryClipRects = false); - RenderLayer* hitTestLayer(RenderLayer* rootLayer, const HitTestRequest&, HitTestResult&, const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform = false); + + RenderLayer* hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform, + const HitTestingTransformState* transformState = 0, double* zOffset = 0); + + PassRefPtr<HitTestingTransformState> createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, + const HitTestingTransformState* containerTransformState) const; + + bool hitTestContents(const HitTestRequest&, HitTestResult&, const IntRect& layerBounds, const IntPoint& hitTestPoint, HitTestFilter) const; + void computeScrollDimensions(bool* needHBar = 0, bool* needVBar = 0); - bool shouldBeOverflowOnly() const; + bool shouldBeNormalFlowOnly() const; virtual void valueChanged(Scrollbar*); virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&); @@ -410,11 +468,21 @@ private: void dirtyVisibleDescendantStatus(); void updateVisibilityStatus(); + // This flag is computed by RenderLayerCompositor, which knows more about 3d hierarchies than we do. + void setHas3DTransformedDescendant(bool b) { m_has3DTransformedDescendant = b; } + bool has3DTransformedDescendant() const { return m_has3DTransformedDescendant; } + + void dirty3DTransformedDescendantStatus(); + // Both updates the status, and returns true if descendants of this have 3d. + bool update3DTransformedDescendantStatus(); + Node* enclosingElement() const; void createReflection(); void updateReflectionStyle(); bool paintingInsideReflection() const { return m_paintingInsideReflection; } + + void parentClipRects(const RenderLayer* rootLayer, ClipRects&, bool temporaryClipRects = false) const; RenderLayer* enclosingTransformedAncestor() const; @@ -424,8 +492,24 @@ private: void updateScrollCornerStyle(); void updateResizerStyle(); -protected: - RenderBox* m_renderer; +#if USE(ACCELERATED_COMPOSITING) + bool hasCompositingDescendant() const { return m_hasCompositingDescendant; } + void setHasCompositingDescendant(bool b) { m_hasCompositingDescendant = b; } + + bool mustOverlayCompositedLayers() const { return m_mustOverlayCompositedLayers; } + void setMustOverlayCompositedLayers(bool b) { m_mustOverlayCompositedLayers = b; } +#endif + +private: + friend class RenderLayerBacking; + friend class RenderLayerCompositor; + friend class RenderBoxModelObject; + + // Only safe to call from RenderBoxModelObject::destroyLayer(RenderArena*) + void destroy(RenderArena*); + +protected: + RenderBoxModelObject* m_renderer; RenderLayer* m_parent; RenderLayer* m_previous; @@ -474,7 +558,7 @@ protected: // This list contains child layers that cannot create stacking contexts. For now it is just // overflow layers, but that may change in the future. - Vector<RenderLayer*>* m_overflowList; + Vector<RenderLayer*>* m_normalFlowList; ClipRects* m_clipRects; // Cached clip rects used when painting and hit testing. #ifndef NDEBUG @@ -483,8 +567,8 @@ protected: bool m_scrollDimensionsDirty : 1; bool m_zOrderListsDirty : 1; - bool m_overflowListDirty: 1; - bool m_isOverflowOnly : 1; + bool m_normalFlowListDirty: 1; + bool m_isNormalFlowOnly : 1; bool m_usedTransparency : 1; // Tracks whether we need to close a transparent layer, i.e., whether // we ended up painting this layer or any descendants (and therefore need to @@ -501,6 +585,14 @@ protected: bool m_visibleDescendantStatusDirty : 1; bool m_hasVisibleDescendant : 1; + bool m_3DTransformedDescendantStatusDirty : 1; + bool m_has3DTransformedDescendant : 1; // Set on a stacking context layer that has 3D descendants anywhere + // in a preserves3D hierarchy. Hint to do 3D-aware hit testing. +#if USE(ACCELERATED_COMPOSITING) + bool m_hasCompositingDescendant : 1; + bool m_mustOverlayCompositedLayers : 1; +#endif + RenderMarquee* m_marquee; // Used by layers with overflow:marquee // Cached normal flow values for absolute positioned elements with static left/top values. @@ -515,6 +607,10 @@ protected: // Renderers to hold our custom scroll corner and resizer. RenderScrollbarPart* m_scrollCorner; RenderScrollbarPart* m_resizer; + +#if USE(ACCELERATED_COMPOSITING) + OwnPtr<RenderLayerBacking> m_backing; +#endif }; } // namespace WebCore diff --git a/WebCore/rendering/RenderLayerBacking.cpp b/WebCore/rendering/RenderLayerBacking.cpp new file mode 100644 index 0000000..40805b5 --- /dev/null +++ b/WebCore/rendering/RenderLayerBacking.cpp @@ -0,0 +1,1067 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AnimationController.h" +#include "CSSPropertyNames.h" +#include "CSSStyleSelector.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "GraphicsLayer.h" +#include "HTMLNames.h" +#include "RenderBox.h" +#include "RenderImage.h" +#include "RenderLayerCompositor.h" +#include "RenderVideo.h" +#include "RenderView.h" + +#include "RenderLayerBacking.h" + +using namespace std; + +namespace WebCore { + +RenderLayerBacking::RenderLayerBacking(RenderLayer* layer) + : m_owningLayer(layer) + , m_ancestorClippingLayer(0) + , m_graphicsLayer(0) + , m_contentsLayer(0) + , m_clippingLayer(0) + , m_isSimpleContainerCompositingLayer(false) + , m_simpleCompositingLayerStatusDirty(true) + , m_compositingContentOffsetDirty(true) +{ + createGraphicsLayer(); +} + +RenderLayerBacking::~RenderLayerBacking() +{ + updateClippingLayers(false, false); + updateContentsLayer(false); + destroyGraphicsLayer(); +} + +void RenderLayerBacking::createGraphicsLayer() +{ + m_graphicsLayer = GraphicsLayer::createGraphicsLayer(this); + +#ifndef NDEBUG + if (renderer()->node()->isDocumentNode()) + m_graphicsLayer->setName("Document Node"); + else { + if (renderer()->node()->isHTMLElement() && renderer()->node()->hasID()) + m_graphicsLayer->setName(renderer()->renderName() + String(" ") + static_cast<HTMLElement*>(renderer()->node())->id()); + else + m_graphicsLayer->setName(renderer()->renderName()); + } +#endif // NDEBUG + + updateLayerOpacity(); + updateLayerTransform(); +} + +void RenderLayerBacking::destroyGraphicsLayer() +{ + if (m_graphicsLayer) + m_graphicsLayer->removeFromParent(); + + delete m_graphicsLayer; + m_graphicsLayer = 0; + + delete m_contentsLayer; + m_contentsLayer = 0; + + delete m_clippingLayer; + m_clippingLayer = 0; +} + +void RenderLayerBacking::updateLayerOpacity() +{ + m_graphicsLayer->setOpacity(compositingOpacity(renderer()->opacity()), 0, 0); +} + +void RenderLayerBacking::updateLayerTransform() +{ + RenderStyle* style = renderer()->style(); + + // FIXME: This could use m_owningLayer->transform(), but that currently has transform-origin + // baked into it, and we don't want that. + TransformationMatrix t; + if (m_owningLayer->hasTransform()) { + style->applyTransform(t, toRenderBox(renderer())->borderBoxRect().size(), RenderStyle::ExcludeTransformOrigin); + makeMatrixRenderable(t); + } + + m_graphicsLayer->setTransform(t); +} + +void RenderLayerBacking::updateAfterLayout() +{ + invalidateDrawingOptimizations(); + detectDrawingOptimizations(); + + updateGraphicsLayerGeometry(); +} + +bool RenderLayerBacking::updateGraphicsLayers(bool needsContentsLayer, bool needsUpperClippingLayer, bool needsLowerClippingLayer, bool needsRepaint) +{ + bool layerConfigChanged = false; + if (updateContentsLayer(needsContentsLayer)) + layerConfigChanged = true; + + if (updateClippingLayers(needsUpperClippingLayer, needsLowerClippingLayer)) + layerConfigChanged = true; + + // See if we can now use any drawing optimizations. + bool didDrawContent = graphicsLayer()->drawsContent(); + invalidateDrawingOptimizations(); + detectDrawingOptimizations(); + if (!didDrawContent && graphicsLayer()->drawsContent()) + needsRepaint = true; + + // Set opacity, if it is not animating. + if (!renderer()->animation()->isAnimatingPropertyOnRenderer(renderer(), CSSPropertyOpacity)) + updateLayerOpacity(); + + RenderStyle* style = renderer()->style(); + m_graphicsLayer->setPreserves3D(style->transformStyle3D() == TransformStyle3DPreserve3D); + m_graphicsLayer->setBackfaceVisibility(style->backfaceVisibility() == BackfaceVisibilityVisible); + + updateGraphicsLayerGeometry(); + + m_graphicsLayer->updateContentsRect(); + + if (needsRepaint) { + m_graphicsLayer->setNeedsDisplay(); + if (m_contentsLayer) + m_contentsLayer->setNeedsDisplay(); + } + + return layerConfigChanged; +} + +void RenderLayerBacking::updateGraphicsLayerGeometry() +{ + // If we haven't built z-order lists yet, wait until later. + if (m_owningLayer->isStackingContext() && m_owningLayer->m_zOrderListsDirty) + return; + + // Set transform property, if it is not animating. We have to do this here because the transform + // is affected by the layer dimensions. + if (!renderer()->animation()->isAnimatingPropertyOnRenderer(renderer(), CSSPropertyWebkitTransform)) + updateLayerTransform(); + + m_compositingContentOffsetDirty = true; + + RenderLayer* compAncestor = m_owningLayer->ancestorCompositingLayer(); + + // We compute everything relative to the enclosing compositing layer. + IntRect ancestorCompositingBounds; + if (compAncestor) + ancestorCompositingBounds = compositor()->calculateCompositedBounds(compAncestor, compAncestor); + + IntRect localCompositingBounds = compositor()->calculateCompositedBounds(m_owningLayer, m_owningLayer); + + IntRect relativeCompositingBounds(localCompositingBounds); + int deltaX = 0, deltaY = 0; + m_owningLayer->convertToLayerCoords(compAncestor, deltaX, deltaY); + relativeCompositingBounds.move(deltaX, deltaY); + + IntPoint graphicsLayerParentLocation; + if (compAncestor && compAncestor->backing()->hasClippingLayer()) { + // If the compositing ancestor has a layer to clip children, we parent in that, and therefore + // position relative to it. + graphicsLayerParentLocation = toRenderBox(compAncestor->renderer())->overflowClipRect(0, 0).location(); + } else + graphicsLayerParentLocation = ancestorCompositingBounds.location(); + + if (compAncestor && m_ancestorClippingLayer) { + // Call calculateRects to get the backgroundRect which is what is used to clip the contents of this + // layer. Note that we call it with temporaryClipRects = true because normally when computing clip rects + // for a compositing layer, rootLayer is the layer itself. + ClipRects parentRects; + m_owningLayer->parentClipRects(compAncestor, parentRects, true); + IntRect parentClipRect = parentRects.overflowClipRect(); + + m_ancestorClippingLayer->setPosition(FloatPoint() + (parentClipRect.location() - graphicsLayerParentLocation)); + m_ancestorClippingLayer->setSize(parentClipRect.size()); + + // backgroundRect is relative to compAncestor, so subtract deltaX/deltaY to get back to local coords. + IntSize rendererOffset(parentClipRect.location().x() - deltaX, parentClipRect.location().y() - deltaY); + m_ancestorClippingLayer->setOffsetFromRenderer(rendererOffset); + + // The primary layer is then parented in, and positioned relative to this clipping layer. + graphicsLayerParentLocation = parentClipRect.location(); + } + + m_graphicsLayer->setPosition(FloatPoint() + (relativeCompositingBounds.location() - graphicsLayerParentLocation)); + m_graphicsLayer->setOffsetFromRenderer(localCompositingBounds.location() - IntPoint()); + + FloatSize oldSize = m_graphicsLayer->size(); + FloatSize newSize = relativeCompositingBounds.size(); + if (oldSize != newSize) { + m_graphicsLayer->setSize(newSize); + // A bounds change will almost always require redisplay. Usually that redisplay + // will happen because of a repaint elsewhere, but not always: + // e.g. see RenderView::setMaximalOutlineSize() + m_graphicsLayer->setNeedsDisplay(); + } + + // If we have a layer that clips children, position it. + if (m_clippingLayer) { + IntRect clippingBox = toRenderBox(renderer())->overflowClipRect(0, 0); + m_clippingLayer->setPosition(FloatPoint() + (clippingBox.location() - localCompositingBounds.location())); + m_clippingLayer->setSize(clippingBox.size()); + m_clippingLayer->setOffsetFromRenderer(clippingBox.location() - IntPoint()); + } + + if (m_owningLayer->hasTransform()) { + const IntRect borderBox = toRenderBox(renderer())->borderBoxRect(); + + IntRect layerBounds = IntRect(m_owningLayer->x(), m_owningLayer->y(), borderBox.width(), borderBox.height()); + // Convert to absolute coords to match bbox. + int x = 0, y = 0; + m_owningLayer->convertToLayerCoords(compAncestor, x, y); + layerBounds.move(x - m_owningLayer->x(), y - m_owningLayer->y()); + + // Update properties that depend on layer dimensions + FloatPoint3D transformOrigin = computeTransformOrigin(borderBox); + // Compute the anchor point, which is in the center of the renderer box unless transform-origin is set. + FloatPoint3D anchor(relativeCompositingBounds.width() != 0.0f ? ((layerBounds.x() - relativeCompositingBounds.x()) + transformOrigin.x()) / relativeCompositingBounds.width() : 0.5f, + relativeCompositingBounds.height() != 0.0f ? ((layerBounds.y() - relativeCompositingBounds.y()) + transformOrigin.y()) / relativeCompositingBounds.height() : 0.5f, + transformOrigin.z()); + m_graphicsLayer->setAnchorPoint(anchor); + + RenderStyle* style = renderer()->style(); + if (style->hasPerspective()) { + TransformationMatrix t = owningLayer()->perspectiveTransform(); + + if (m_clippingLayer) { + m_clippingLayer->setChildrenTransform(t); + m_graphicsLayer->setChildrenTransform(TransformationMatrix()); + } + else + m_graphicsLayer->setChildrenTransform(t); + } else { + if (m_clippingLayer) + m_clippingLayer->setChildrenTransform(TransformationMatrix()); + else + m_graphicsLayer->setChildrenTransform(TransformationMatrix()); + } + } else { + m_graphicsLayer->setAnchorPoint(FloatPoint3D(0.5f, 0.5f, 0)); + } + + if (m_contentsLayer) { + // The contents layer is always coincidental with the graphicsLayer for now. + m_contentsLayer->setPosition(IntPoint(0, 0)); + m_contentsLayer->setSize(newSize); + m_contentsLayer->setOffsetFromRenderer(m_graphicsLayer->offsetFromRenderer()); + } + + m_graphicsLayer->updateContentsRect(); +} + +void RenderLayerBacking::updateInternalHierarchy() +{ + // m_contentsLayer has to be inserted in the correct order with child layers, + // so it's not inserted here. + if (m_ancestorClippingLayer) { + m_ancestorClippingLayer->removeAllChildren(); + m_graphicsLayer->removeFromParent(); + m_ancestorClippingLayer->addChild(m_graphicsLayer); + } + + if (m_clippingLayer) { + m_clippingLayer->removeFromParent(); + m_graphicsLayer->addChild(m_clippingLayer); + } +} + +// Return true if the layers changed. +bool RenderLayerBacking::updateClippingLayers(bool needsAncestorClip, bool needsDescendantClip) +{ + bool layersChanged = false; + + if (needsAncestorClip) { + if (!m_ancestorClippingLayer) { + m_ancestorClippingLayer = GraphicsLayer::createGraphicsLayer(this); +#ifndef NDEBUG + m_ancestorClippingLayer->setName("Ancestor clipping Layer"); +#endif + m_ancestorClippingLayer->setMasksToBounds(true); + layersChanged = true; + } + } else if (m_ancestorClippingLayer) { + m_ancestorClippingLayer->removeFromParent(); + delete m_ancestorClippingLayer; + m_ancestorClippingLayer = 0; + layersChanged = true; + } + + if (needsDescendantClip) { + if (!m_clippingLayer) { + m_clippingLayer = GraphicsLayer::createGraphicsLayer(0); +#ifndef NDEBUG + m_clippingLayer->setName("Child clipping Layer"); +#endif + m_clippingLayer->setMasksToBounds(true); + layersChanged = true; + } + } else if (m_clippingLayer) { + m_clippingLayer->removeFromParent(); + delete m_clippingLayer; + m_clippingLayer = 0; + layersChanged = true; + } + + if (layersChanged) + updateInternalHierarchy(); + + return layersChanged; +} + +bool RenderLayerBacking::updateContentsLayer(bool needsContentsLayer) +{ + bool layerChanged = false; + if (needsContentsLayer) { + if (!m_contentsLayer) { + m_contentsLayer = GraphicsLayer::createGraphicsLayer(this); +#ifndef NDEBUG + m_contentsLayer->setName("Contents"); +#endif + m_contentsLayer->setDrawsContent(true); + m_contentsLayer->setDrawingPhase(GraphicsLayerPaintForegroundMask); + m_graphicsLayer->setDrawingPhase(GraphicsLayerPaintBackgroundMask); + layerChanged = true; + } + } else if (m_contentsLayer) { + m_contentsLayer->removeFromParent(); + delete m_contentsLayer; + m_contentsLayer = 0; + m_graphicsLayer->setDrawingPhase(GraphicsLayerPaintAllMask); + layerChanged = true; + } + return layerChanged; +} + +float RenderLayerBacking::compositingOpacity(float rendererOpacity) const +{ + float finalOpacity = rendererOpacity; + + for (RenderLayer* curr = m_owningLayer->parent(); curr; curr = curr->parent()) { + // We only care about parents that are stacking contexts. + // Recall that opacity creates stacking context. + if (!curr->isStackingContext()) + continue; + + // If we found a compositing layer, we want to compute opacity + // relative to it. So we can break here. + if (curr->isComposited()) + break; + + finalOpacity *= curr->renderer()->opacity(); + } + + return finalOpacity; +} + +static bool hasBorderOutlineOrShadow(const RenderStyle* style) +{ + return style->hasBorder() || style->hasBorderRadius() || style->hasOutline() || style->hasAppearance() || style->boxShadow(); +} + +static bool hasBoxDecorations(const RenderStyle* style) +{ + return hasBorderOutlineOrShadow(style) || style->hasBackground(); +} + +static bool hasBoxDecorationsWithBackgroundImage(const RenderStyle* style) +{ + return hasBorderOutlineOrShadow(style) || style->hasBackgroundImage(); +} + +bool RenderLayerBacking::rendererHasBackground() const +{ + // FIXME: share more code here + if (renderer()->node()->isDocumentNode()) { + RenderObject* htmlObject = renderer()->firstChild(); + if (!htmlObject) + return false; + + RenderStyle* style = htmlObject->style(); + if (style->hasBackground()) + return true; + + RenderObject* bodyObject = htmlObject->firstChild(); + if (!bodyObject) + return false; + + style = bodyObject->style(); + return style->hasBackground(); + } + + return renderer()->style()->hasBackground(); +} + +const Color& RenderLayerBacking::rendererBackgroundColor() const +{ + // FIXME: share more code here + if (renderer()->node()->isDocumentNode()) { + RenderObject* htmlObject = renderer()->firstChild(); + RenderStyle* style = htmlObject->style(); + if (style->hasBackground()) + return style->backgroundColor(); + + RenderObject* bodyObject = htmlObject->firstChild(); + style = bodyObject->style(); + return style->backgroundColor(); + } + + return renderer()->style()->backgroundColor(); +} + +bool RenderLayerBacking::canBeSimpleContainerCompositingLayer() const +{ + RenderObject* renderObject = renderer(); + if (renderObject->isReplaced() || // replaced objects are not containers + renderObject->hasMask()) // masks require special treatment + return false; + + RenderStyle* style = renderObject->style(); + + // 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)) + return false; + + // If we have got this far and the renderer has no children, then we're ok. + if (!renderObject->firstChild()) + return true; + + if (renderObject->node()->isDocumentNode()) { + // Look to see if the root object has a non-simple backgound + RenderObject* rootObject = renderObject->document()->documentElement()->renderer(); + if (!rootObject) + return false; + + style = rootObject->style(); + + // 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)) + return false; + + // Now look at the body's renderer. + HTMLElement* body = renderObject->document()->body(); + RenderObject* bodyObject = (body && body->hasLocalName(HTMLNames::bodyTag)) ? body->renderer() : 0; + if (!bodyObject) + return false; + + style = bodyObject->style(); + + if (hasBoxDecorationsWithBackgroundImage(style)) + return false; + + // Ceck to see if all the body's children are compositing layers. + if (hasNonCompositingContent()) + return false; + + return true; + } + + // Check to see if all the renderer's children are compositing layers. + if (hasNonCompositingContent()) + return false; + + return true; +} + +bool RenderLayerBacking::hasNonCompositingContent() const +{ + // Conservative test for having no rendered children. + + // Some HTML can cause whitespace text nodes to have renderers, like: + // <div> + // <img src=...> + // </div> + // so test for 0x0 RenderTexts here + for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) { + if (!child->hasLayer()) { + if (child->isRenderInline() || !child->isBox()) + return true; + + if (toRenderBox(child)->width() > 0 || toRenderBox(child)->height() > 0) + return true; + } + } + + // FIXME: test for overflow controls. + if (m_owningLayer->isStackingContext()) { + // Use the m_hasCompositingDescendant bit to optimize? + Vector<RenderLayer*>* negZOrderList = m_owningLayer->negZOrderList(); + if (negZOrderList && negZOrderList->size() > 0) { + for (Vector<RenderLayer*>::const_iterator it = negZOrderList->begin(); it != negZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + if (!curLayer->isComposited()) + return true; + } + } + + Vector<RenderLayer*>* posZOrderList = m_owningLayer->posZOrderList(); + if (posZOrderList && posZOrderList->size() > 0) { + for (Vector<RenderLayer*>::const_iterator it = posZOrderList->begin(); it != posZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + if (!curLayer->isComposited()) + return true; + } + } + } + + Vector<RenderLayer*>* normalFlowList = m_owningLayer->normalFlowList(); + if (normalFlowList && normalFlowList->size() > 0) { + for (Vector<RenderLayer*>::const_iterator it = normalFlowList->begin(); it != normalFlowList->end(); ++it) { + RenderLayer* curLayer = (*it); + if (!curLayer->isComposited()) + return true; + } + } + + 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 +{ + RenderObject* renderObject = renderer(); + + // Reject anything that isn't an image + if (!renderObject->isImage()) + return false; + + if (renderObject->hasMask() || renderObject->hasReflection()) + return false; + + // 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()); +} + +// A "simple container layer" is a RenderLayer which has no visible content to render. +// It may have no children, or all its children may be themselves composited. +// This is a useful optimization, because it allows us to avoid allocating backing store. +bool RenderLayerBacking::isSimpleContainerCompositingLayer() +{ + if (m_simpleCompositingLayerStatusDirty) { + m_isSimpleContainerCompositingLayer = canBeSimpleContainerCompositingLayer(); + m_simpleCompositingLayerStatusDirty = false; + } + + return m_isSimpleContainerCompositingLayer; +} + +void RenderLayerBacking::detectDrawingOptimizations() +{ + bool drawsContent = true; + + // Check if a replaced layer can be further simplified. + if (canUseDirectCompositing()) { + if (renderer()->isImage()) { + updateImageContents(); + drawsContent = false; + } + + if (rendererHasBackground()) + m_graphicsLayer->setBackgroundColor(rendererBackgroundColor()); + else + m_graphicsLayer->clearBackgroundColor(); + + } else { + m_graphicsLayer->clearBackgroundColor(); + m_graphicsLayer->clearContents(); + + if (isSimpleContainerCompositingLayer()) + drawsContent = false; + } + + if (paintingGoesToWindow()) + drawsContent = false; + + m_graphicsLayer->setDrawsContent(drawsContent); +} + +void RenderLayerBacking::invalidateDrawingOptimizations() +{ + m_simpleCompositingLayerStatusDirty = true; +} + +void RenderLayerBacking::rendererContentChanged() +{ + if (canUseDirectCompositing() && renderer()->isImage()) + updateImageContents(); +} + +void RenderLayerBacking::updateImageContents() +{ + ASSERT(renderer()->isImage()); + RenderImage* imageRenderer = static_cast<RenderImage*>(renderer()); + + CachedImage* cachedImage = imageRenderer->cachedImage(); + if (!cachedImage) + return; + + Image* image = cachedImage->image(); + if (!image) + return; + + // We have to wait until the image is fully loaded before setting it on the layer. + if (!cachedImage->isLoaded()) + return; + + // This is a no-op if the layer doesn't have an inner layer for the image. + m_graphicsLayer->setContentsToImage(image); + + // Image animation is "lazy", in that it automatically stops unless someone is drawing + // the image. So we have to kick the animation each time; this has the downside that the + // image will keep animating, even if its layer is not visible. + image->startAnimation(); +} + +FloatPoint3D RenderLayerBacking::computeTransformOrigin(const IntRect& borderBox) const +{ + RenderStyle* style = renderer()->style(); + + FloatPoint3D origin; + origin.setX(style->transformOriginX().calcFloatValue(borderBox.width())); + origin.setY(style->transformOriginY().calcFloatValue(borderBox.height())); + origin.setZ(style->transformOriginZ()); + + return origin; +} + +FloatPoint RenderLayerBacking::computePerspectiveOrigin(const IntRect& borderBox) const +{ + RenderStyle* style = renderer()->style(); + + float boxWidth = borderBox.width(); + float boxHeight = borderBox.height(); + + FloatPoint origin; + origin.setX(style->perspectiveOriginX().calcFloatValue(boxWidth)); + origin.setY(style->perspectiveOriginY().calcFloatValue(boxHeight)); + + return origin; +} + +// Return the offset from the top-left of this compositing layer at which the renderer's contents are painted. +IntSize RenderLayerBacking::contentOffsetInCompostingLayer() +{ + if (!m_compositingContentOffsetDirty) + return m_compositingContentOffset; + + IntRect relativeCompositingBounds = compositor()->calculateCompositedBounds(m_owningLayer, m_owningLayer); + m_compositingContentOffset = IntSize(-relativeCompositingBounds.x(), -relativeCompositingBounds.y()); + m_compositingContentOffsetDirty = false; + + return m_compositingContentOffset; +} + +IntRect RenderLayerBacking::contentsBox(const GraphicsLayer*) +{ + if (!renderer()->isBox()) + return IntRect(); + + IntRect contentsRect = toRenderBox(renderer())->contentBoxRect(); + IntSize contentOffset = contentOffsetInCompostingLayer(); + contentsRect.move(contentOffset); + return contentsRect; +} + +// Map the given point from coordinates in the GraphicsLayer to RenderLayer coordinates. +FloatPoint RenderLayerBacking::graphicsLayerToContentsCoordinates(const GraphicsLayer* graphicsLayer, const FloatPoint& point) +{ + return point + FloatSize(graphicsLayer->offsetFromRenderer()); +} + +// Map the given point from coordinates in the RenderLayer to GraphicsLayer coordinates. +FloatPoint RenderLayerBacking::contentsToGraphicsLayerCoordinates(const GraphicsLayer* graphicsLayer, const FloatPoint& point) +{ + return point - FloatSize(graphicsLayer->offsetFromRenderer()); +} + +bool RenderLayerBacking::paintingGoesToWindow() const +{ + return m_owningLayer->isRootLayer(); +} + +void RenderLayerBacking::setContentsNeedDisplay() +{ + if (m_graphicsLayer) + m_graphicsLayer->setNeedsDisplay(); + if (m_contentsLayer) + m_contentsLayer->setNeedsDisplay(); +} + +// r is in the coordinate space of the layer's render object +void RenderLayerBacking::setContentsNeedDisplayInRect(const IntRect& r) +{ + if (m_graphicsLayer) { + FloatPoint dirtyOrigin = contentsToGraphicsLayerCoordinates(m_graphicsLayer, FloatPoint(r.x(), r.y())); + FloatRect dirtyRect(dirtyOrigin, r.size()); + FloatRect bounds(FloatPoint(), m_graphicsLayer->size()); + if (bounds.intersects(dirtyRect)) + m_graphicsLayer->setNeedsDisplayInRect(dirtyRect); + } + + if (m_contentsLayer) { + // FIXME: do incremental repaint + m_contentsLayer->setNeedsDisplay(); + } +} + +static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) +{ + if (paintDirtyRect == clipRect) + return; + p->save(); + p->clip(clipRect); +} + +static void restoreClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) +{ + if (paintDirtyRect == clipRect) + return; + p->restore(); +} + +// Share this with RenderLayer::paintLayer, which would have to be educated about GraphicsLayerPaintingPhase? +void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* context, + const IntRect& paintDirtyRect, // in the coords of rootLayer + bool haveTransparency, PaintRestriction paintRestriction, GraphicsLayerPaintingPhase paintingPhase, + RenderObject* paintingRoot) +{ + if (paintingGoesToWindow()) { + ASSERT_NOT_REACHED(); + return; + } + + m_owningLayer->updateLayerListsIfNeeded(); + + // Calculate the clip rects we should use. + IntRect layerBounds, damageRect, clipRectToApply, outlineRect; + m_owningLayer->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect); + + int x = layerBounds.x(); // layerBounds is computed relative to rootLayer + int y = layerBounds.y(); + int tx = x - m_owningLayer->renderBoxX(); + int ty = y - m_owningLayer->renderBoxY(); + + // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which + // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set). + // Else, our renderer tree may or may not contain the painting root, so we pass that root along + // so it will be tested against as we decend through the renderers. + RenderObject *paintingRootForRenderer = 0; + if (paintingRoot && !renderer()->isDescendantOf(paintingRoot)) + paintingRootForRenderer = paintingRoot; + + if (paintingPhase & GraphicsLayerPaintBackgroundMask) { + // If this is the root then we need to send in a bigger bounding box + // because we'll be painting the background as well (see RenderBox::paintRootBoxDecorations()). + IntRect paintBox = clipRectToApply; + + // FIXME: do we need this code? + if (renderer()->node()->isDocumentNode() && renderer()->document()->isHTMLDocument()) { + RenderBox* box = toRenderBox(renderer()); + int w = box->width(); + int h = box->height(); + + int rw; + int rh; + if (box->view()->frameView()) { + rw = box->view()->frameView()->contentsWidth(); + rh = box->view()->frameView()->contentsHeight(); + } else { + rw = box->view()->width(); + rh = box->view()->height(); + } + + int bx = tx - box->marginLeft(); + int by = ty - box->marginTop(); + int bw = max(w + box->marginLeft() + box->marginRight() + box->borderLeft() + box->borderRight(), rw); + int bh = max(h + box->marginTop() + box->marginBottom() + box->borderTop() + box->borderBottom(), rh); + paintBox = IntRect(bx, by, bw, bh); + } + + // Paint our background first, before painting any child layers. + // Establish the clip used to paint our background. + setClip(context, paintDirtyRect, damageRect); + + RenderObject::PaintInfo info(context, paintBox, PaintPhaseBlockBackground, false, paintingRootForRenderer, 0); + renderer()->paint(info, tx, ty); + + // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with + // z-index. We paint after we painted the background/border, so that the scrollbars will + // sit above the background/border. + m_owningLayer->paintOverflowControls(context, x, y, damageRect); + + // Restore the clip. + restoreClip(context, paintDirtyRect, damageRect); + } + + if (paintingPhase & GraphicsLayerPaintForegroundMask) { + // Now walk the sorted list of children with negative z-indices. Only RenderLayers without compositing layers will paint. + // FIXME: should these be painted as background? + Vector<RenderLayer*>* negZOrderList = m_owningLayer->negZOrderList(); + if (negZOrderList) { + for (Vector<RenderLayer*>::iterator it = negZOrderList->begin(); it != negZOrderList->end(); ++it) + it[0]->paintLayer(rootLayer, context, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot); + } + + bool forceBlackText = paintRestriction == PaintRestrictionSelectionOnlyBlackText; + bool selectionOnly = paintRestriction == PaintRestrictionSelectionOnly || paintRestriction == PaintRestrictionSelectionOnlyBlackText; + + // Set up the clip used when painting our children. + setClip(context, paintDirtyRect, clipRectToApply); + RenderObject::PaintInfo paintInfo(context, clipRectToApply, + selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, + forceBlackText, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + + if (!selectionOnly) { + paintInfo.phase = PaintPhaseFloat; + renderer()->paint(paintInfo, tx, ty); + + paintInfo.phase = PaintPhaseForeground; + renderer()->paint(paintInfo, tx, ty); + + paintInfo.phase = PaintPhaseChildOutlines; + renderer()->paint(paintInfo, tx, ty); + } + + // Now restore our clip. + restoreClip(context, paintDirtyRect, clipRectToApply); + + if (!outlineRect.isEmpty()) { + // Paint our own outline + RenderObject::PaintInfo paintInfo(context, outlineRect, PaintPhaseSelfOutline, false, paintingRootForRenderer, 0); + setClip(context, paintDirtyRect, outlineRect); + renderer()->paint(paintInfo, tx, ty); + restoreClip(context, paintDirtyRect, outlineRect); + } + + // Paint any child layers that have overflow. + Vector<RenderLayer*>* normalFlowList = m_owningLayer->normalFlowList(); + if (normalFlowList) { + for (Vector<RenderLayer*>::iterator it = normalFlowList->begin(); it != normalFlowList->end(); ++it) + it[0]->paintLayer(rootLayer, context, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot); + } + + // Now walk the sorted list of children with positive z-indices. + Vector<RenderLayer*>* posZOrderList = m_owningLayer->posZOrderList(); + if (posZOrderList) { + for (Vector<RenderLayer*>::iterator it = posZOrderList->begin(); it != posZOrderList->end(); ++it) + it[0]->paintLayer(rootLayer, context, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot); + } + + if (renderer()->hasMask() && !selectionOnly && !damageRect.isEmpty()) { + setClip(context, paintDirtyRect, damageRect); + + // Paint the mask. + RenderObject::PaintInfo paintInfo(context, damageRect, PaintPhaseMask, false, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + + // Restore the clip. + restoreClip(context, paintDirtyRect, damageRect); + } + } + + ASSERT(!m_owningLayer->m_usedTransparency); +} + +// Up-call from compositing layer drawing callback. +void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase drawingPhase, const IntRect& clip) +{ + // We have to use the same root as for hit testing, because both methods + // can compute and cache clipRects. + IntRect enclosingBBox = compositor()->calculateCompositedBounds(m_owningLayer, m_owningLayer); + + IntRect clipRect(clip); + + // Set up the coordinate space to be in the layer's rendering coordinates. + context.translate(-enclosingBBox.x(), -enclosingBBox.y()); + + // Offset the clip. + clipRect.move(enclosingBBox.x(), enclosingBBox.y()); + + // The dirtyRect is in the coords of the painting root. + IntRect dirtyRect = enclosingBBox; + dirtyRect.intersect(clipRect); + + paintIntoLayer(m_owningLayer, &context, dirtyRect, false, PaintRestrictionNone, drawingPhase, renderer()); +} + +bool RenderLayerBacking::startAnimation(double beginTime, const Animation* anim, const KeyframeList& keyframes) +{ + bool hasOpacity = keyframes.containsProperty(CSSPropertyOpacity); + bool hasTransform = keyframes.containsProperty(CSSPropertyWebkitTransform); + + if (!hasOpacity && !hasTransform) + return false; + + GraphicsLayer::TransformValueList transformVector; + GraphicsLayer::FloatValueList opacityVector; + + for (Vector<KeyframeValue>::const_iterator it = keyframes.beginKeyframes(); it != keyframes.endKeyframes(); ++it) { + const RenderStyle* keyframeStyle = it->style(); + float key = it->key(); + + if (!keyframeStyle) + continue; + + // get timing function + const TimingFunction* tf = keyframeStyle->hasAnimations() ? &((*keyframeStyle->animations()).animation(0)->timingFunction()) : 0; + + if (hasTransform) + transformVector.insert(key, &(keyframeStyle->transform()), tf); + + if (hasOpacity) + opacityVector.insert(key, keyframeStyle->opacity(), tf); + } + + bool didAnimateTransform = !hasTransform; + bool didAnimateOpacity = !hasOpacity; + + if (hasTransform && m_graphicsLayer->animateTransform(transformVector, toRenderBox(renderer())->borderBoxRect().size(), anim, beginTime, false)) + didAnimateTransform = true; + + if (hasOpacity && m_graphicsLayer->animateFloat(AnimatedPropertyOpacity, opacityVector, anim, beginTime)) + didAnimateOpacity = true; + + return didAnimateTransform && didAnimateOpacity; +} + +bool RenderLayerBacking::startTransition(double beginTime, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle) +{ + bool didAnimate = false; + ASSERT(property != cAnimateAll); + + if (property == (int)CSSPropertyOpacity) { + const Animation* opacityAnim = toStyle->transitionForProperty(CSSPropertyOpacity); + if (opacityAnim && !opacityAnim->isEmptyOrZeroDuration()) { + // If beginTime is not 0, we are restarting this transition, so first set the from value + // in case it was smashed by a previous animation. + if (beginTime > 0) + m_graphicsLayer->setOpacity(compositingOpacity(fromStyle->opacity()), 0, 0); + + if (m_graphicsLayer->setOpacity(compositingOpacity(toStyle->opacity()), opacityAnim, beginTime)) + didAnimate = true; + } + } + + if (property == (int)CSSPropertyWebkitTransform && m_owningLayer->hasTransform()) { + // We get a TransformOperation, which is a linked list of primitive operations and their arguments. + // Arguments can be floats or Length values, which need to be converted to numbers using + // val.calcFloatValue(renderer()->width()) (or height()). + const Animation* transformAnim = toStyle->transitionForProperty(CSSPropertyWebkitTransform); + if (transformAnim && !transformAnim->isEmptyOrZeroDuration()) { + GraphicsLayer::TransformValueList transformVector; + transformVector.insert(0, &fromStyle->transform(), 0); + transformVector.insert(1, &toStyle->transform(), 0); + if (m_graphicsLayer->animateTransform(transformVector, toRenderBox(renderer())->borderBoxRect().size(), transformAnim, beginTime, true)) + didAnimate = true; + } + } + + return didAnimate; +} + +void RenderLayerBacking::notifyAnimationStarted(const GraphicsLayer*, double time) +{ + renderer()->animation()->notifyAnimationStarted(renderer(), time); +} + +void RenderLayerBacking::animationFinished(const String& name, int index, bool reset) +{ + m_graphicsLayer->removeFinishedAnimations(name, index, reset); +} + +void RenderLayerBacking::transitionFinished(int property) +{ + AnimatedPropertyID animatedProperty = cssToGraphicsLayerProperty(property); + if (animatedProperty != AnimatedPropertyInvalid) + m_graphicsLayer->removeFinishedTransitions(animatedProperty); +} + +void RenderLayerBacking::suspendAnimations() +{ + m_graphicsLayer->suspendAnimations(); +} + +void RenderLayerBacking::resumeAnimations() +{ + m_graphicsLayer->resumeAnimations(); +} + +int RenderLayerBacking::graphicsLayerToCSSProperty(AnimatedPropertyID property) +{ + int cssProperty = CSSPropertyInvalid; + switch (property) { + case AnimatedPropertyWebkitTransform: + cssProperty = CSSPropertyWebkitTransform; + break; + case AnimatedPropertyOpacity: + cssProperty = CSSPropertyOpacity; + break; + case AnimatedPropertyBackgroundColor: + cssProperty = CSSPropertyBackgroundColor; + break; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + } + return cssProperty; +} + +AnimatedPropertyID RenderLayerBacking::cssToGraphicsLayerProperty(int cssProperty) +{ + switch (cssProperty) { + case CSSPropertyWebkitTransform: + return AnimatedPropertyWebkitTransform; + case CSSPropertyOpacity: + return AnimatedPropertyOpacity; + case CSSPropertyBackgroundColor: + return AnimatedPropertyBackgroundColor; + // It's fine if we see other css properties here; they are just not accelerated. + } + return AnimatedPropertyInvalid; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/rendering/RenderLayerBacking.h b/WebCore/rendering/RenderLayerBacking.h new file mode 100644 index 0000000..46b81ad --- /dev/null +++ b/WebCore/rendering/RenderLayerBacking.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderLayerBacking_h +#define RenderLayerBacking_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "FloatPoint.h" +#include "FloatPoint3D.h" +#include "GraphicsLayer.h" +#include "GraphicsLayerClient.h" +#include "RenderLayer.h" +#include "TransformationMatrix.h" + +namespace WebCore { + +class KeyframeList; +class RenderLayerCompositor; + +// RenderLayerBacking controls the compositing behavior for a single RenderLayer. +// It holds the various GraphicsLayers, and makes decisions about intra-layer rendering +// optimizations. +// +// There is one RenderLayerBacking for each RenderLayer that is composited. + +class RenderLayerBacking : public GraphicsLayerClient { +public: + RenderLayerBacking(RenderLayer*); + ~RenderLayerBacking(); + + RenderLayer* owningLayer() const { return m_owningLayer; } + + void updateAfterLayout(); + + // Returns true if layer configuration changed. + bool updateGraphicsLayers(bool needsContentsLayer, bool needsUpperClippingLayer, bool needsLowerClippingLayer, bool needsRepaint); + void updateGraphicsLayerGeometry(); + void updateInternalHierarchy(); + + GraphicsLayer* graphicsLayer() const { return m_graphicsLayer; } + + // Layer to clip children + bool hasClippingLayer() const { return m_clippingLayer != 0; } + GraphicsLayer* clippingLayer() const { return m_clippingLayer; } + + // Layer to get clipped by ancestor + bool hasAncestorClippingLayer() const { return m_ancestorClippingLayer != 0; } + GraphicsLayer* ancestorClippingLayer() const { return m_ancestorClippingLayer; } + + bool hasContentsLayer() const { return m_contentsLayer != 0; } + GraphicsLayer* contentsLayer() const { return m_contentsLayer; } + + GraphicsLayer* parentForSublayers() const { return m_clippingLayer ? m_clippingLayer : m_graphicsLayer; } + GraphicsLayer* childForSuperlayers() const { return m_ancestorClippingLayer ? m_ancestorClippingLayer : m_graphicsLayer; } + + // RenderLayers with backing normally short-circuit paintLayer() because + // their content is rendered via callbacks from GraphicsLayer. However, the document + // layer is special, because it has a GraphicsLayer to act as a container for the GraphicsLayers + // for descendants, but its contents usually render into the window (in which case this returns true). + // This returns false for other layers, and when the document layer actually needs to paint into its backing store + // for some reason. + bool paintingGoesToWindow() const; + + void setContentsNeedDisplay(); + // r is in the coordinate space of the layer's render object + void setContentsNeedDisplayInRect(const IntRect& r); + + // Notification from the renderer that its content changed; used by RenderImage. + 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); + void animationFinished(const String& name, int index, bool reset); + void transitionFinished(int property); + + void suspendAnimations(); + void resumeAnimations(); + + FloatPoint graphicsLayerToContentsCoordinates(const GraphicsLayer*, const FloatPoint&); + FloatPoint contentsToGraphicsLayerCoordinates(const GraphicsLayer*, const FloatPoint&); + + void detectDrawingOptimizations(); + void invalidateDrawingOptimizations(); + + // GraphicsLayerClient interface + virtual void notifyAnimationStarted(const GraphicsLayer*, double startTime); + + virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& clip); + + virtual IntRect contentsBox(const GraphicsLayer*); + +private: + void createGraphicsLayer(); + void destroyGraphicsLayer(); + + RenderBoxModelObject* renderer() const { return m_owningLayer->renderer(); } + RenderLayerCompositor* compositor() const { return m_owningLayer->compositor(); } + + bool updateClippingLayers(bool needsAncestorClip, bool needsDescendantClip); + bool updateContentsLayer(bool needsContentsLayer); + + IntSize contentOffsetInCompostingLayer(); + // Result is transform origin in pixels. + FloatPoint3D computeTransformOrigin(const IntRect& borderBox) const; + // Result is perspective origin in pixels. + FloatPoint computePerspectiveOrigin(const IntRect& borderBox) const; + + void updateLayerOpacity(); + void updateLayerTransform(); + + // 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). + bool isSimpleContainerCompositingLayer(); + // Returns true if we can optimize the RenderLayer to draw the replaced content + // directly into a compositing buffer + bool canUseDirectCompositing() const; + void updateImageContents(); + + bool rendererHasBackground() const; + const Color& rendererBackgroundColor() const; + + bool canBeSimpleContainerCompositingLayer() const; + bool hasNonCompositingContent() const; + + void paintIntoLayer(RenderLayer* rootLayer, GraphicsContext*, const IntRect& paintDirtyRect, + bool haveTransparency, PaintRestriction paintRestriction, GraphicsLayerPaintingPhase, RenderObject* paintingRoot); + + static int graphicsLayerToCSSProperty(AnimatedPropertyID); + static AnimatedPropertyID cssToGraphicsLayerProperty(int); + +private: + RenderLayer* m_owningLayer; + + GraphicsLayer* m_ancestorClippingLayer; // only used if we are clipped by an ancestor which is not a stacking context + GraphicsLayer* m_graphicsLayer; + GraphicsLayer* m_contentsLayer; // only used in cases where we need to draw the foreground separately + GraphicsLayer* m_clippingLayer; // only used if we have clipping on a stacking context, with compositing children + + IntSize m_compositingContentOffset; + + bool m_isSimpleContainerCompositingLayer : 1; // is this compositing layer able to be simplified + bool m_simpleCompositingLayerStatusDirty : 1; // set if the test for simple layers needs to be redone + + bool m_compositingContentOffsetDirty: 1; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // RenderLayerBacking_h diff --git a/WebCore/rendering/RenderLayerCompositor.cpp b/WebCore/rendering/RenderLayerCompositor.cpp new file mode 100644 index 0000000..cbb3df7 --- /dev/null +++ b/WebCore/rendering/RenderLayerCompositor.cpp @@ -0,0 +1,803 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerCompositor.h" + +#include "AnimationController.h" +#include "ChromeClient.h" +#include "CSSPropertyNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsLayer.h" +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "Page.h" +#include "RenderLayerBacking.h" +#include "RenderView.h" + +#if PROFILE_LAYER_REBUILD +#include <wtf/CurrentTime.h> +#endif + +#ifndef NDEBUG +#include "CString.h" +#include "RenderTreeAsText.h" +#endif + +#if ENABLE(3D_RENDERING) +// This symbol is used to determine from a script whether 3D rendering is enabled (via 'nm'). +bool WebCoreHas3DRendering = true; +#endif + +namespace WebCore { + +struct CompositingState { + CompositingState(RenderLayer* compAncestor) + : m_subtreeIsCompositing(false) + , m_compositingAncestor(compAncestor) +#ifndef NDEBUG + , m_depth(0) +#endif + { + } + + bool m_subtreeIsCompositing; + RenderLayer* m_compositingAncestor; +#ifndef NDEBUG + int m_depth; +#endif +}; + +static TransformationMatrix flipTransform() +{ + TransformationMatrix flipper; + flipper.flipY(); + return flipper; +} + +RenderLayerCompositor::RenderLayerCompositor(RenderView* renderView) + : m_renderView(renderView) + , m_rootPlatformLayer(0) + , m_compositing(false) + , m_rootLayerAttached(false) + , m_compositingLayersNeedUpdate(false) +#if PROFILE_LAYER_REBUILD + , m_rootLayerUpdateCount(0) +#endif // PROFILE_LAYER_REBUILD +{ +} + +RenderLayerCompositor::~RenderLayerCompositor() +{ + ASSERT(!m_rootLayerAttached); + delete m_rootPlatformLayer; +} + +void RenderLayerCompositor::enableCompositingMode(bool enable /* = true */) +{ + if (enable != m_compositing) { + m_compositing = enable; + + // We never go out of compositing mode for a given page, + // but if all the layers disappear, we'll just be left with + // the empty root layer, which has minimal overhead. + if (m_compositing) + ensureRootPlatformLayer(); + } +} + +void RenderLayerCompositor::setCompositingLayersNeedUpdate(bool needUpdate) +{ + if (inCompositingMode()) + m_compositingLayersNeedUpdate = needUpdate; +} + +void RenderLayerCompositor::updateCompositingLayers(RenderLayer* updateRoot) +{ + if (!m_compositingLayersNeedUpdate) + return; + + ASSERT(inCompositingMode()); + + if (!updateRoot) { + // Only clear the flag if we're updating the entire hierarchy + m_compositingLayersNeedUpdate = false; + updateRoot = rootRenderLayer(); + } + +#if PROFILE_LAYER_REBUILD + ++m_rootLayerUpdateCount; + + 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. + { + CompositingState compState(updateRoot); + computeCompositingRequirements(updateRoot, compState); + } + + // Now create and parent the compositing layers. + { + CompositingState compState(updateRoot); + rebuildCompositingLayerTree(updateRoot, compState); + } + +#if PROFILE_LAYER_REBUILD + double endTime = WTF::currentTime(); + if (!updateRoot) + fprintf(stderr, "Update %d: computeCompositingRequirements for the world took %fms\n" + m_rootLayerUpdateCount, 1000.0 * (endTime - startTime)); +#endif + ASSERT(updateRoot || !m_compositingLayersNeedUpdate); +} + +bool RenderLayerCompositor::updateLayerCompositingState(RenderLayer* layer, StyleDifference diff) +{ + bool needsLayer = needsToBeComposited(layer); + bool layerChanged = false; + + RenderBoxModelObject* repaintContainer = 0; + IntRect repaintRect; + + if (needsLayer) { + enableCompositingMode(); + if (!layer->backing()) { + // Get the repaint container before we make backing for this layer + repaintContainer = layer->renderer()->containerForRepaint(); + repaintRect = calculateCompositedBounds(layer, repaintContainer ? repaintContainer->layer() : layer->root()); + + layer->ensureBacking(); + layerChanged = true; + } + } else { + if (layer->backing()) { + layer->clearBacking(); + // Get the repaint container now that we've cleared the backing + repaintContainer = layer->renderer()->containerForRepaint(); + repaintRect = calculateCompositedBounds(layer, repaintContainer ? repaintContainer->layer() : layer->root()); + layerChanged = true; + } + } + + if (layerChanged) { + // Invalidate the destination into which this layer used to render. + layer->renderer()->repaintUsingContainer(repaintContainer, repaintRect); + + if (!repaintContainer || repaintContainer == m_renderView) { + // The contents of this layer may be moving between the window + // and a GraphicsLayer, so we need to make sure the window system + // synchronizes those changes on the screen. + m_renderView->frameView()->setNeedsOneShotDrawingSynchronization(); + } + } + + if (!needsLayer) + return layerChanged; + + if (layer->backing()->updateGraphicsLayers(needsContentsCompositingLayer(layer), + clippedByAncestor(layer), + clipsCompositingDescendants(layer), + diff >= StyleDifferenceRepaint)) + layerChanged = true; + + return layerChanged; +} + +// The bounds of the GraphicsLayer created for a compositing layer is the union of the bounds of all the descendant +// RenderLayers that are rendered by the composited RenderLayer. +IntRect RenderLayerCompositor::calculateCompositedBounds(const RenderLayer* layer, const RenderLayer* ancestorLayer, IntRect* layerBoundingBox) +{ + IntRect boundingBoxRect, unionBounds; + boundingBoxRect = unionBounds = layer->localBoundingBox(); + + ASSERT(layer->isStackingContext() || (!layer->m_posZOrderList || layer->m_posZOrderList->size() == 0)); + + Vector<RenderLayer*>* negZOrderList = layer->negZOrderList(); + if (negZOrderList) { + for (Vector<RenderLayer*>::iterator it = negZOrderList->begin(); it != negZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + if (!curLayer->isComposited()) { + IntRect childUnionBounds = calculateCompositedBounds(curLayer, layer); + unionBounds.unite(childUnionBounds); + } + } + } + + Vector<RenderLayer*>* posZOrderList = layer->posZOrderList(); + if (posZOrderList) { + for (Vector<RenderLayer*>::iterator it = posZOrderList->begin(); it != posZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + if (!curLayer->isComposited()) { + IntRect childUnionBounds = calculateCompositedBounds(curLayer, layer); + unionBounds.unite(childUnionBounds); + } + } + } + + Vector<RenderLayer*>* normalFlowList = layer->normalFlowList(); + if (normalFlowList) { + for (Vector<RenderLayer*>::iterator it = normalFlowList->begin(); it != normalFlowList->end(); ++it) { + RenderLayer* curLayer = (*it); + if (!curLayer->isComposited()) { + IntRect curAbsBounds = calculateCompositedBounds(curLayer, layer); + unionBounds.unite(curAbsBounds); + } + } + } + + if (!layer->isComposited() && layer->transform()) { + TransformationMatrix* affineTrans = layer->transform(); + boundingBoxRect = affineTrans->mapRect(boundingBoxRect); + unionBounds = affineTrans->mapRect(unionBounds); + } + + int ancestorRelX = 0, ancestorRelY = 0; + layer->convertToLayerCoords(ancestorLayer, ancestorRelX, ancestorRelY); + unionBounds.move(ancestorRelX, ancestorRelY); + + if (layerBoundingBox) { + boundingBoxRect.move(ancestorRelX, ancestorRelY); + *layerBoundingBox = boundingBoxRect; + } + + return unionBounds; +} + +void RenderLayerCompositor::layerWasAdded(RenderLayer* /*parent*/, RenderLayer* /*child*/) +{ + setCompositingLayersNeedUpdate(); +} + +void RenderLayerCompositor::layerWillBeRemoved(RenderLayer* parent, RenderLayer* child) +{ + if (child->isComposited()) + setCompositingParent(child, 0); + + // If the document is being torn down (document's renderer() is null), then there's + // no need to do any layer updating. + if (parent->renderer()->documentBeingDestroyed()) + return; + + RenderLayer* compLayer = parent->enclosingCompositingLayer(); + if (compLayer) { + IntRect ancestorRect = calculateCompositedBounds(child, compLayer); + compLayer->setBackingNeedsRepaintInRect(ancestorRect); + // The contents of this layer may be moving from a GraphicsLayer to the window, + // so we need to make sure the window system synchronizes those changes on the screen. + m_renderView->frameView()->setNeedsOneShotDrawingSynchronization(); + } + + setCompositingLayersNeedUpdate(); +} + +RenderLayer* RenderLayerCompositor::enclosingNonStackingClippingLayer(const RenderLayer* layer) const +{ + for (RenderLayer* curr = layer->parent(); curr != 0; curr = curr->parent()) { + if (curr->isStackingContext()) + return 0; + + if (curr->renderer()->hasOverflowClip()) + return curr; + } + return 0; +} + +// Recurse through the layers in z-index and overflow order (which is equivalent to painting order) +// For the z-order children of a compositing layer: +// If a child layers has a compositing layer, then all subsequent layers must +// be compositing in order to render above that layer. +// +// If a child in the negative z-order list is compositing, then the layer itself +// must be compositing so that its contents render over that child. +// This implies that its positive z-index children must also be compositing. +// +void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, struct CompositingState& ioCompState) +{ + layer->updateLayerPosition(); + layer->updateZOrderLists(); + layer->updateNormalFlowList(); + + // Clear the flag + layer->setHasCompositingDescendant(false); + layer->setMustOverlayCompositedLayers(ioCompState.m_subtreeIsCompositing); + + const bool isCompositingLayer = needsToBeComposited(layer); + ioCompState.m_subtreeIsCompositing = isCompositingLayer; + + CompositingState childState = ioCompState; + if (isCompositingLayer) + childState.m_compositingAncestor = layer; + + // The children of this stacking context don't need to composite, unless there is + // a compositing layer among them, so start by assuming false. + childState.m_subtreeIsCompositing = false; + +#ifndef NDEBUG + ++childState.m_depth; +#endif + + if (layer->isStackingContext()) { + ASSERT(!layer->m_zOrderListsDirty); + Vector<RenderLayer*>* negZOrderList = layer->negZOrderList(); + if (negZOrderList && negZOrderList->size() > 0) { + for (Vector<RenderLayer*>::const_iterator it = negZOrderList->begin(); it != negZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + computeCompositingRequirements(curLayer, childState); + + // if we have to make a layer for this child, make one now so we can have a contents layer + // (since we need to ensure that the -ve z-order child renders underneath our contents) + if (childState.m_subtreeIsCompositing) { + // make |this| compositing + layer->setMustOverlayCompositedLayers(true); + childState.m_compositingAncestor = layer; + } + } + } + } + + ASSERT(!layer->m_normalFlowListDirty); + Vector<RenderLayer*>* normalFlowList = layer->normalFlowList(); + if (normalFlowList && normalFlowList->size() > 0) { + for (Vector<RenderLayer*>::const_iterator it = normalFlowList->begin(); it != normalFlowList->end(); ++it) { + RenderLayer* curLayer = (*it); + computeCompositingRequirements(curLayer, childState); + } + } + + if (layer->isStackingContext()) { + Vector<RenderLayer*>* posZOrderList = layer->posZOrderList(); + if (posZOrderList && posZOrderList->size() > 0) { + for (Vector<RenderLayer*>::const_iterator it = posZOrderList->begin(); it != posZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + computeCompositingRequirements(curLayer, childState); + } + } + } + + // If we have a software transform, and we have layers under us, we need to also + // be composited. Also, if we have opacity < 1, then we need to be a layer so that + // the child layers are opaque, then rendered with opacity on this layer. + if (childState.m_subtreeIsCompositing && + (layer->renderer()->hasTransform() || layer->renderer()->style()->opacity() < 1)) + layer->setMustOverlayCompositedLayers(true); + + // Subsequent layers in the parent stacking context also need to composite. + if (childState.m_subtreeIsCompositing) + ioCompState.m_subtreeIsCompositing = true; + + // Set the flag to say that this SC has compositing children. + // this can affect the answer to needsToBeComposited() when clipping, + // but that's ok here. + layer->setHasCompositingDescendant(childState.m_subtreeIsCompositing); +} + +void RenderLayerCompositor::setCompositingParent(RenderLayer* childLayer, RenderLayer* parentLayer) +{ + ASSERT(childLayer->isComposited()); + ASSERT(!parentLayer || parentLayer->isComposited()); + + if (parentLayer) { + GraphicsLayer* hostingLayer = parentLayer->backing()->parentForSublayers(); + GraphicsLayer* hostedLayer = childLayer->backing()->childForSuperlayers(); + + hostingLayer->addChild(hostedLayer); + } else + childLayer->backing()->childForSuperlayers()->removeFromParent(); + + // FIXME: setCompositingParent() is only called at present by rebuildCompositingLayerTree(), + // which calls updateGraphicsLayerGeometry via updateLayerCompositingState(), so this should + // be optimized. + if (parentLayer) + childLayer->backing()->updateGraphicsLayerGeometry(); +} + +void RenderLayerCompositor::removeCompositedChildren(RenderLayer* layer) +{ + ASSERT(layer->isComposited()); + + GraphicsLayer* hostingLayer = layer->backing()->parentForSublayers(); + hostingLayer->removeAllChildren(); +} + +void RenderLayerCompositor::parentInRootLayer(RenderLayer* layer) +{ + ASSERT(layer->isComposited()); + + GraphicsLayer* layerAnchor = layer->backing()->childForSuperlayers(); + + if (layerAnchor->parent() != m_rootPlatformLayer) { + layerAnchor->removeFromParent(); + if (m_rootPlatformLayer) + m_rootPlatformLayer->addChild(layerAnchor); + } +} + +void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, struct CompositingState& ioCompState) +{ + updateLayerCompositingState(layer, StyleDifferenceEqual); + + // host the document layer in the RenderView's root layer + if (layer->isRootLayer()) + parentInRootLayer(layer); + + CompositingState childState = ioCompState; + if (layer->isComposited()) + childState.m_compositingAncestor = layer; + +#ifndef NDEBUG + ++childState.m_depth; +#endif + + RenderLayerBacking* layerBacking = layer->backing(); + + // FIXME: make this more incremental + if (layer->isComposited()) { + layerBacking->parentForSublayers()->removeAllChildren(); + layerBacking->updateInternalHierarchy(); + } + + // The children of this stacking context don't need to composite, unless there is + // a compositing layer among them, so start by assuming false. + childState.m_subtreeIsCompositing = false; + + if (layer->isStackingContext()) { + ASSERT(!layer->m_zOrderListsDirty); + + Vector<RenderLayer*>* negZOrderList = layer->negZOrderList(); + if (negZOrderList && negZOrderList->size() > 0) { + for (Vector<RenderLayer*>::const_iterator it = negZOrderList->begin(); it != negZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + rebuildCompositingLayerTree(curLayer, childState); + if (curLayer->isComposited()) + setCompositingParent(curLayer, childState.m_compositingAncestor); + } + } + + if (layerBacking && layerBacking->contentsLayer()) { + // we only have a contents layer if we have an m_layer + layerBacking->contentsLayer()->removeFromParent(); + + GraphicsLayer* hostingLayer = layerBacking->clippingLayer() ? layerBacking->clippingLayer() : layerBacking->graphicsLayer(); + hostingLayer->addChild(layerBacking->contentsLayer()); + } + } + + ASSERT(!layer->m_normalFlowListDirty); + Vector<RenderLayer*>* normalFlowList = layer->normalFlowList(); + if (normalFlowList && normalFlowList->size() > 0) { + for (Vector<RenderLayer*>::iterator it = normalFlowList->begin(); it != normalFlowList->end(); ++it) { + RenderLayer* curLayer = (*it); + rebuildCompositingLayerTree(curLayer, childState); + if (curLayer->isComposited()) + setCompositingParent(curLayer, childState.m_compositingAncestor); + } + } + + if (layer->isStackingContext()) { + Vector<RenderLayer*>* posZOrderList = layer->posZOrderList(); + if (posZOrderList && posZOrderList->size() > 0) { + for (Vector<RenderLayer*>::const_iterator it = posZOrderList->begin(); it != posZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + rebuildCompositingLayerTree(curLayer, childState); + if (curLayer->isComposited()) + setCompositingParent(curLayer, childState.m_compositingAncestor); + } + } + } +} + +void RenderLayerCompositor::repaintCompositedLayersAbsoluteRect(const IntRect& absRect) +{ + recursiveRepaintLayerRect(rootRenderLayer(), absRect); +} + +void RenderLayerCompositor::recursiveRepaintLayerRect(RenderLayer* layer, const IntRect& rect) +{ + if (layer->isComposited()) + layer->setBackingNeedsRepaintInRect(rect); + + if (layer->hasCompositingDescendant()) { + Vector<RenderLayer*>* negZOrderList = layer->negZOrderList(); + if (negZOrderList) { + for (Vector<RenderLayer*>::iterator it = negZOrderList->begin(); it != negZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + int x = 0, y = 0; + curLayer->convertToLayerCoords(layer, x, y); + IntRect childRect(rect); + childRect.move(-x, -y); + recursiveRepaintLayerRect(curLayer, childRect); + } + } + + Vector<RenderLayer*>* posZOrderList = layer->posZOrderList(); + if (posZOrderList) { + for (Vector<RenderLayer*>::iterator it = posZOrderList->begin(); it != posZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + int x = 0, y = 0; + curLayer->convertToLayerCoords(layer, x, y); + IntRect childRect(rect); + childRect.move(-x, -y); + recursiveRepaintLayerRect(curLayer, childRect); + } + } + + Vector<RenderLayer*>* normalFlowList = layer->normalFlowList(); + if (normalFlowList) { + for (Vector<RenderLayer*>::iterator it = normalFlowList->begin(); it != normalFlowList->end(); ++it) { + RenderLayer* curLayer = (*it); + int x = 0, y = 0; + curLayer->convertToLayerCoords(layer, x, y); + IntRect childRect(rect); + childRect.move(-x, -y); + recursiveRepaintLayerRect(curLayer, childRect); + } + } + } +} + +RenderLayer* RenderLayerCompositor::rootRenderLayer() const +{ + return m_renderView->layer(); +} + +GraphicsLayer* RenderLayerCompositor::rootPlatformLayer() const +{ + return m_rootPlatformLayer; +} + +void RenderLayerCompositor::didMoveOnscreen() +{ + if (!m_rootPlatformLayer) + return; + + Frame* frame = m_renderView->frameView()->frame(); + Page* page = frame ? frame->page() : 0; + if (!page) + return; + + page->chrome()->client()->attachRootGraphicsLayer(frame, m_rootPlatformLayer); + m_rootLayerAttached = true; +} + +void RenderLayerCompositor::willMoveOffscreen() +{ + if (!m_rootPlatformLayer || !m_rootLayerAttached) + return; + + Frame* frame = m_renderView->frameView()->frame(); + Page* page = frame ? frame->page() : 0; + if (!page) + return; + + page->chrome()->client()->attachRootGraphicsLayer(frame, 0); + m_rootLayerAttached = false; +} + +void RenderLayerCompositor::updateRootLayerPosition() +{ + if (m_rootPlatformLayer) + m_rootPlatformLayer->setSize(FloatSize(m_renderView->docWidth(), m_renderView->docHeight())); +} + +bool RenderLayerCompositor::has3DContent() const +{ + return layerHas3DContent(rootRenderLayer()); +} + +bool RenderLayerCompositor::needsToBeComposited(const RenderLayer* layer) const +{ + return requiresCompositingLayer(layer) || layer->mustOverlayCompositedLayers(); +} + +#define VERBOSE_COMPOSITINGLAYER 0 + +// Note: this specifies whether the RL needs a compositing layer for intrinsic reasons. +// Use needsToBeComposited() to determine if a RL actually needs a compositing layer. +// static +bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer) const +{ + // FIXME: cache the result of these tests? +#if VERBOSE_COMPOSITINGLAYER + bool gotReason = false; + + if (!gotReason && inCompositingMode() && layer->isRootLayer()) { + fprintf(stderr, "RenderLayer %p requires compositing layer because: it's the document root\n", layer); + gotReason = true; + } + + if (!gotReason && requiresCompositingForTransform(layer->renderer())) { + fprintf(stderr, "RenderLayer %p requires compositing layer because: it has 3d transform, perspective, backface, or animating transform\n", layer); + gotReason = true; + } + + if (!gotReason && layer->renderer()->style()->backfaceVisibility() == BackfaceVisibilityHidden) { + fprintf(stderr, "RenderLayer %p requires compositing layer because: it has backface-visibility: hidden\n", layer); + gotReason = true; + } + + if (!gotReason && clipsCompositingDescendants(layer)) { + fprintf(stderr, "RenderLayer %p requires compositing layer because: it has overflow clip\n", layer); + gotReason = true; + } + + if (!gotReason && requiresCompositingForAnimation(layer->renderer())) { + fprintf(stderr, "RenderLayer %p requires compositing layer because: it has a running transition for opacity or transform\n", layer); + gotReason = true; + } + + if (!gotReason) + fprintf(stderr, "RenderLayer %p does not require compositing layer\n", layer); +#endif + + // The root layer always has a compositing layer, but it may not have backing. + return (inCompositingMode() && layer->isRootLayer()) || + requiresCompositingForTransform(layer->renderer()) || + layer->renderer()->style()->backfaceVisibility() == BackfaceVisibilityHidden || + clipsCompositingDescendants(layer) || + requiresCompositingForAnimation(layer->renderer()); +} + +// Return true if the given layer has some ancestor in the RenderLayer hierarchy that clips, +// up to the enclosing compositing ancestor. This is required because compositing layers are parented +// according to the z-order hierarchy, yet clipping goes down the renderer hierarchy. +// Thus, a RenderLayer can be clipped by a RenderLayer that is an ancestor in the renderer hierarchy, +// but a sibling in the z-order hierarchy. +bool RenderLayerCompositor::clippedByAncestor(RenderLayer* layer) const +{ + if (!layer->isComposited() || !layer->parent()) + return false; + + RenderLayer* compositingAncestor = layer->ancestorCompositingLayer(); + if (!compositingAncestor) + return false; + + // If the compositingAncestor clips, that will be taken care of by clipsCompositingDescendants(), + // so we only care about clipping between its first child that is our ancestor (the computeClipRoot), + // and layer. + RenderLayer* computeClipRoot = 0; + RenderLayer* curr = layer; + while (curr) { + RenderLayer* next = curr->parent(); + if (next == compositingAncestor) { + computeClipRoot = curr; + break; + } + curr = next; + } + + if (!computeClipRoot || computeClipRoot == layer) + return false; + + ClipRects parentRects; + layer->parentClipRects(computeClipRoot, parentRects, true); + + return parentRects.overflowClipRect() != ClipRects::infiniteRect(); +} + +// Return true if the given layer is a stacking context and has compositing child +// layers that it needs to clip. In this case we insert a clipping GraphicsLayer +// into the hierarchy between this layer and its children in the z-order hierarchy. +bool RenderLayerCompositor::clipsCompositingDescendants(const RenderLayer* layer) const +{ + // FIXME: need to look at hasClip() too eventually + return layer->hasCompositingDescendant() && + layer->renderer()->hasOverflowClip(); +} + +bool RenderLayerCompositor::requiresCompositingForTransform(RenderObject* renderer) +{ + RenderStyle* style = renderer->style(); + // Note that we ask the renderer if it has a transform, because the style may have transforms, + // but the renderer may be an inline that doesn't suppport them. + return renderer->hasTransform() && (style->transform().has3DOperation() || style->transformStyle3D() == TransformStyle3DPreserve3D || style->hasPerspective()); +} + +bool RenderLayerCompositor::requiresCompositingForAnimation(RenderObject* renderer) +{ + AnimationController* animController = renderer->animation(); + if (animController) + return animController->isAnimatingPropertyOnRenderer(renderer, CSSPropertyOpacity) || + animController->isAnimatingPropertyOnRenderer(renderer, CSSPropertyWebkitTransform); + return false; +} + +// If an element has negative z-index children, those children render in front of the +// layer background, so we need an extra 'contents' layer for the foreground of the layer +// object. +bool RenderLayerCompositor::needsContentsCompositingLayer(const RenderLayer* layer) const +{ + return (layer->m_negZOrderList && layer->m_negZOrderList->size() > 0); +} + +void RenderLayerCompositor::ensureRootPlatformLayer() +{ + if (m_rootPlatformLayer) + return; + + m_rootPlatformLayer = GraphicsLayer::createGraphicsLayer(0); + m_rootPlatformLayer->setSize(FloatSize(m_renderView->docWidth(), m_renderView->docHeight())); + m_rootPlatformLayer->setPosition(FloatPoint(0, 0)); + + if (GraphicsLayer::graphicsContextsFlipped()) + m_rootPlatformLayer->setChildrenTransform(flipTransform()); + + // Need to clip to prevent transformed content showing outside this frame + m_rootPlatformLayer->setMasksToBounds(true); + + didMoveOnscreen(); +} + +bool RenderLayerCompositor::layerHas3DContent(const RenderLayer* layer) const +{ + const RenderStyle* style = layer->renderer()->style(); + + if (style && + (style->transformStyle3D() == TransformStyle3DPreserve3D || + style->hasPerspective() || + style->transform().has3DOperation())) + return true; + + if (layer->isStackingContext()) { + Vector<RenderLayer*>* negZOrderList = layer->negZOrderList(); + if (negZOrderList) { + for (Vector<RenderLayer*>::iterator it = negZOrderList->begin(); it != negZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + if (layerHas3DContent(curLayer)) + return true; + } + } + + Vector<RenderLayer*>* posZOrderList = layer->posZOrderList(); + if (posZOrderList) { + for (Vector<RenderLayer*>::iterator it = posZOrderList->begin(); it != posZOrderList->end(); ++it) { + RenderLayer* curLayer = (*it); + if (layerHas3DContent(curLayer)) + return true; + } + } + } + + Vector<RenderLayer*>* normalFlowList = layer->normalFlowList(); + if (normalFlowList) { + for (Vector<RenderLayer*>::iterator it = normalFlowList->begin(); it != normalFlowList->end(); ++it) { + RenderLayer* curLayer = (*it); + if (layerHas3DContent(curLayer)) + return true; + } + } + return false; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + diff --git a/WebCore/rendering/RenderLayerCompositor.h b/WebCore/rendering/RenderLayerCompositor.h new file mode 100644 index 0000000..dfceb1f --- /dev/null +++ b/WebCore/rendering/RenderLayerCompositor.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderLayerCompositor_h +#define RenderLayerCompositor_h + +#include "RenderLayer.h" + +namespace WebCore { + +#define PROFILE_LAYER_REBUILD 0 + +class GraphicsLayer; + +// RenderLayerCompositor manages the hierarchy of +// composited RenderLayers. It determines which RenderLayers +// become compositing, and creates and maintains a hierarchy of +// GraphicsLayers based on the RenderLayer painting order. +// +// There is one RenderLayerCompositor per RenderView. + +class RenderLayerCompositor { +public: + + RenderLayerCompositor(RenderView*); + ~RenderLayerCompositor(); + + // Return true if this RenderView is in "compositing mode" (i.e. has one or more + // composited RenderLayers) + bool inCompositingMode() const { return m_compositing; } + // This will make a compositing layer at the root automatically, and hook up to + // the native view/window system. + void enableCompositingMode(bool enable = true); + + void setCompositingLayersNeedUpdate(bool needUpdate = true); + bool compositingLayersNeedUpdate() const { return m_compositingLayersNeedUpdate; } + + // Rebuild the tree of compositing layers + void updateCompositingLayers(RenderLayer* updateRoot = 0); + + // Update the compositing state of the given layer. Returns true if that state changed + bool updateLayerCompositingState(RenderLayer*, StyleDifference); + + // Return the bounding box required for compositing layer and its childern, relative to ancestorLayer. + // If layerBoundingBox is not 0, on return it contains the bounding box of this layer only. + IntRect calculateCompositedBounds(const RenderLayer* layer, const RenderLayer* ancestorLayer, IntRect* layerBoundingBox = 0); + + // Notify us that a layer has been added or removed + void layerWasAdded(RenderLayer* parent, RenderLayer* child); + void layerWillBeRemoved(RenderLayer* parent, RenderLayer* child); + + // Get the nearest ancestor layer that has overflow or clip, but is not a stacking context + RenderLayer* enclosingNonStackingClippingLayer(const RenderLayer* layer) const; + + // Repaint parts of all composited layers that intersect the given absolute rectangle. + void repaintCompositedLayersAbsoluteRect(const IntRect&); + + RenderLayer* rootRenderLayer() const; + GraphicsLayer* rootPlatformLayer() const; + + void didMoveOnscreen(); + void willMoveOffscreen(); + + void updateRootLayerPosition(); + + // Walk the tree looking for layers with 3d transforms. Useful in case you need + // to know if there is non-affine content, e.g. for drawing into an image. + bool has3DContent() const; + +private: + // Whether the given RL needs a compositing layer. + bool needsToBeComposited(const RenderLayer*) const; + // Whether the layer has an intrinsic need for compositing layer. + bool requiresCompositingLayer(const RenderLayer*) const; + + // Whether we need a graphics layer to do clipping by an ancestor (non-stacking-context parent with overflow). + bool clippedByAncestor(RenderLayer*) const; + // Whether we need a graphics layer to clip z-order children of the given layer. + bool clipsCompositingDescendants(const RenderLayer*) const; + + // Whether the given layer needs an extra 'contents' layer. + bool needsContentsCompositingLayer(const RenderLayer*) const; + + // Repaint the given rect (which is layer's coords), and regions of child layers that intersect that rect. + void recursiveRepaintLayerRect(RenderLayer* layer, const IntRect& rect); + + void computeCompositingRequirements(RenderLayer*, struct CompositingState&); + void rebuildCompositingLayerTree(RenderLayer* layer, struct CompositingState&); + + // Hook compositing layers together + void setCompositingParent(RenderLayer* childLayer, RenderLayer* parentLayer); + void removeCompositedChildren(RenderLayer*); + + void parentInRootLayer(RenderLayer*); + + bool layerHas3DContent(const RenderLayer*) const; + + void ensureRootPlatformLayer(); + + // Whether a running transition or animation enforces the need for a compositing layer. + static bool requiresCompositingForAnimation(RenderObject*); + static bool requiresCompositingForTransform(RenderObject*); + +private: + RenderView* m_renderView; + GraphicsLayer* m_rootPlatformLayer; + bool m_compositing; + bool m_rootLayerAttached; + bool m_compositingLayersNeedUpdate; +#if PROFILE_LAYER_REBUILD + int m_rootLayerUpdateCount; +#endif +}; + + +} // namespace WebCore + +#endif // RenderLayerCompositor_h diff --git a/WebCore/rendering/RenderLegend.cpp b/WebCore/rendering/RenderLegend.cpp deleted file mode 100644 index 1fac53f..0000000 --- a/WebCore/rendering/RenderLegend.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of the DOM implementation for KDE. - * - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "RenderLegend.h" - -namespace WebCore { - -RenderLegend::RenderLegend(Node* element) - : RenderBlock(element) -{ -} - -} // namespace WebCore diff --git a/WebCore/rendering/RenderLegend.h b/WebCore/rendering/RenderLegend.h deleted file mode 100644 index 649f132..0000000 --- a/WebCore/rendering/RenderLegend.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of the DOM implementation for KDE. - * - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2006 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RenderLegend_h -#define RenderLegend_h - -#include "RenderBlock.h" - -namespace WebCore { - - class RenderLegend : public RenderBlock { - public: - RenderLegend(Node*); - - virtual const char* renderName() const { return "RenderLegend"; } - }; - -} // namespace WebCore - -#endif // RenderLegend_h diff --git a/WebCore/rendering/RenderLineBoxList.cpp b/WebCore/rendering/RenderLineBoxList.cpp new file mode 100644 index 0000000..00566b8 --- /dev/null +++ b/WebCore/rendering/RenderLineBoxList.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderLineBoxList.h" + +#include "InlineTextBox.h" +#include "RenderArena.h" +#include "RenderInline.h" +#include "RenderView.h" +#include "RootInlineBox.h" + +using namespace std; + +namespace WebCore { + +#ifndef NDEBUG +RenderLineBoxList::~RenderLineBoxList() +{ + ASSERT(!m_firstLineBox); + ASSERT(!m_lastLineBox); +} +#endif + +void RenderLineBoxList::appendLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + if (!m_firstLineBox) + m_firstLineBox = m_lastLineBox = box; + else { + m_lastLineBox->setNextLineBox(box); + box->setPreviousLineBox(m_lastLineBox); + m_lastLineBox = box; + } + + checkConsistency(); +} + +void RenderLineBoxList::deleteLineBoxTree(RenderArena* arena) +{ + InlineFlowBox* line = m_firstLineBox; + InlineFlowBox* nextLine; + while (line) { + nextLine = line->nextFlowBox(); + line->deleteLine(arena); + line = nextLine; + } + m_firstLineBox = m_lastLineBox = 0; +} + +void RenderLineBoxList::extractLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + m_lastLineBox = box->prevFlowBox(); + if (box == m_firstLineBox) + m_firstLineBox = 0; + if (box->prevLineBox()) + box->prevLineBox()->setNextLineBox(0); + box->setPreviousLineBox(0); + for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox()) + curr->setExtracted(); + + checkConsistency(); +} + +void RenderLineBoxList::attachLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + if (m_lastLineBox) { + m_lastLineBox->setNextLineBox(box); + box->setPreviousLineBox(m_lastLineBox); + } else + m_firstLineBox = box; + InlineFlowBox* last = box; + for (InlineFlowBox* curr = box; curr; curr = curr->nextFlowBox()) { + curr->setExtracted(false); + last = curr; + } + m_lastLineBox = last; + + checkConsistency(); +} + +void RenderLineBoxList::removeLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + if (box == m_firstLineBox) + m_firstLineBox = box->nextFlowBox(); + if (box == m_lastLineBox) + m_lastLineBox = box->prevFlowBox(); + if (box->nextLineBox()) + box->nextLineBox()->setPreviousLineBox(box->prevLineBox()); + if (box->prevLineBox()) + box->prevLineBox()->setNextLineBox(box->nextLineBox()); + + checkConsistency(); +} + +void RenderLineBoxList::deleteLineBoxes(RenderArena* arena) +{ + if (m_firstLineBox) { + InlineRunBox* next; + for (InlineRunBox* curr = m_firstLineBox; curr; curr = next) { + next = curr->nextLineBox(); + curr->destroy(arena); + } + m_firstLineBox = 0; + m_lastLineBox = 0; + } +} + +void RenderLineBoxList::dirtyLineBoxes() +{ + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + curr->dirtyLineBoxes(); +} + +void RenderLineBoxList::paint(RenderBoxModelObject* renderer, RenderObject::PaintInfo& paintInfo, int tx, int ty) const +{ + // Only paint during the foreground/selection phases. + if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseOutline + && paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines && paintInfo.phase != PaintPhaseTextClip + && paintInfo.phase != PaintPhaseMask) + return; + + ASSERT(renderer->isRenderBlock() || (renderer->isRenderInline() && renderer->hasLayer())); // The only way an inline could paint like this is if it has a layer. + + // If we have no lines then we have no work to do. + if (!firstLineBox()) + return; + + // We can check the first box and last box and avoid painting if we don't + // intersect. This is a quick short-circuit that we can take to avoid walking any lines. + // FIXME: This check is flawed in the following extremely obscure way: + // if some line in the middle has a huge overflow, it might actually extend below the last line. + int yPos = firstLineBox()->root()->topOverflow() - renderer->maximalOutlineSize(paintInfo.phase); + int h = renderer->maximalOutlineSize(paintInfo.phase) + lastLineBox()->root()->bottomOverflow() - yPos; + yPos += ty; + if (yPos >= paintInfo.rect.bottom() || yPos + h <= paintInfo.rect.y()) + return; + + RenderObject::PaintInfo info(paintInfo); + ListHashSet<RenderInline*> outlineObjects; + info.outlineObjects = &outlineObjects; + + // See if our root lines intersect with the dirty rect. If so, then we paint + // them. Note that boxes can easily overlap, so we can't make any assumptions + // based off positions of our first line box or our last line box. + RenderView* v = renderer->view(); + bool usePrintRect = !v->printRect().isEmpty(); + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) { + if (usePrintRect) { + // FIXME: This is a feeble effort to avoid splitting a line across two pages. + // It is utterly inadequate, and this should not be done at paint time at all. + // The whole way objects break across pages needs to be redone. + // Try to avoid splitting a line vertically, but only if it's less than the height + // of the entire page. + if (curr->root()->bottomOverflow() - curr->root()->topOverflow() <= v->printRect().height()) { + if (ty + curr->root()->bottomOverflow() > v->printRect().bottom()) { + if (ty + curr->root()->topOverflow() < v->truncatedAt()) + v->setBestTruncatedAt(ty + curr->root()->topOverflow(), renderer); + // If we were able to truncate, don't paint. + if (ty + curr->root()->topOverflow() >= v->truncatedAt()) + break; + } + } + } + + int top = min(curr->root()->topOverflow(), curr->root()->selectionTop()) - renderer->maximalOutlineSize(info.phase); + int bottom = curr->root()->bottomOverflow() + renderer->maximalOutlineSize(info.phase); + h = bottom - top; + yPos = ty + top; + if (yPos < info.rect.bottom() && yPos + h > info.rect.y()) + curr->paint(info, tx, ty); + } + + if (info.phase == PaintPhaseOutline || info.phase == PaintPhaseSelfOutline || info.phase == PaintPhaseChildOutlines) { + ListHashSet<RenderInline*>::iterator end = info.outlineObjects->end(); + for (ListHashSet<RenderInline*>::iterator it = info.outlineObjects->begin(); it != end; ++it) { + RenderInline* flow = *it; + flow->paintOutline(info.context, tx, ty); + } + info.outlineObjects->clear(); + } +} + + +bool RenderLineBoxList::hitTest(RenderBoxModelObject* renderer, const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) const +{ + if (hitTestAction != HitTestForeground) + return false; + + ASSERT(renderer->isRenderBlock() || (renderer->isRenderInline() && renderer->hasLayer())); // The only way an inline could hit test like this is if it has a layer. + + // If we have no lines then we have no work to do. + if (!firstLineBox()) + return false; + + // We can check the first box and last box and avoid hit testing if we don't + // contain the point. This is a quick short-circuit that we can take to avoid walking any lines. + // FIXME: This check is flawed in the following extremely obscure way: + // if some line in the middle has a huge overflow, it might actually extend below the last line. + if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow())) + return false; + + // See if our root lines contain the point. If so, then we hit test + // them further. Note that boxes can easily overlap, so we can't make any assumptions + // based off positions of our first line box or our last line box. + for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevFlowBox()) { + if (y >= ty + curr->root()->topOverflow() && y < ty + curr->root()->bottomOverflow()) { + bool inside = curr->nodeAtPoint(request, result, x, y, tx, ty); + if (inside) { + renderer->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + } + + return false; +} + +void RenderLineBoxList::dirtyLinesFromChangedChild(RenderObject* container, RenderObject* child) +{ + if (!container->parent() || (container->isRenderBlock() && (container->selfNeedsLayout() || !container->isBlockFlow()))) + return; + + // If we have no first line box, then just bail early. + if (!firstLineBox()) { + // For an empty inline, go ahead and propagate the check up to our parent, unless the parent + // is already dirty. + if (container->isInline() && !container->parent()->selfNeedsLayout()) + container->parent()->dirtyLinesFromChangedChild(container); + return; + } + + // Try to figure out which line box we belong in. First try to find a previous + // line box by examining our siblings. If we didn't find a line box, then use our + // parent's first line box. + RootInlineBox* box = 0; + RenderObject* curr = 0; + for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { + if (curr->isFloatingOrPositioned()) + continue; + + if (curr->isReplaced()) { + InlineBox* wrapper = toRenderBox(curr)->inlineBoxWrapper(); + if (wrapper) + box = wrapper->root(); + } else if (curr->isText()) { + InlineTextBox* textBox = toRenderText(curr)->lastTextBox(); + if (textBox) + box = textBox->root(); + } else if (curr->isRenderInline()) { + InlineRunBox* runBox = toRenderInline(curr)->lastLineBox(); + if (runBox) + box = runBox->root(); + } + + if (box) + break; + } + if (!box) + box = firstLineBox()->root(); + + // If we found a line box, then dirty it. + if (box) { + RootInlineBox* adjacentBox; + box->markDirty(); + + // dirty the adjacent lines that might be affected + // NOTE: we dirty the previous line because RootInlineBox objects cache + // the address of the first object on the next line after a BR, which we may be + // invalidating here. For more info, see how RenderBlock::layoutInlineChildren + // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak, + // despite the name, actually returns the first RenderObject after the BR. + // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize." + adjacentBox = box->prevRootBox(); + if (adjacentBox) + adjacentBox->markDirty(); + if (child->isBR() || (curr && curr->isBR())) { + adjacentBox = box->nextRootBox(); + if (adjacentBox) + adjacentBox->markDirty(); + } + } +} + +#ifndef NDEBUG + +void RenderLineBoxList::checkConsistency() const +{ +#ifdef CHECK_CONSISTENCY + const InlineFlowBox* prev = 0; + for (const InlineFlowBox* child = m_firstLineBox; child != 0; child = child->nextFlowBox()) { + ASSERT(child->prevFlowBox() == prev); + prev = child; + } + ASSERT(prev == m_lastLineBox); +#endif +} + +#endif + +} diff --git a/WebCore/rendering/RenderLineBoxList.h b/WebCore/rendering/RenderLineBoxList.h new file mode 100644 index 0000000..52d7542 --- /dev/null +++ b/WebCore/rendering/RenderLineBoxList.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef RenderLineBoxList_h +#define RenderLineBoxList_h + +#include "RenderBox.h" + +namespace WebCore { + +class RenderLineBoxList { +public: + RenderLineBoxList() + : m_firstLineBox(0) + , m_lastLineBox(0) + { + } + +#ifndef NDEBUG + ~RenderLineBoxList(); +#endif + + InlineFlowBox* firstLineBox() const { return m_firstLineBox; } + InlineFlowBox* lastLineBox() const { return m_lastLineBox; } + + void checkConsistency() const; + + void appendLineBox(InlineFlowBox*); + + void deleteLineBoxTree(RenderArena*); + void deleteLineBoxes(RenderArena*); + + void extractLineBox(InlineFlowBox*); + void attachLineBox(InlineFlowBox*); + void removeLineBox(InlineFlowBox*); + + void dirtyLineBoxes(); + void dirtyLinesFromChangedChild(RenderObject* parent, RenderObject* child); + + void paint(RenderBoxModelObject*, RenderObject::PaintInfo&, int x, int y) const; + bool hitTest(RenderBoxModelObject*, const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction) const; + +private: + // For block flows, each box represents the root inline box for a line in the + // paragraph. + // For inline flows, each box represents a portion of that inline. + InlineFlowBox* m_firstLineBox; + InlineFlowBox* m_lastLineBox; +}; + + +#ifdef NDEBUG +inline void RenderLineBoxList::checkConsistency() const +{ +} +#endif + +} // namespace WebCore + +#endif // RenderFlow_h diff --git a/WebCore/rendering/RenderListBox.cpp b/WebCore/rendering/RenderListBox.cpp index 3dddc13..50b406b 100644 --- a/WebCore/rendering/RenderListBox.cpp +++ b/WebCore/rendering/RenderListBox.cpp @@ -87,7 +87,7 @@ RenderListBox::~RenderListBox() setHasVerticalScrollbar(false); } -void RenderListBox::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderListBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); setReplaced(isInline()); @@ -527,7 +527,7 @@ void RenderListBox::valueChanged(Scrollbar*) m_indexOffset = newOffset; repaint(); // Fire the scroll DOM event. - EventTargetNodeCast(node())->dispatchEventForType(eventNames().scrollEvent, false, false); + node()->dispatchEventForType(eventNames().scrollEvent, false, false); } } @@ -579,6 +579,29 @@ void RenderListBox::setScrollTop(int newTop) m_vBar->setValue(index); } +bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + if (!RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction)) + return false; + const Vector<HTMLElement*>& listItems = static_cast<HTMLSelectElement*>(node())->listItems(); + int size = numItems(); + tx += this->x(); + ty += this->y(); + for (int i = 0; i < size; ++i) { + if (itemBoundingBoxRect(tx, ty, i).contains(x, y)) { + if (HTMLElement* node = listItems[i]) { + result.setInnerNode(node); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(node); + result.setLocalPoint(IntPoint(x - tx, y - ty)); + break; + } + } + } + + return true; +} + IntRect RenderListBox::controlClipRect(int tx, int ty) const { IntRect clipRect = contentBoxRect(); @@ -602,11 +625,11 @@ void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& PassRefPtr<Scrollbar> RenderListBox::createScrollbar() { RefPtr<Scrollbar> widget; - bool hasCustomScrollbarStyle = style()->hasPseudoStyle(RenderStyle::SCROLLBAR); + bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR); if (hasCustomScrollbarStyle) widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this); else - widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, SmallScrollbar); + widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart)); document()->view()->addChild(widget.get()); return widget.release(); } diff --git a/WebCore/rendering/RenderListBox.h b/WebCore/rendering/RenderListBox.h index b9cfcb1..acc313f 100644 --- a/WebCore/rendering/RenderListBox.h +++ b/WebCore/rendering/RenderListBox.h @@ -92,8 +92,10 @@ public: virtual void setScrollLeft(int); virtual void setScrollTop(int); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); private: // ScrollbarClient interface. diff --git a/WebCore/rendering/RenderListItem.cpp b/WebCore/rendering/RenderListItem.cpp index 47158b6..fb965d2 100644 --- a/WebCore/rendering/RenderListItem.cpp +++ b/WebCore/rendering/RenderListItem.cpp @@ -49,7 +49,7 @@ RenderListItem::RenderListItem(Node* node) setInline(false); } -void RenderListItem::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); @@ -153,11 +153,11 @@ static RenderObject* getParentOfFirstLineBox(RenderBlock* curr, RenderObject* ma if (currChild->isTable() || !currChild->isRenderBlock()) break; - if (curr->isListItem() && currChild->style()->htmlHacks() && currChild->element() && - (currChild->element()->hasTagName(ulTag)|| currChild->element()->hasTagName(olTag))) + if (curr->isListItem() && currChild->style()->htmlHacks() && currChild->node() && + (currChild->node()->hasTagName(ulTag)|| currChild->node()->hasTagName(olTag))) break; - RenderObject* lineBox = getParentOfFirstLineBox(static_cast<RenderBlock*>(currChild), marker); + RenderObject* lineBox = getParentOfFirstLineBox(toRenderBlock(currChild), marker); if (lineBox) return lineBox; } @@ -248,7 +248,7 @@ void RenderListItem::positionListMarker() RootInlineBox* root = m_marker->inlineBoxWrapper()->root(); if (style()->direction() == LTR) { - int leftLineOffset = leftRelOffset(yOffset, leftOffset(yOffset)); + int leftLineOffset = leftRelOffset(yOffset, leftOffset(yOffset, false), false); markerXPos = leftLineOffset - xOffset - paddingLeft() - borderLeft() + m_marker->marginLeft(); m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0); if (markerXPos < root->leftOverflow()) { @@ -256,7 +256,7 @@ void RenderListItem::positionListMarker() adjustOverflow = true; } } else { - int rightLineOffset = rightRelOffset(yOffset, rightOffset(yOffset)); + int rightLineOffset = rightRelOffset(yOffset, rightOffset(yOffset, false), false); markerXPos = rightLineOffset - xOffset + paddingRight() + borderRight() + m_marker->marginLeft(); m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0); if (markerXPos + m_marker->width() > root->rightOverflow()) { @@ -271,7 +271,7 @@ void RenderListItem::positionListMarker() do { o = o->parentBox(); if (o->isRenderBlock()) - static_cast<RenderBlock*>(o)->addVisualOverflow(markerRect); + toRenderBlock(o)->addVisualOverflow(markerRect); markerRect.move(-o->x(), -o->y()); } while (o != this); } diff --git a/WebCore/rendering/RenderListItem.h b/WebCore/rendering/RenderListItem.h index d4dd675..91844f7 100644 --- a/WebCore/rendering/RenderListItem.h +++ b/WebCore/rendering/RenderListItem.h @@ -61,7 +61,7 @@ public: const String& markerText() const; protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); private: void updateMarkerLocation(); diff --git a/WebCore/rendering/RenderListMarker.cpp b/WebCore/rendering/RenderListMarker.cpp index 340db50..32b5999 100644 --- a/WebCore/rendering/RenderListMarker.cpp +++ b/WebCore/rendering/RenderListMarker.cpp @@ -159,7 +159,7 @@ static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UCha int lowerOffset = upper ? 0 : 0x0030; - if (int thousands = number / 1000) + if (int thousands = number / 1000) { if (thousands == 7) { letters[length++] = 0x0548 + lowerOffset; letters[length++] = 0x0552 + lowerOffset; @@ -170,6 +170,7 @@ static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UCha if (addCircumflex) letters[length++] = 0x0302; } + } if (int hundreds = (number / 100) % 10) { letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds; @@ -472,7 +473,6 @@ String listMarkerText(EListStyleType type, int value) RenderListMarker::RenderListMarker(RenderListItem* item) : RenderBox(item->document()) , m_listItem(item) - , m_selectionState(SelectionNone) { // init RenderObject attributes setInline(true); // our object is Inline @@ -485,7 +485,7 @@ RenderListMarker::~RenderListMarker() m_image->removeClient(this); } -void RenderListMarker::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType())) setNeedsLayoutAndPrefWidthsRecalc(); @@ -493,7 +493,7 @@ void RenderListMarker::styleWillChange(RenderStyle::Diff diff, const RenderStyle RenderBox::styleWillChange(diff, newStyle); } -void RenderListMarker::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBox::styleDidChange(diff, oldStyle); @@ -506,9 +506,8 @@ void RenderListMarker::styleDidChange(RenderStyle::Diff diff, const RenderStyle* } } -InlineBox* RenderListMarker::createInlineBox(bool, bool unusedIsRootLineBox, bool) +InlineBox* RenderListMarker::createInlineBox() { - ASSERT_UNUSED(unusedIsRootLineBox, !unusedIsRootLineBox); ListMarkerBox* box = new (renderArena()) ListMarkerBox(this); m_inlineBoxWrapper = box; return box; @@ -546,8 +545,10 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) paintCustomHighlight(tx, ty, style()->highlight(), true); #endif context->drawImage(m_image->image(this, marker.size()), marker.location()); - if (selectionState() != SelectionNone) + if (selectionState() != SelectionNone) { + // FIXME: selectionRect() is in absolute, not painting coordinates. context->fillRect(selectionRect(), selectionBackgroundColor()); + } return; } @@ -557,8 +558,10 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) paintCustomHighlight(tx, ty, style()->highlight(), true); #endif - if (selectionState() != SelectionNone) + if (selectionState() != SelectionNone) { + // FIXME: selectionRect() is in absolute, not painting coordinates. context->fillRect(selectionRect(), selectionBackgroundColor()); + } const Color color(style()->color()); context->setStrokeColor(color); @@ -874,14 +877,14 @@ IntRect RenderListMarker::getRelativeMarkerRect() void RenderListMarker::setSelectionState(SelectionState state) { - m_selectionState = state; + RenderBox::setSelectionState(state); if (InlineBox* box = inlineBoxWrapper()) if (RootInlineBox* root = box->root()) root->setHasSelectedChildren(state != SelectionNone); containingBlock()->setSelectionState(state); } -IntRect RenderListMarker::selectionRect(bool clipToVisibleContent) +IntRect RenderListMarker::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent) { ASSERT(!needsLayout()); @@ -892,11 +895,9 @@ IntRect RenderListMarker::selectionRect(bool clipToVisibleContent) IntRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight()); if (clipToVisibleContent) - computeAbsoluteRepaintRect(rect); - else { - FloatPoint absPos = localToAbsolute(); - rect.move(absPos.x(), absPos.y()); - } + computeRectForRepaint(repaintContainer, rect); + else + rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox(); return rect; } diff --git a/WebCore/rendering/RenderListMarker.h b/WebCore/rendering/RenderListMarker.h index 738427c..57580a8 100644 --- a/WebCore/rendering/RenderListMarker.h +++ b/WebCore/rendering/RenderListMarker.h @@ -49,7 +49,7 @@ public: virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); - virtual InlineBox* createInlineBox(bool, bool, bool); + virtual InlineBox* createInlineBox(); virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; @@ -60,16 +60,15 @@ public: bool isInside() const; - virtual SelectionState selectionState() const { return m_selectionState; } virtual void setSelectionState(SelectionState); - virtual IntRect selectionRect(bool clipToVisibleContent = true); + virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent = true); virtual bool canBeSelectionLeaf() const { return true; } void updateMargins(); protected: - virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); private: IntRect getRelativeMarkerRect(); @@ -77,7 +76,6 @@ private: String m_text; RefPtr<StyleImage> m_image; RenderListItem* m_listItem; - SelectionState m_selectionState; }; } // namespace WebCore diff --git a/WebCore/rendering/RenderMarquee.cpp b/WebCore/rendering/RenderMarquee.cpp index 2b4dcfd..48659f7 100644 --- a/WebCore/rendering/RenderMarquee.cpp +++ b/WebCore/rendering/RenderMarquee.cpp @@ -68,9 +68,9 @@ RenderMarquee::RenderMarquee(RenderLayer* l) int RenderMarquee::marqueeSpeed() const { int result = m_layer->renderer()->style()->marqueeSpeed(); - Node* elt = m_layer->renderer()->element(); - if (elt && elt->hasTagName(marqueeTag)) { - HTMLMarqueeElement* marqueeElt = static_cast<HTMLMarqueeElement*>(elt); + Node* n = m_layer->renderer()->node(); + if (n && n->hasTagName(marqueeTag)) { + HTMLMarqueeElement* marqueeElt = static_cast<HTMLMarqueeElement*>(n); result = max(result, marqueeElt->minimumDelay()); } return result; @@ -105,17 +105,18 @@ bool RenderMarquee::isHorizontal() const int RenderMarquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge) { - RenderBox* o = m_layer->renderer(); - RenderStyle* s = o->style(); + RenderBox* box = m_layer->renderBox(); + ASSERT(box); + RenderStyle* s = box->style(); if (isHorizontal()) { bool ltr = s->direction() == LTR; - int clientWidth = o->clientWidth(); - int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false); + int clientWidth = box->clientWidth(); + int contentWidth = ltr ? box->rightmostPosition(true, false) : box->leftmostPosition(true, false); if (ltr) - contentWidth += (o->paddingRight() - o->borderLeft()); + contentWidth += (box->paddingRight() - box->borderLeft()); else { - contentWidth = o->width() - contentWidth; - contentWidth += (o->paddingLeft() - o->borderRight()); + contentWidth = box->width() - contentWidth; + contentWidth += (box->paddingLeft() - box->borderRight()); } if (dir == MRIGHT) { if (stopAtContentEdge) @@ -131,9 +132,9 @@ int RenderMarquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge } } else { - int contentHeight = m_layer->renderer()->lowestPosition(true, false) - - m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom(); - int clientHeight = m_layer->renderer()->clientHeight(); + int contentHeight = box->lowestPosition(true, false) - + box->borderTop() + box->paddingBottom(); + int clientHeight = box->clientHeight(); if (dir == MUP) { if (stopAtContentEdge) return min(contentHeight - clientHeight, 0); @@ -283,7 +284,7 @@ void RenderMarquee::timerFired(Timer<RenderMarquee>*) addIncrement = !addIncrement; } bool positive = range > 0; - int clientSize = (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight()); + int clientSize = (isHorizontal() ? m_layer->renderBox()->clientWidth() : m_layer->renderBox()->clientHeight()); int increment = max(1, abs(m_layer->renderer()->style()->marqueeIncrement().calcValue(clientSize))); int currentPos = (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset()); newPos = currentPos + (addIncrement ? increment : -increment); diff --git a/WebCore/rendering/RenderMarquee.h b/WebCore/rendering/RenderMarquee.h index d9d20cd..886c343 100644 --- a/WebCore/rendering/RenderMarquee.h +++ b/WebCore/rendering/RenderMarquee.h @@ -44,7 +44,8 @@ #ifndef RenderMarquee_h #define RenderMarquee_h -#include "RenderStyle.h" +#include "Length.h" +#include "RenderStyleConstants.h" #include "Timer.h" namespace WebCore { diff --git a/WebCore/rendering/RenderMedia.cpp b/WebCore/rendering/RenderMedia.cpp index 80bf586..42cd709 100644 --- a/WebCore/rendering/RenderMedia.cpp +++ b/WebCore/rendering/RenderMedia.cpp @@ -39,7 +39,6 @@ #include "MediaControlElements.h" #include "MouseEvent.h" #include "MediaPlayer.h" -#include "RenderSlider.h" #include <wtf/CurrentTime.h> #include <wtf/MathExtras.h> @@ -89,6 +88,7 @@ void RenderMedia::destroy() removeChild(m_controlsShadowRoot->renderer()); m_controlsShadowRoot->detach(); + m_controlsShadowRoot = 0; } RenderReplaced::destroy(); } @@ -103,6 +103,28 @@ MediaPlayer* RenderMedia::player() const return mediaElement()->player(); } +void RenderMedia::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderReplaced::styleDidChange(diff, oldStyle); + + if (m_controlsShadowRoot) { + if (m_panel->renderer()) + m_panel->renderer()->setStyle(getCachedPseudoStyle(MEDIA_CONTROLS_PANEL)); + + if (m_timelineContainer->renderer()) + m_timelineContainer->renderer()->setStyle(getCachedPseudoStyle(MEDIA_CONTROLS_TIMELINE_CONTAINER)); + + m_muteButton->updateStyle(); + m_playButton->updateStyle(); + m_seekBackButton->updateStyle(); + m_seekForwardButton->updateStyle(); + m_timeline->updateStyle(); + m_fullscreenButton->updateStyle(); + m_currentTimeDisplay->updateStyle(); + m_timeRemainingDisplay->updateStyle(); + } +} + void RenderMedia::layout() { IntSize oldSize = contentBoxRect().size(); @@ -123,16 +145,16 @@ void RenderMedia::layout() } } -RenderObject* RenderMedia::firstChild() const -{ - return m_controlsShadowRoot ? m_controlsShadowRoot->renderer() : 0; +const RenderObjectChildList* RenderMedia::children() const +{ + return m_controlsShadowRoot ? m_controlsShadowRoot->renderer()->virtualChildren() : 0; } -RenderObject* RenderMedia::lastChild() const -{ - return m_controlsShadowRoot ? m_controlsShadowRoot->renderer() : 0; +RenderObjectChildList* RenderMedia::children() +{ + return m_controlsShadowRoot ? m_controlsShadowRoot->renderer()->virtualChildren() : 0; } - + void RenderMedia::removeChild(RenderObject* child) { ASSERT(m_controlsShadowRoot); @@ -150,7 +172,7 @@ void RenderMedia::createControlsShadowRoot() void RenderMedia::createPanel() { ASSERT(!m_panel); - RenderStyle* style = getCachedPseudoStyle(RenderStyle::MEDIA_CONTROLS_PANEL); + RenderStyle* style = getCachedPseudoStyle(MEDIA_CONTROLS_PANEL); m_panel = new HTMLDivElement(HTMLNames::divTag, document()); RenderObject* renderer = m_panel->createRenderer(renderArena(), style); if (renderer) { @@ -194,7 +216,7 @@ void RenderMedia::createSeekForwardButton() void RenderMedia::createTimelineContainer() { ASSERT(!m_timelineContainer); - RenderStyle* style = getCachedPseudoStyle(RenderStyle::MEDIA_CONTROLS_TIMELINE_CONTAINER); + RenderStyle* style = getCachedPseudoStyle(MEDIA_CONTROLS_TIMELINE_CONTAINER); m_timelineContainer = new HTMLDivElement(HTMLNames::divTag, document()); RenderObject* renderer = m_timelineContainer->createRenderer(renderArena(), style); if (renderer) { @@ -278,7 +300,7 @@ void RenderMedia::updateControls() createFullscreenButton(); } - if (media->paused() || media->ended() || media->networkState() < HTMLMediaElement::LOADED_METADATA) { + if (media->canPlay()) { if (m_timeUpdateTimer.isActive()) m_timeUpdateTimer.stop(); } else if (style()->visibility() == VISIBLE && m_timeline && m_timeline->renderer() && m_timeline->renderer()->style()->display() != NONE ) { @@ -350,11 +372,11 @@ void RenderMedia::updateControlVisibility() // Don't fade for audio controls. HTMLMediaElement* media = mediaElement(); - if (player() && !player()->hasVideo() || !media->isVideo()) + if (!media->hasVideo()) return; // do fading manually, css animations don't work well with shadow trees - bool visible = style()->visibility() == VISIBLE && (m_mouseOver || media->paused() || media->ended() || media->networkState() < HTMLMediaElement::LOADED_METADATA); + bool visible = style()->visibility() == VISIBLE && (m_mouseOver || media->canPlay()); if (visible == (m_opacityAnimationTo > 0)) return; @@ -403,7 +425,7 @@ void RenderMedia::forwardEvent(Event* event) { if (event->isMouseEvent() && m_controlsShadowRoot) { MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); - IntPoint point(mouseEvent->pageX(), mouseEvent->pageY()); + IntPoint point(mouseEvent->absoluteLocation()); if (m_muteButton && m_muteButton->hitTest(point)) m_muteButton->defaultEventHandler(event); @@ -427,8 +449,10 @@ void RenderMedia::forwardEvent(Event* event) updateControlVisibility(); } if (event->type() == eventNames().mouseoutEvent) { - // FIXME: moving over scrollbar thumb generates mouseout for the ancestor media element for some reason - m_mouseOver = absoluteBoundingBoxRect().contains(point); + // When the scrollbar thumb captures mouse events, we should treat the mouse as still being over our renderer if the new target is a descendant + Node* mouseOverNode = mouseEvent->relatedTarget() ? mouseEvent->relatedTarget()->toNode() : 0; + RenderObject* mouseOverRenderer = mouseOverNode ? mouseOverNode->renderer() : 0; + m_mouseOver = mouseOverRenderer && mouseOverRenderer->isDescendantOf(this); updateControlVisibility(); } } @@ -440,7 +464,7 @@ int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return bottom; - return max(bottom, m_controlsShadowRoot->renderBox()->y() + m_controlsShadowRoot->renderer()->lowestPosition(includeOverflowInterior, includeSelf)); + return max(bottom, m_controlsShadowRoot->renderBox()->y() + m_controlsShadowRoot->renderBox()->lowestPosition(includeOverflowInterior, includeSelf)); } int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const @@ -449,7 +473,7 @@ int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSel if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return right; - return max(right, m_controlsShadowRoot->renderBox()->x() + m_controlsShadowRoot->renderer()->rightmostPosition(includeOverflowInterior, includeSelf)); + return max(right, m_controlsShadowRoot->renderBox()->x() + m_controlsShadowRoot->renderBox()->rightmostPosition(includeOverflowInterior, includeSelf)); } int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const @@ -458,7 +482,7 @@ int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return left; - return min(left, m_controlsShadowRoot->renderBox()->x() + m_controlsShadowRoot->renderer()->leftmostPosition(includeOverflowInterior, includeSelf)); + return min(left, m_controlsShadowRoot->renderBox()->x() + m_controlsShadowRoot->renderBox()->leftmostPosition(includeOverflowInterior, includeSelf)); } } // namespace WebCore diff --git a/WebCore/rendering/RenderMedia.h b/WebCore/rendering/RenderMedia.h index a4670e9..0e56dab 100644 --- a/WebCore/rendering/RenderMedia.h +++ b/WebCore/rendering/RenderMedia.h @@ -49,8 +49,11 @@ public: RenderMedia(HTMLMediaElement*, const IntSize& intrinsicSize); virtual ~RenderMedia(); - virtual RenderObject* firstChild() const; - virtual RenderObject* lastChild() const; + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const; + RenderObjectChildList* children(); + virtual void removeChild(RenderObject*); virtual void destroy(); @@ -95,6 +98,8 @@ private: void changeOpacity(HTMLElement*, float opacity); void opacityAnimationTimerFired(Timer<RenderMedia>*); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + RefPtr<HTMLElement> m_controlsShadowRoot; RefPtr<HTMLElement> m_panel; RefPtr<MediaControlMuteButtonElement> m_muteButton; @@ -106,8 +111,8 @@ private: RefPtr<HTMLElement> m_timelineContainer; RefPtr<MediaTimeDisplayElement> m_currentTimeDisplay; RefPtr<MediaTimeDisplayElement> m_timeRemainingDisplay; - EventTargetNode* m_lastUnderNode; - EventTargetNode* m_nodeUnderMouse; + Node* m_lastUnderNode; + Node* m_nodeUnderMouse; Timer<RenderMedia> m_timeUpdateTimer; Timer<RenderMedia> m_opacityAnimationTimer; diff --git a/WebCore/rendering/RenderMenuList.cpp b/WebCore/rendering/RenderMenuList.cpp index 71e9e15..d61c16b 100644 --- a/WebCore/rendering/RenderMenuList.cpp +++ b/WebCore/rendering/RenderMenuList.cpp @@ -115,7 +115,7 @@ void RenderMenuList::removeChild(RenderObject* oldChild) m_innerBlock->removeChild(oldChild); } -void RenderMenuList::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderMenuList::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); @@ -143,7 +143,15 @@ void RenderMenuList::updateOptionsWidth() continue; String text = optionElement->textIndentedToRespectGroupLabel(); - if (!text.isEmpty()) + if (theme()->popupOptionSupportsTextIndent()) { + // Add in the option's text indent. We can't calculate percentage values for now. + float optionWidth = 0; + if (RenderStyle* optionStyle = element->renderStyle()) + optionWidth += optionStyle->textIndent().calcMinValue(0); + if (!text.isEmpty()) + optionWidth += style()->font().floatWidth(text); + maxOptionWidth = max(maxOptionWidth, optionWidth); + } else if (!text.isEmpty()) maxOptionWidth = max(maxOptionWidth, style()->font().floatWidth(text)); } @@ -315,9 +323,15 @@ bool RenderMenuList::itemIsEnabled(unsigned listIndex) const if (!element->hasTagName(optionTag)) return false; bool groupEnabled = true; - if (element->parentNode() && element->parentNode()->hasTagName(optgroupTag)) - groupEnabled = element->parentNode()->isEnabled(); - return element->isEnabled() && groupEnabled; + if (element->parentNode() && element->parentNode()->hasTagName(optgroupTag)) { + FormControlElement* formControlElement = toFormControlElement(static_cast<Element*>(element->parentNode())); + groupEnabled = formControlElement->isEnabled(); + } + + if (!groupEnabled) + return false; + + return toFormControlElement(element)->isEnabled(); } PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const @@ -326,7 +340,7 @@ PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const HTMLElement* element = select->listItems()[listIndex]; RenderStyle* style = element->renderStyle() ? element->renderStyle() : element->computedStyle(); - return style ? PopupMenuStyle(style->color(), itemBackgroundColor(listIndex), style->font(), style->visibility() == VISIBLE) : menuStyle(); + return style ? PopupMenuStyle(style->color(), itemBackgroundColor(listIndex), style->font(), style->visibility() == VISIBLE, style->textIndent(), style->direction()) : menuStyle(); } Color RenderMenuList::itemBackgroundColor(unsigned listIndex) const @@ -354,7 +368,7 @@ PopupMenuStyle RenderMenuList::menuStyle() const { RenderStyle* s = m_innerBlock ? m_innerBlock->style() : style(); - return PopupMenuStyle(s->color(), s->backgroundColor(), s->font(), s->visibility() == VISIBLE); + return PopupMenuStyle(s->color(), s->backgroundColor(), s->font(), s->visibility() == VISIBLE, s->textIndent(), s->direction()); } HostWindow* RenderMenuList::hostWindow() const @@ -365,7 +379,7 @@ HostWindow* RenderMenuList::hostWindow() const PassRefPtr<Scrollbar> RenderMenuList::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) { RefPtr<Scrollbar> widget; - bool hasCustomScrollbarStyle = style()->hasPseudoStyle(RenderStyle::SCROLLBAR); + bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR); if (hasCustomScrollbarStyle) widget = RenderScrollbar::createCustomScrollbar(client, orientation, this); else diff --git a/WebCore/rendering/RenderMenuList.h b/WebCore/rendering/RenderMenuList.h index a7530fa..f16525e 100644 --- a/WebCore/rendering/RenderMenuList.h +++ b/WebCore/rendering/RenderMenuList.h @@ -72,7 +72,7 @@ public: String text() const; private: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); // PopupMenuClient methods virtual String itemText(unsigned listIndex) const; diff --git a/WebCore/rendering/RenderObject.cpp b/WebCore/rendering/RenderObject.cpp index ea84f33..bb98c9f 100644 --- a/WebCore/rendering/RenderObject.cpp +++ b/WebCore/rendering/RenderObject.cpp @@ -28,6 +28,7 @@ #include "AXObjectCache.h" #include "CSSStyleSelector.h" #include "FloatQuad.h" +#include "Frame.h" #include "FrameView.h" #include "GraphicsContext.h" #include "HTMLNames.h" @@ -44,12 +45,18 @@ #include "RenderTableRow.h" #include "RenderTheme.h" #include "RenderView.h" +#include "TransformState.h" #include <algorithm> #ifdef ANDROID_LAYOUT #include "Settings.h" #endif #include <stdio.h> #include <wtf/RefCountedLeakCounter.h> +#include <wtf/UnusedParam.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerCompositor.h" +#endif #if ENABLE(WML) #include "WMLNames.h" @@ -89,10 +96,10 @@ RenderObject* RenderObject::createObject(Node* node, RenderStyle* style) // Works only if we have exactly one piece of content and it's a URL. // Otherwise acts as if we didn't support this feature. const ContentData* contentData = style->contentData(); - if (contentData && !contentData->m_next && contentData->m_type == CONTENT_OBJECT && doc != node) { + if (contentData && !contentData->next() && contentData->isImage() && doc != node) { RenderImageGeneratedContent* image = new (arena) RenderImageGeneratedContent(node); image->setStyle(style); - if (StyleImage* styleImage = contentData->m_content.m_image) + if (StyleImage* styleImage = contentData->image()) image->setStyleImage(styleImage); return image; } @@ -164,7 +171,6 @@ RenderObject::RenderObject(Node* node) , m_hasAXObject(false) , m_setNeedsLayoutForbidden(false) #endif - , m_verticalPosition(PositionUndefined) , m_needsLayout(false) , m_needsPositionedMovementLayout(false) , m_normalChildNeedsLayout(false) @@ -187,10 +193,19 @@ RenderObject::RenderObject(Node* node) , m_hasOverrideSize(false) , m_hasCounterNodeMap(false) , m_everHadLayout(false) + , m_childrenInline(false) + , m_topMarginQuirk(false) + , m_bottomMarginQuirk(false) + , m_hasMarkupTruncation(false) + , m_selectionState(SelectionNone) + , m_hasColumns(false) + , m_cellWidthChanged(false) + , m_replacedHasOverflow(false) { #ifndef NDEBUG renderObjectCounter.increment(); #endif + ASSERT(node); } RenderObject::~RenderObject() @@ -213,58 +228,95 @@ bool RenderObject::isDescendantOf(const RenderObject* obj) const bool RenderObject::isBody() const { - return node()->hasTagName(bodyTag); + return node() && node()->hasTagName(bodyTag); } bool RenderObject::isHR() const { - return element() && element()->hasTagName(hrTag); + return node() && node()->hasTagName(hrTag); } bool RenderObject::isHTMLMarquee() const { - return element() && element()->renderer() == this && element()->hasTagName(marqueeTag); -} - -bool RenderObject::canHaveChildren() const -{ - return false; -} - -bool RenderObject::isInlineContinuation() const -{ - return false; + return node() && node()->renderer() == this && node()->hasTagName(marqueeTag); } -void RenderObject::addChild(RenderObject*, RenderObject*) +static void updateListMarkerNumbers(RenderObject* child) { - ASSERT_NOT_REACHED(); + for (RenderObject* r = child; r; r = r->nextSibling()) + if (r->isListItem()) + static_cast<RenderListItem*>(r)->updateValue(); } -RenderObject* RenderObject::removeChildNode(RenderObject*, bool) +void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) { - ASSERT_NOT_REACHED(); - return 0; -} - -void RenderObject::removeChild(RenderObject*) -{ - ASSERT_NOT_REACHED(); -} + RenderObjectChildList* children = virtualChildren(); + ASSERT(children); + if (!children) + return; -void RenderObject::moveChildNode(RenderObject*) -{ - ASSERT_NOT_REACHED(); + bool needsTable = false; + + if (newChild->isListItem()) + updateListMarkerNumbers(beforeChild ? beforeChild : children->lastChild()); + else if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) + needsTable = !isTable(); + else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) + needsTable = !isTable(); + else if (newChild->isTableSection()) + needsTable = !isTable(); + else if (newChild->isTableRow()) + needsTable = !isTableSection(); + else if (newChild->isTableCell()) { + needsTable = !isTableRow(); + // I'm not 100% sure this is the best way to fix this, but without this + // change we recurse infinitely when trying to render the CSS2 test page: + // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html. + // See Radar 2925291. + if (needsTable && isTableCell() && !children->firstChild() && !newChild->isTableCell()) + needsTable = false; + } + + if (needsTable) { + RenderTable* table; + RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : children->lastChild(); + if (afterChild && afterChild->isAnonymous() && afterChild->isTable()) + table = static_cast<RenderTable*>(afterChild); + else { + table = new (renderArena()) RenderTable(document() /* is anonymous */); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(TABLE); + table->setStyle(newStyle.release()); + addChild(table, beforeChild); + } + table->addChild(newChild); + } else { + // Just add it... + children->insertChildNode(this, newChild, beforeChild); + } + + if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { + RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalText(); + if (textToTransform) + toRenderText(newChild)->setText(textToTransform.release(), true); + } } -void RenderObject::appendChildNode(RenderObject*, bool) +void RenderObject::removeChild(RenderObject* oldChild) { - ASSERT_NOT_REACHED(); -} + RenderObjectChildList* children = virtualChildren(); + ASSERT(children); + if (!children) + return; -void RenderObject::insertChildNode(RenderObject*, RenderObject*, bool) -{ - ASSERT_NOT_REACHED(); + // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode + // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on + // layout anyway). + if (oldChild->isFloatingOrPositioned()) + toRenderBox(oldChild)->removeFloatingOrPositionedChildFromBlockLists(); + + children->removeChildNode(this, oldChild); } RenderObject* RenderObject::nextInPreOrder() const @@ -336,20 +388,6 @@ RenderObject* RenderObject::childAt(unsigned index) const return child; } -bool RenderObject::isEditable() const -{ - RenderText* textRenderer = 0; - if (isText()) - textRenderer = toRenderText(const_cast<RenderObject*>(this)); - - return style()->visibility() == VISIBLE && - element() && element()->isContentEditable() && - ((isBlockFlow() && !firstChild()) || - isReplaced() || - isBR() || - (textRenderer && textRenderer->firstTextBox())); -} - RenderObject* RenderObject::firstLeafChild() const { RenderObject* r = firstChild(); @@ -387,7 +425,7 @@ static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject* beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject); newObject = 0; } - parentLayer->addChild(toRenderBox(obj)->layer(), beforeChild); + parentLayer->addChild(toRenderBoxModelObject(obj)->layer(), beforeChild); return; } @@ -411,7 +449,7 @@ void RenderObject::removeLayers(RenderLayer* parentLayer) return; if (hasLayer()) { - parentLayer->removeChild(toRenderBox(this)->layer()); + parentLayer->removeChild(toRenderBoxModelObject(this)->layer()); return; } @@ -425,7 +463,7 @@ void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) return; if (hasLayer()) { - RenderLayer* layer = toRenderBox(this)->layer(); + RenderLayer* layer = toRenderBoxModelObject(this)->layer(); if (oldParent) oldParent->removeChild(layer); newParent->addChild(layer); @@ -444,7 +482,7 @@ RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* return 0; // Step 1: If our layer is a child of the desired parent, then return our layer. - RenderLayer* ourLayer = hasLayer() ? toRenderBox(this)->layer() : 0; + RenderLayer* ourLayer = hasLayer() ? toRenderBoxModelObject(this)->layer() : 0; if (ourLayer && ourLayer->parent() == parentLayer) return ourLayer; @@ -476,7 +514,7 @@ RenderLayer* RenderObject::enclosingLayer() const { const RenderObject* curr = this; while (curr) { - RenderLayer* layer = curr->hasLayer() ? toRenderBox(curr)->layer() : 0; + RenderLayer* layer = curr->hasLayer() ? toRenderBoxModelObject(curr)->layer() : 0; if (layer) return layer; curr = curr->parent(); @@ -484,6 +522,18 @@ RenderLayer* RenderObject::enclosingLayer() const return 0; } +RenderLayer* RenderObject::enclosingSelfPaintingLayer() const +{ + const RenderObject* curr = this; + while (curr) { + RenderLayer* layer = curr->hasLayer() ? toRenderBoxModelObject(curr)->layer() : 0; + if (layer && layer->isSelfPaintingLayer()) + return layer; + curr = curr->parent(); + } + return 0; +} + RenderBox* RenderObject::enclosingBox() const { RenderObject* curr = const_cast<RenderObject*>(this); @@ -502,16 +552,6 @@ RenderBlock* RenderObject::firstLineBlock() const return 0; } -bool RenderObject::hasStaticX() const -{ - return (style()->left().isAuto() && style()->right().isAuto()) || style()->left().isStatic() || style()->right().isStatic(); -} - -bool RenderObject::hasStaticY() const -{ - return (style()->top().isAuto() && style()->bottom().isAuto()) || style()->top().isStatic(); -} - void RenderObject::setPrefWidthsDirty(bool b, bool markParents) { bool alreadyDirty = m_prefWidthsDirty; @@ -526,113 +566,25 @@ void RenderObject::invalidateContainerPrefWidths() // in the chain that we mark dirty (even though they're kind of irrelevant). RenderObject* o = isTableCell() ? containingBlock() : container(); while (o && !o->m_prefWidthsDirty) { + // Don't invalidate the outermost object of an unrooted subtree. That object will be + // invalidated when the subtree is added to the document. + RenderObject* container = o->isTableCell() ? o->containingBlock() : o->container(); + if (!container && !o->isRenderView()) + break; + o->m_prefWidthsDirty = true; if (o->style()->position() == FixedPosition || o->style()->position() == AbsolutePosition) // A positioned object has no effect on the min/max width of its containing block ever. // We can optimize this case and not go up any further. break; - o = o->isTableCell() ? o->containingBlock() : o->container(); - } -} - -void RenderObject::setNeedsLayout(bool b, bool markParents) -{ - bool alreadyNeededLayout = m_needsLayout; - m_needsLayout = b; - if (b) { - ASSERT(!isSetNeedsLayoutForbidden()); - if (!alreadyNeededLayout) { - if (markParents) - markContainingBlocksForLayout(); - if (hasLayer()) - toRenderBox(this)->layer()->setNeedsFullRepaint(); - } - } else { - m_everHadLayout = true; - m_posChildNeedsLayout = false; - m_normalChildNeedsLayout = false; - m_needsPositionedMovementLayout = false; - } -} - -void RenderObject::setChildNeedsLayout(bool b, bool markParents) -{ - bool alreadyNeededLayout = m_normalChildNeedsLayout; - m_normalChildNeedsLayout = b; - if (b) { - ASSERT(!isSetNeedsLayoutForbidden()); - if (!alreadyNeededLayout && markParents) - markContainingBlocksForLayout(); - } else { - m_posChildNeedsLayout = false; - m_normalChildNeedsLayout = false; - m_needsPositionedMovementLayout = false; - } -} - -void RenderObject::setNeedsPositionedMovementLayout() -{ - bool alreadyNeededLayout = needsLayout(); - m_needsPositionedMovementLayout = true; - if (!alreadyNeededLayout) { - markContainingBlocksForLayout(); - if (hasLayer()) - toRenderBox(this)->layer()->setNeedsFullRepaint(); + o = container; } } -static inline bool objectIsRelayoutBoundary(const RenderObject *obj) +void RenderObject::setLayerNeedsFullRepaint() { - // FIXME: In future it may be possible to broaden this condition in order to improve performance. - // Table cells are excluded because even when their CSS height is fixed, their height() - // may depend on their contents. - return obj->isTextField() || obj->isTextArea() - || obj->hasOverflowClip() && !obj->style()->width().isIntrinsicOrAuto() && !obj->style()->height().isIntrinsicOrAuto() && !obj->style()->height().isPercent() && !obj->isTableCell() -#if ENABLE(SVG) - || obj->isSVGRoot() -#endif - ; -} - -void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderObject* newRoot) -{ - ASSERT(!scheduleRelayout || !newRoot); - - RenderObject* o = container(); - RenderObject* last = this; - - while (o) { - if (!last->isText() && (last->style()->position() == FixedPosition || last->style()->position() == AbsolutePosition)) { - if (last->hasStaticY()) { - RenderObject* parent = last->parent(); - if (!parent->normalChildNeedsLayout()) { - parent->setChildNeedsLayout(true, false); - if (parent != newRoot) - parent->markContainingBlocksForLayout(scheduleRelayout, newRoot); - } - } - if (o->m_posChildNeedsLayout) - return; - o->m_posChildNeedsLayout = true; - ASSERT(!o->isSetNeedsLayoutForbidden()); - } else { - if (o->m_normalChildNeedsLayout) - return; - o->m_normalChildNeedsLayout = true; - ASSERT(!o->isSetNeedsLayoutForbidden()); - } - - if (o == newRoot) - return; - - last = o; - if (scheduleRelayout && objectIsRelayoutBoundary(last)) - break; - o = o->container(); - } - - if (scheduleRelayout) - last->scheduleRelayout(); + ASSERT(hasLayer()); + toRenderBoxModelObject(this)->layer()->setNeedsFullRepaint(true); } RenderBlock* RenderObject::containingBlock() const @@ -645,7 +597,7 @@ RenderBlock* RenderObject::containingBlock() const } if (isRenderView()) - return const_cast<RenderBlock*>(static_cast<const RenderBlock*>(this)); + return const_cast<RenderView*>(toRenderView(this)); RenderObject* o = parent(); if (!isText() && m_style->position() == FixedPosition) { @@ -675,19 +627,7 @@ RenderBlock* RenderObject::containingBlock() const if (!o || !o->isRenderBlock()) return 0; // Probably doesn't happen any more, but leave just in case. -dwh - return static_cast<RenderBlock*>(o); -} - -int RenderObject::containingBlockWidth() const -{ - // FIXME ? - return containingBlock()->availableWidth(); -} - -int RenderObject::containingBlockHeight() const -{ - // FIXME ? - return containingBlock()->contentHeight(); + return toRenderBlock(o); } static bool mustRepaintFillLayers(const RenderObject* renderer, const FillLayer* layer) @@ -736,97 +676,9 @@ bool RenderObject::mustRepaintBackgroundOrBorder() const return false; } -void RenderObject::drawBorderArc(GraphicsContext* graphicsContext, int x, int y, float thickness, IntSize radius, - int angleStart, int angleSpan, BorderSide s, Color c, const Color& textColor, - EBorderStyle style, bool firstCorner) -{ - if ((style == DOUBLE && thickness / 2 < 3) || ((style == RIDGE || style == GROOVE) && thickness / 2 < 2)) - style = SOLID; - - if (!c.isValid()) { - if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE) - c.setRGB(238, 238, 238); - else - c = textColor; - } - - switch (style) { - case BNONE: - case BHIDDEN: - return; - case DOTTED: - case DASHED: - graphicsContext->setStrokeColor(c); - graphicsContext->setStrokeStyle(style == DOTTED ? DottedStroke : DashedStroke); - graphicsContext->setStrokeThickness(thickness); - graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); - break; - case DOUBLE: { - float third = thickness / 3.0f; - float innerThird = (thickness + 1.0f) / 6.0f; - int shiftForInner = static_cast<int>(innerThird * 2.5f); - - int outerY = y; - int outerHeight = radius.height() * 2; - int innerX = x + shiftForInner; - int innerY = y + shiftForInner; - int innerWidth = (radius.width() - shiftForInner) * 2; - int innerHeight = (radius.height() - shiftForInner) * 2; - if (innerThird > 1 && (s == BSTop || (firstCorner && (s == BSLeft || s == BSRight)))) { - outerHeight += 2; - innerHeight += 2; - } - - graphicsContext->setStrokeStyle(SolidStroke); - graphicsContext->setStrokeColor(c); - graphicsContext->setStrokeThickness(third); - graphicsContext->strokeArc(IntRect(x, outerY, radius.width() * 2, outerHeight), angleStart, angleSpan); - graphicsContext->setStrokeThickness(innerThird > 2 ? innerThird - 1 : innerThird); - graphicsContext->strokeArc(IntRect(innerX, innerY, innerWidth, innerHeight), angleStart, angleSpan); - break; - } - case GROOVE: - case RIDGE: { - Color c2; - if ((style == RIDGE && (s == BSTop || s == BSLeft)) || - (style == GROOVE && (s == BSBottom || s == BSRight))) - c2 = c.dark(); - else { - c2 = c; - c = c.dark(); - } - - graphicsContext->setStrokeStyle(SolidStroke); - graphicsContext->setStrokeColor(c); - graphicsContext->setStrokeThickness(thickness); - graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); - - float halfThickness = (thickness + 1.0f) / 4.0f; - int shiftForInner = static_cast<int>(halfThickness * 1.5f); - graphicsContext->setStrokeColor(c2); - graphicsContext->setStrokeThickness(halfThickness > 2 ? halfThickness - 1 : halfThickness); - graphicsContext->strokeArc(IntRect(x + shiftForInner, y + shiftForInner, (radius.width() - shiftForInner) * 2, - (radius.height() - shiftForInner) * 2), angleStart, angleSpan); - break; - } - case INSET: - if (s == BSTop || s == BSLeft) - c = c.dark(); - case OUTSET: - if (style == OUTSET && (s == BSBottom || s == BSRight)) - c = c.dark(); - case SOLID: - graphicsContext->setStrokeStyle(SolidStroke); - graphicsContext->setStrokeColor(c); - graphicsContext->setStrokeThickness(thickness); - graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); - break; - } -} - -void RenderObject::drawBorder(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, - BorderSide s, Color c, const Color& textcolor, EBorderStyle style, - int adjbw1, int adjbw2) +void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, + BoxSide s, Color c, const Color& textcolor, EBorderStyle style, + int adjbw1, int adjbw2) { int width = (s == BSTop || s == BSBottom ? y2 - y1 : x2 - x1); @@ -889,34 +741,34 @@ void RenderObject::drawBorder(GraphicsContext* graphicsContext, int x1, int y1, switch (s) { case BSTop: - drawBorder(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), + drawLineForBoxSide(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), y1, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y1 + third, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); - drawBorder(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), + drawLineForBoxSide(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), y2 - third, x2 - max((adjbw2 * 2 + 1) / 3, 0), y2, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); break; case BSLeft: - drawBorder(graphicsContext, x1, y1 + max((-adjbw1 * 2 + 1) / 3, 0), + drawLineForBoxSide(graphicsContext, x1, y1 + max((-adjbw1 * 2 + 1) / 3, 0), x1 + third, y2 - max((-adjbw2 * 2 + 1) / 3, 0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); - drawBorder(graphicsContext, x2 - third, y1 + max((adjbw1 * 2 + 1) / 3, 0), + drawLineForBoxSide(graphicsContext, x2 - third, y1 + max((adjbw1 * 2 + 1) / 3, 0), x2, y2 - max((adjbw2 * 2 + 1) / 3, 0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); break; case BSBottom: - drawBorder(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), + drawLineForBoxSide(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), y1, x2 - max((adjbw2 * 2 + 1) / 3, 0), y1 + third, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); - drawBorder(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), + drawLineForBoxSide(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), y2 - third, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y2, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); break; case BSRight: - drawBorder(graphicsContext, x1, y1 + max((adjbw1 * 2 + 1) / 3, 0), + drawLineForBoxSide(graphicsContext, x1, y1 + max((adjbw1 * 2 + 1) / 3, 0), x1 + third, y2 - max(( adjbw2 * 2 + 1) / 3, 0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); - drawBorder(graphicsContext, x2 - third, y1 + max((-adjbw1 * 2 + 1) / 3, 0), + drawLineForBoxSide(graphicsContext, x2 - third, y1 + max((-adjbw1 * 2 + 1) / 3, 0), x2, y2 - max((-adjbw2 * 2 + 1) / 3, 0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); break; @@ -944,27 +796,27 @@ void RenderObject::drawBorder(GraphicsContext* graphicsContext, int x1, int y1, switch (s) { case BSTop: - drawBorder(graphicsContext, x1 + max(-adjbw1, 0) / 2, y1, x2 - max(-adjbw2, 0) / 2, (y1 + y2 + 1) / 2, + drawLineForBoxSide(graphicsContext, x1 + max(-adjbw1, 0) / 2, y1, x2 - max(-adjbw2, 0) / 2, (y1 + y2 + 1) / 2, s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); - drawBorder(graphicsContext, x1 + max(adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(adjbw2 + 1, 0) / 2, y2, + drawLineForBoxSide(graphicsContext, x1 + max(adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(adjbw2 + 1, 0) / 2, y2, s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); break; case BSLeft: - drawBorder(graphicsContext, x1, y1 + max(-adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(-adjbw2, 0) / 2, + drawLineForBoxSide(graphicsContext, x1, y1 + max(-adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(-adjbw2, 0) / 2, s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); - drawBorder(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(adjbw1 + 1, 0) / 2, x2, y2 - max(adjbw2 + 1, 0) / 2, + drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(adjbw1 + 1, 0) / 2, x2, y2 - max(adjbw2 + 1, 0) / 2, s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); break; case BSBottom: - drawBorder(graphicsContext, x1 + max(adjbw1, 0) / 2, y1, x2 - max(adjbw2, 0) / 2, (y1 + y2 + 1) / 2, + drawLineForBoxSide(graphicsContext, x1 + max(adjbw1, 0) / 2, y1, x2 - max(adjbw2, 0) / 2, (y1 + y2 + 1) / 2, s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); - drawBorder(graphicsContext, x1 + max(-adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(-adjbw2 + 1, 0) / 2, y2, + drawLineForBoxSide(graphicsContext, x1 + max(-adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(-adjbw2 + 1, 0) / 2, y2, s, c, textcolor, s1, adjbw1/2, adjbw2/2); break; case BSRight: - drawBorder(graphicsContext, x1, y1 + max(adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(adjbw2, 0) / 2, + drawLineForBoxSide(graphicsContext, x1, y1 + max(adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(adjbw2, 0) / 2, s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); - drawBorder(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(-adjbw1 + 1, 0) / 2, x2, y2 - max(-adjbw2 + 1, 0) / 2, + drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(-adjbw1 + 1, 0) / 2, x2, y2 - max(-adjbw2 + 1, 0) / 2, s, c, textcolor, s1, adjbw1/2, adjbw2/2); break; } @@ -1020,516 +872,91 @@ void RenderObject::drawBorder(GraphicsContext* graphicsContext, int x1, int y1, } } -bool RenderObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style, - const NinePieceImage& ninePieceImage, CompositeOperator op) -{ - StyleImage* styleImage = ninePieceImage.image(); - if (!styleImage || !styleImage->canRender(style->effectiveZoom())) - return false; - - if (!styleImage->isLoaded()) - return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either. - - // If we have a border radius, the image gets clipped to the rounded rect. - bool clipped = false; - if (style->hasBorderRadius()) { - IntRect clipRect(tx, ty, w, h); - graphicsContext->save(); - graphicsContext->addRoundedRectClip(clipRect, style->borderTopLeftRadius(), style->borderTopRightRadius(), - style->borderBottomLeftRadius(), style->borderBottomRightRadius()); - clipped = true; - } - - // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function - // doesn't have any understanding of the zoom that is in effect on the tile. - styleImage->setImageContainerSize(IntSize(w, h)); - IntSize imageSize = styleImage->imageSize(this, 1.0f); - int imageWidth = imageSize.width(); - int imageHeight = imageSize.height(); - - int topSlice = min(imageHeight, ninePieceImage.m_slices.top().calcValue(imageHeight)); - int bottomSlice = min(imageHeight, ninePieceImage.m_slices.bottom().calcValue(imageHeight)); - int leftSlice = min(imageWidth, ninePieceImage.m_slices.left().calcValue(imageWidth)); - int rightSlice = min(imageWidth, ninePieceImage.m_slices.right().calcValue(imageWidth)); - - ENinePieceImageRule hRule = ninePieceImage.horizontalRule(); - ENinePieceImageRule vRule = ninePieceImage.verticalRule(); - - bool fitToBorder = style->borderImage() == ninePieceImage; - - int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice; - int topWidth = fitToBorder ? style->borderTopWidth() : topSlice; - int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice; - int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice; - - bool drawLeft = leftSlice > 0 && leftWidth > 0; - bool drawTop = topSlice > 0 && topWidth > 0; - bool drawRight = rightSlice > 0 && rightWidth > 0; - bool drawBottom = bottomSlice > 0 && bottomWidth > 0; - bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 && - (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0; - - Image* image = styleImage->image(this, imageSize); - - if (drawLeft) { - // Paint the top and bottom left corners. - - // The top left corner rect is (tx, ty, leftWidth, topWidth) - // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice) - if (drawTop) - graphicsContext->drawImage(image, IntRect(tx, ty, leftWidth, topWidth), - IntRect(0, 0, leftSlice, topSlice), op); - - // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth) - // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice) - if (drawBottom) - graphicsContext->drawImage(image, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth), - IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op); - - // Paint the left edge. - // Have to scale and tile into the border rect. - graphicsContext->drawTiledImage(image, IntRect(tx, ty + topWidth, leftWidth, - h - topWidth - bottomWidth), - IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice), - Image::StretchTile, (Image::TileRule)vRule, op); - } - - if (drawRight) { - // Paint the top and bottom right corners - // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth) - // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice) - if (drawTop) - graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth), - IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op); - - // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth) - // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice) - if (drawBottom) - graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth), - IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op); - - // Paint the right edge. - graphicsContext->drawTiledImage(image, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth, - h - topWidth - bottomWidth), - IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice), - Image::StretchTile, (Image::TileRule)vRule, op); - } - - // Paint the top edge. - if (drawTop) - graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth), - IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice), - (Image::TileRule)hRule, Image::StretchTile, op); - - // Paint the bottom edge. - if (drawBottom) - graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + h - bottomWidth, - w - leftWidth - rightWidth, bottomWidth), - IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice), - (Image::TileRule)hRule, Image::StretchTile, op); - - // Paint the middle. - if (drawMiddle) - graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth, - h - topWidth - bottomWidth), - IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice), - (Image::TileRule)hRule, (Image::TileRule)vRule, op); - - // Clear the clip for the border radius. - if (clipped) - graphicsContext->restore(); - - return true; -} - -void RenderObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, - const RenderStyle* style, bool begin, bool end) +void RenderObject::drawArcForBoxSide(GraphicsContext* graphicsContext, int x, int y, float thickness, IntSize radius, + int angleStart, int angleSpan, BoxSide s, Color c, const Color& textColor, + EBorderStyle style, bool firstCorner) { - if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) - return; - - const Color& tc = style->borderTopColor(); - const Color& bc = style->borderBottomColor(); - const Color& lc = style->borderLeftColor(); - const Color& rc = style->borderRightColor(); - - bool tt = style->borderTopIsTransparent(); - bool bt = style->borderBottomIsTransparent(); - bool rt = style->borderRightIsTransparent(); - bool lt = style->borderLeftIsTransparent(); - - EBorderStyle ts = style->borderTopStyle(); - EBorderStyle bs = style->borderBottomStyle(); - EBorderStyle ls = style->borderLeftStyle(); - EBorderStyle rs = style->borderRightStyle(); - - bool renderTop = ts > BHIDDEN && !tt; - bool renderLeft = ls > BHIDDEN && begin && !lt; - bool renderRight = rs > BHIDDEN && end && !rt; - bool renderBottom = bs > BHIDDEN && !bt; - - // Need sufficient width and height to contain border radius curves. Sanity check our border radii - // and our width/height values to make sure the curves can all fit. If not, then we won't paint - // any border radii. - bool renderRadii = false; - IntSize topLeft = style->borderTopLeftRadius(); - IntSize topRight = style->borderTopRightRadius(); - IntSize bottomLeft = style->borderBottomLeftRadius(); - IntSize bottomRight = style->borderBottomRightRadius(); - - if (style->hasBorderRadius() && - static_cast<unsigned>(w) >= static_cast<unsigned>(topLeft.width()) + static_cast<unsigned>(topRight.width()) && - static_cast<unsigned>(w) >= static_cast<unsigned>(bottomLeft.width()) + static_cast<unsigned>(bottomRight.width()) && - static_cast<unsigned>(h) >= static_cast<unsigned>(topLeft.height()) + static_cast<unsigned>(bottomLeft.height()) && - static_cast<unsigned>(h) >= static_cast<unsigned>(topRight.height()) + static_cast<unsigned>(bottomRight.height())) - renderRadii = true; - - // Clip to the rounded rectangle. - if (renderRadii) { - graphicsContext->save(); - graphicsContext->addRoundedRectClip(IntRect(tx, ty, w, h), topLeft, topRight, bottomLeft, bottomRight); - } - - int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan; - float thickness; - bool upperLeftBorderStylesMatch = renderLeft && (ts == ls) && (tc == lc); - bool upperRightBorderStylesMatch = renderRight && (ts == rs) && (tc == rc) && (ts != OUTSET) && (ts != RIDGE) && (ts != INSET) && (ts != GROOVE); - bool lowerLeftBorderStylesMatch = renderLeft && (bs == ls) && (bc == lc) && (bs != OUTSET) && (bs != RIDGE) && (bs != INSET) && (bs != GROOVE); - bool lowerRightBorderStylesMatch = renderRight && (bs == rs) && (bc == rc); - - if (renderTop) { - bool ignore_left = (renderRadii && topLeft.width() > 0) || - (tc == lc && tt == lt && ts >= OUTSET && - (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); - - bool ignore_right = (renderRadii && topRight.width() > 0) || - (tc == rc && tt == rt && ts >= OUTSET && - (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); - - int x = tx; - int x2 = tx + w; - if (renderRadii) { - x += topLeft.width(); - x2 -= topRight.width(); - } - - drawBorder(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, - ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); - - if (renderRadii) { - int leftY = ty; - - // We make the arc double thick and let the clip rect take care of clipping the extra off. - // We're doing this because it doesn't seem possible to match the curve of the clip exactly - // with the arc-drawing function. - thickness = style->borderTopWidth() * 2; - - if (topLeft.width()) { - int leftX = tx; - // The inner clip clips inside the arc. This is especially important for 1px borders. - bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width()) - && (style->borderTopWidth() < topLeft.height()) - && (ts != DOUBLE || style->borderTopWidth() > 6); - if (applyLeftInnerClip) { - graphicsContext->save(); - graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2), - style->borderTopWidth()); - } - - firstAngleStart = 90; - firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45; - - // Draw upper left arc - drawBorderArc(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan, - BSTop, tc, style->color(), ts, true); - if (applyLeftInnerClip) - graphicsContext->restore(); - } - - if (topRight.width()) { - int rightX = tx + w - topRight.width() * 2; - bool applyRightInnerClip = (style->borderRightWidth() < topRight.width()) - && (style->borderTopWidth() < topRight.height()) - && (ts != DOUBLE || style->borderTopWidth() > 6); - if (applyRightInnerClip) { - graphicsContext->save(); - graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2), - style->borderTopWidth()); - } - - if (upperRightBorderStylesMatch) { - secondAngleStart = 0; - secondAngleSpan = 90; - } else { - secondAngleStart = 45; - secondAngleSpan = 45; - } - - // Draw upper right arc - drawBorderArc(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan, - BSTop, tc, style->color(), ts, false); - if (applyRightInnerClip) - graphicsContext->restore(); - } - } - } - - if (renderBottom) { - bool ignore_left = (renderRadii && bottomLeft.width() > 0) || - (bc == lc && bt == lt && bs >= OUTSET && - (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); - - bool ignore_right = (renderRadii && bottomRight.width() > 0) || - (bc == rc && bt == rt && bs >= OUTSET && - (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); - - int x = tx; - int x2 = tx + w; - if (renderRadii) { - x += bottomLeft.width(); - x2 -= bottomRight.width(); - } - - drawBorder(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bc, style->color(), bs, - ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); - - if (renderRadii) { - thickness = style->borderBottomWidth() * 2; - - if (bottomLeft.width()) { - int leftX = tx; - int leftY = ty + h - bottomLeft.height() * 2; - bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width()) - && (style->borderBottomWidth() < bottomLeft.height()) - && (bs != DOUBLE || style->borderBottomWidth() > 6); - if (applyLeftInnerClip) { - graphicsContext->save(); - graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2), - style->borderBottomWidth()); - } - - if (lowerLeftBorderStylesMatch) { - firstAngleStart = 180; - firstAngleSpan = 90; - } else { - firstAngleStart = 225; - firstAngleSpan = 45; - } - - // Draw lower left arc - drawBorderArc(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan, - BSBottom, bc, style->color(), bs, true); - if (applyLeftInnerClip) - graphicsContext->restore(); - } - - if (bottomRight.width()) { - int rightY = ty + h - bottomRight.height() * 2; - int rightX = tx + w - bottomRight.width() * 2; - bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width()) - && (style->borderBottomWidth() < bottomRight.height()) - && (bs != DOUBLE || style->borderBottomWidth() > 6); - if (applyRightInnerClip) { - graphicsContext->save(); - graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2), - style->borderBottomWidth()); - } - - secondAngleStart = 270; - secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45; + if ((style == DOUBLE && thickness / 2 < 3) || ((style == RIDGE || style == GROOVE) && thickness / 2 < 2)) + style = SOLID; - // Draw lower right arc - drawBorderArc(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan, - BSBottom, bc, style->color(), bs, false); - if (applyRightInnerClip) - graphicsContext->restore(); - } - } + if (!c.isValid()) { + if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE) + c.setRGB(238, 238, 238); + else + c = textColor; } - if (renderLeft) { - bool ignore_top = (renderRadii && topLeft.height() > 0) || - (tc == lc && tt == lt && ls >= OUTSET && - (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); - - bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) || - (bc == lc && bt == lt && ls >= OUTSET && - (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); - - int y = ty; - int y2 = ty + h; - if (renderRadii) { - y += topLeft.height(); - y2 -= bottomLeft.height(); - } - - drawBorder(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, lc, style->color(), ls, - ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); - - if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) { - int topX = tx; - thickness = style->borderLeftWidth() * 2; - - if (!upperLeftBorderStylesMatch && topLeft.width()) { - int topY = ty; - bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width()) - && (style->borderTopWidth() < topLeft.height()) - && (ls != DOUBLE || style->borderLeftWidth() > 6); - if (applyTopInnerClip) { - graphicsContext->save(); - graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2), - style->borderLeftWidth()); - } - - firstAngleStart = 135; - firstAngleSpan = 45; - - // Draw top left arc - drawBorderArc(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan, - BSLeft, lc, style->color(), ls, true); - if (applyTopInnerClip) - graphicsContext->restore(); - } - - if (!lowerLeftBorderStylesMatch && bottomLeft.width()) { - int bottomY = ty + h - bottomLeft.height() * 2; - bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width()) - && (style->borderBottomWidth() < bottomLeft.height()) - && (ls != DOUBLE || style->borderLeftWidth() > 6); - if (applyBottomInnerClip) { - graphicsContext->save(); - graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2), - style->borderLeftWidth()); - } - - secondAngleStart = 180; - secondAngleSpan = 45; + switch (style) { + case BNONE: + case BHIDDEN: + return; + case DOTTED: + case DASHED: + graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeStyle(style == DOTTED ? DottedStroke : DashedStroke); + graphicsContext->setStrokeThickness(thickness); + graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); + break; + case DOUBLE: { + float third = thickness / 3.0f; + float innerThird = (thickness + 1.0f) / 6.0f; + int shiftForInner = static_cast<int>(innerThird * 2.5f); - // Draw bottom left arc - drawBorderArc(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan, - BSLeft, lc, style->color(), ls, false); - if (applyBottomInnerClip) - graphicsContext->restore(); + int outerY = y; + int outerHeight = radius.height() * 2; + int innerX = x + shiftForInner; + int innerY = y + shiftForInner; + int innerWidth = (radius.width() - shiftForInner) * 2; + int innerHeight = (radius.height() - shiftForInner) * 2; + if (innerThird > 1 && (s == BSTop || (firstCorner && (s == BSLeft || s == BSRight)))) { + outerHeight += 2; + innerHeight += 2; } - } - } - if (renderRight) { - bool ignore_top = (renderRadii && topRight.height() > 0) || - ((tc == rc) && (tt == rt) && - (rs >= DOTTED || rs == INSET) && - (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); - - bool ignore_bottom = (renderRadii && bottomRight.height() > 0) || - ((bc == rc) && (bt == rt) && - (rs >= DOTTED || rs == INSET) && - (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); - - int y = ty; - int y2 = ty + h; - if (renderRadii) { - y += topRight.height(); - y2 -= bottomRight.height(); + graphicsContext->setStrokeStyle(SolidStroke); + graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeThickness(third); + graphicsContext->strokeArc(IntRect(x, outerY, radius.width() * 2, outerHeight), angleStart, angleSpan); + graphicsContext->setStrokeThickness(innerThird > 2 ? innerThird - 1 : innerThird); + graphicsContext->strokeArc(IntRect(innerX, innerY, innerWidth, innerHeight), angleStart, angleSpan); + break; } - - drawBorder(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rc, style->color(), rs, - ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); - - if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) { - thickness = style->borderRightWidth() * 2; - - if (!upperRightBorderStylesMatch && topRight.width()) { - int topX = tx + w - topRight.width() * 2; - int topY = ty; - bool applyTopInnerClip = (style->borderRightWidth() < topRight.width()) - && (style->borderTopWidth() < topRight.height()) - && (rs != DOUBLE || style->borderRightWidth() > 6); - if (applyTopInnerClip) { - graphicsContext->save(); - graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2), - style->borderRightWidth()); - } - - firstAngleStart = 0; - firstAngleSpan = 45; - - // Draw top right arc - drawBorderArc(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan, - BSRight, rc, style->color(), rs, true); - if (applyTopInnerClip) - graphicsContext->restore(); - } - - if (!lowerRightBorderStylesMatch && bottomRight.width()) { - int bottomX = tx + w - bottomRight.width() * 2; - int bottomY = ty + h - bottomRight.height() * 2; - bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width()) - && (style->borderBottomWidth() < bottomRight.height()) - && (rs != DOUBLE || style->borderRightWidth() > 6); - if (applyBottomInnerClip) { - graphicsContext->save(); - graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2), - style->borderRightWidth()); - } - - secondAngleStart = 315; - secondAngleSpan = 45; - - // Draw bottom right arc - drawBorderArc(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan, - BSRight, rc, style->color(), rs, false); - if (applyBottomInnerClip) - graphicsContext->restore(); + case GROOVE: + case RIDGE: { + Color c2; + if ((style == RIDGE && (s == BSTop || s == BSLeft)) || + (style == GROOVE && (s == BSBottom || s == BSRight))) + c2 = c.dark(); + else { + c2 = c; + c = c.dark(); } - } - } - - if (renderRadii) - graphicsContext->restore(); -} -void RenderObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, bool begin, bool end) -{ - // FIXME: Deal with border-image. Would be great to use border-image as a mask. - - IntRect rect(tx, ty, w, h); - bool hasBorderRadius = s->hasBorderRadius(); - bool hasOpaqueBackground = s->backgroundColor().isValid() && s->backgroundColor().alpha() == 255; - for (ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next) { - context->save(); - - IntSize shadowOffset(shadow->x, shadow->y); - int shadowBlur = shadow->blur; - IntRect fillRect(rect); - - if (hasBorderRadius) { - IntRect shadowRect(rect); - shadowRect.inflate(shadowBlur); - shadowRect.move(shadowOffset); - context->clip(shadowRect); - - // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not - // bleed in (due to antialiasing) if the context is transformed. - IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 1, 0); - shadowOffset -= extraOffset; - fillRect.move(extraOffset); - } + graphicsContext->setStrokeStyle(SolidStroke); + graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeThickness(thickness); + graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); - context->setShadow(shadowOffset, shadowBlur, shadow->color); - if (hasBorderRadius) { - IntSize topLeft = begin ? s->borderTopLeftRadius() : IntSize(); - IntSize topRight = end ? s->borderTopRightRadius() : IntSize(); - IntSize bottomLeft = begin ? s->borderBottomLeftRadius() : IntSize(); - IntSize bottomRight = end ? s->borderBottomRightRadius() : IntSize(); - if (!hasOpaqueBackground) - context->clipOutRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); - context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black); - } else { - if (!hasOpaqueBackground) - context->clipOut(rect); - context->fillRect(fillRect, Color::black); + float halfThickness = (thickness + 1.0f) / 4.0f; + int shiftForInner = static_cast<int>(halfThickness * 1.5f); + graphicsContext->setStrokeColor(c2); + graphicsContext->setStrokeThickness(halfThickness > 2 ? halfThickness - 1 : halfThickness); + graphicsContext->strokeArc(IntRect(x + shiftForInner, y + shiftForInner, (radius.width() - shiftForInner) * 2, + (radius.height() - shiftForInner) * 2), angleStart, angleSpan); + break; } - context->restore(); + case INSET: + if (s == BSTop || s == BSLeft) + c = c.dark(); + case OUTSET: + if (style == OUTSET && (s == BSBottom || s == BSRight)) + c = c.dark(); + case SOLID: + graphicsContext->setStrokeStyle(SolidStroke); + graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeThickness(thickness); + graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); + break; } } @@ -1537,13 +964,13 @@ void RenderObject::addPDFURLRect(GraphicsContext* context, const IntRect& rect) { if (rect.isEmpty()) return; - Node* node = element(); - if (!node || !node->isLink() || !node->isElementNode()) + Node* n = node(); + if (!n || !n->isLink() || !n->isElementNode()) return; - const AtomicString& href = static_cast<Element*>(node)->getAttribute(hrefAttr); + const AtomicString& href = static_cast<Element*>(n)->getAttribute(hrefAttr); if (href.isNull()) return; - context->setURLForRect(node->document()->completeURL(href), rect); + context->setURLForRect(n->document()->completeURL(href), rect); } void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style) @@ -1564,11 +991,11 @@ void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty 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); if (style->outlineStyleIsAuto()) - addFocusRingRects(graphicsContext, tx, ty); + graphicsContext->drawFocusRing(oc); else addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); - graphicsContext->drawFocusRing(oc); graphicsContext->clearFocusRing(); } } @@ -1584,21 +1011,52 @@ void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty if (h < 0 || w < 0) return; - drawBorder(graphicsContext, tx - ow, ty - ow, tx, ty + h + ow, + drawLineForBoxSide(graphicsContext, tx - ow, ty - ow, tx, ty + h + ow, BSLeft, Color(oc), style->color(), os, ow, ow); - drawBorder(graphicsContext, tx - ow, ty - ow, tx + w + ow, ty, + drawLineForBoxSide(graphicsContext, tx - ow, ty - ow, tx + w + ow, ty, BSTop, Color(oc), style->color(), os, ow, ow); - drawBorder(graphicsContext, tx + w, ty - ow, tx + w + ow, ty + h + ow, + drawLineForBoxSide(graphicsContext, tx + w, ty - ow, tx + w + ow, ty + h + ow, BSRight, Color(oc), style->color(), os, ow, ow); - drawBorder(graphicsContext, tx - ow, ty + h, tx + w + ow, ty + h + ow, + drawLineForBoxSide(graphicsContext, tx - ow, ty + h, tx + w + ow, ty + h + ow, BSBottom, Color(oc), style->color(), os, ow, ow); } -void RenderObject::addLineBoxRects(Vector<IntRect>&, unsigned, unsigned, bool) + +void RenderObject::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool) { + if (!firstChild()) { + if ((isInline() || isAnonymousBlock())) { + FloatPoint absPos = localToAbsolute(FloatPoint()); + absoluteRects(rects, absPos.x(), absPos.y()); + } + return; + } + + unsigned offset = start; + for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { + if (child->isText() || child->isInline() || child->isAnonymousBlock()) { + FloatPoint absPos = child->localToAbsolute(FloatPoint()); + child->absoluteRects(rects, absPos.x(), absPos.y()); + } + } +} + +void RenderObject::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool) +{ + if (!firstChild()) { + if (isInline() || isAnonymousBlock()) + absoluteQuads(quads); + return; + } + + unsigned offset = start; + for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { + if (child->isText() || child->isInline() || child->isAnonymousBlock()) + child->absoluteQuads(quads); + } } IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) @@ -1652,58 +1110,80 @@ void RenderObject::paint(PaintInfo& /*paintInfo*/, int /*tx*/, int /*ty*/) { } -RenderBox* RenderObject::containerForRepaint() const +RenderBoxModelObject* RenderObject::containerForRepaint() const { - // For now, all repaints are root-relative. +#if USE(ACCELERATED_COMPOSITING) + if (RenderView* v = view()) { + if (v->usesCompositing()) { + RenderLayer* compLayer = enclosingLayer()->enclosingCompositingLayer(); + return compLayer ? compLayer->renderer() : 0; + } + } +#endif + // Do root-relative repaint. return 0; } +void RenderObject::repaintUsingContainer(RenderBoxModelObject* repaintContainer, const IntRect& r, bool immediate) +{ + if (!repaintContainer || repaintContainer->isRenderView()) { + RenderView* v = repaintContainer ? toRenderView(repaintContainer) : view(); + v->repaintViewRectangle(r, immediate); + } else { +#if USE(ACCELERATED_COMPOSITING) + RenderView* v = view(); + if (v->usesCompositing()) { + ASSERT(repaintContainer->hasLayer() && repaintContainer->layer()->isComposited()); + repaintContainer->layer()->setBackingNeedsRepaintInRect(r); + } +#else + ASSERT_NOT_REACHED(); +#endif + } +} + void RenderObject::repaint(bool immediate) { - // Can't use view(), since we might be unrooted. - RenderObject* o = this; - while (o->parent()) - o = o->parent(); - if (!o->isRenderView()) + // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) + RenderView* view; + if (!isRooted(&view)) return; - RenderView* view = static_cast<RenderView*>(o); if (view->printing()) return; // Don't repaint if we're printing. - view->repaintViewRectangle(absoluteClippedOverflowRect(), immediate); + RenderBoxModelObject* repaintContainer = containerForRepaint(); + repaintUsingContainer(repaintContainer ? repaintContainer : view, clippedOverflowRectForRepaint(repaintContainer), immediate); } void RenderObject::repaintRectangle(const IntRect& r, bool immediate) { - // Can't use view(), since we might be unrooted. - RenderObject* o = this; - while (o->parent()) - o = o->parent(); - if (!o->isRenderView()) + // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) + RenderView* view; + if (!isRooted(&view)) return; - RenderView* view = static_cast<RenderView*>(o); if (view->printing()) return; // Don't repaint if we're printing. - IntRect absRect(r); + IntRect dirtyRect(r); // FIXME: layoutDelta needs to be applied in parts before/after transforms and // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 - absRect.move(view->layoutDelta()); + dirtyRect.move(view->layoutDelta()); - computeAbsoluteRepaintRect(absRect); - view->repaintViewRectangle(absRect, immediate); + RenderBoxModelObject* repaintContainer = containerForRepaint(); + computeRectForRepaint(repaintContainer, dirtyRect); + repaintUsingContainer(repaintContainer ? repaintContainer : view, dirtyRect, immediate); } -bool RenderObject::repaintAfterLayoutIfNeeded(const IntRect& oldBounds, const IntRect& oldOutlineBox) +bool RenderObject::repaintAfterLayoutIfNeeded(RenderBoxModelObject* repaintContainer, const IntRect& oldBounds, const IntRect& oldOutlineBox) { RenderView* v = view(); if (v->printing()) return false; // Don't repaint if we're printing. - IntRect newBounds = absoluteClippedOverflowRect(); + IntRect newBounds = clippedOverflowRectForRepaint(repaintContainer); IntRect newOutlineBox; bool fullRepaint = selfNeedsLayout(); @@ -1711,14 +1191,18 @@ bool RenderObject::repaintAfterLayoutIfNeeded(const IntRect& oldBounds, const In if (!fullRepaint && style()->borderFit() == BorderFitLines) fullRepaint = true; if (!fullRepaint) { - newOutlineBox = absoluteOutlineBounds(); + newOutlineBox = outlineBoundsForRepaint(repaintContainer); if (newOutlineBox.location() != oldOutlineBox.location() || (mustRepaintBackgroundOrBorder() && (newBounds != oldBounds || newOutlineBox != oldOutlineBox))) fullRepaint = true; } + + if (!repaintContainer) + repaintContainer = v; + if (fullRepaint) { - v->repaintViewRectangle(oldBounds); + repaintUsingContainer(repaintContainer, oldBounds); if (newBounds != oldBounds) - v->repaintViewRectangle(newBounds); + repaintUsingContainer(repaintContainer, newBounds); return true; } @@ -1727,35 +1211,34 @@ bool RenderObject::repaintAfterLayoutIfNeeded(const IntRect& oldBounds, const In int deltaLeft = newBounds.x() - oldBounds.x(); if (deltaLeft > 0) - v->repaintViewRectangle(IntRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height())); + repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height())); else if (deltaLeft < 0) - v->repaintViewRectangle(IntRect(newBounds.x(), newBounds.y(), -deltaLeft, newBounds.height())); + repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), newBounds.y(), -deltaLeft, newBounds.height())); int deltaRight = newBounds.right() - oldBounds.right(); if (deltaRight > 0) - v->repaintViewRectangle(IntRect(oldBounds.right(), newBounds.y(), deltaRight, newBounds.height())); + repaintUsingContainer(repaintContainer, IntRect(oldBounds.right(), newBounds.y(), deltaRight, newBounds.height())); else if (deltaRight < 0) - v->repaintViewRectangle(IntRect(newBounds.right(), oldBounds.y(), -deltaRight, oldBounds.height())); + repaintUsingContainer(repaintContainer, IntRect(newBounds.right(), oldBounds.y(), -deltaRight, oldBounds.height())); int deltaTop = newBounds.y() - oldBounds.y(); if (deltaTop > 0) - v->repaintViewRectangle(IntRect(oldBounds.x(), oldBounds.y(), oldBounds.width(), deltaTop)); + repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), oldBounds.y(), oldBounds.width(), deltaTop)); else if (deltaTop < 0) - v->repaintViewRectangle(IntRect(newBounds.x(), newBounds.y(), newBounds.width(), -deltaTop)); + repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), newBounds.y(), newBounds.width(), -deltaTop)); int deltaBottom = newBounds.bottom() - oldBounds.bottom(); if (deltaBottom > 0) - v->repaintViewRectangle(IntRect(newBounds.x(), oldBounds.bottom(), newBounds.width(), deltaBottom)); + repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), oldBounds.bottom(), newBounds.width(), deltaBottom)); else if (deltaBottom < 0) - v->repaintViewRectangle(IntRect(oldBounds.x(), newBounds.bottom(), oldBounds.width(), -deltaBottom)); + repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), newBounds.bottom(), oldBounds.width(), -deltaBottom)); if (newOutlineBox == oldOutlineBox) return false; // We didn't move, but we did change size. Invalidate the delta, which will consist of possibly // two rectangles (but typically only one). - RenderFlow* continuation = virtualContinuation(); - RenderStyle* outlineStyle = !isInline() && continuation ? continuation->style() : style(); + RenderStyle* outlineStyle = outlineStyleForRepaint(); int ow = outlineStyle->outlineSize(); ShadowData* boxShadow = style()->boxShadow(); int width = abs(newOutlineBox.width() - oldOutlineBox.width()); @@ -1773,7 +1256,7 @@ bool RenderObject::repaintAfterLayoutIfNeeded(const IntRect& oldBounds, const In int right = min(newBounds.right(), oldBounds.right()); if (rightRect.x() < right) { rightRect.setWidth(min(rightRect.width(), right - rightRect.x())); - v->repaintViewRectangle(rightRect); + repaintUsingContainer(repaintContainer, rightRect); } } int height = abs(newOutlineBox.height() - oldOutlineBox.height()); @@ -1791,7 +1274,7 @@ bool RenderObject::repaintAfterLayoutIfNeeded(const IntRect& oldBounds, const In int bottom = min(newBounds.bottom(), oldBounds.bottom()); if (bottomRect.y() < bottom) { bottomRect.setHeight(min(bottomRect.height(), bottom - bottomRect.y())); - v->repaintViewRectangle(bottomRect); + repaintUsingContainer(repaintContainer, bottomRect); } } return false; @@ -1813,39 +1296,27 @@ bool RenderObject::checkForRepaintDuringLayout() const return !document()->view()->needsFullRepaint() && !hasLayer(); } -IntRect RenderObject::rectWithOutlineForRepaint(RenderBox* repaintContainer, int outlineWidth) +IntRect RenderObject::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth) { IntRect r(clippedOverflowRectForRepaint(repaintContainer)); r.inflate(outlineWidth); - - if (virtualContinuation() && !isInline()) - r.inflateY(toRenderBox(this)->collapsedMarginTop()); - - if (isRenderInline()) { - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { - if (!curr->isText()) - r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth)); - } - } - return r; } -IntRect RenderObject::clippedOverflowRectForRepaint(RenderBox* repaintContainer) +IntRect RenderObject::clippedOverflowRectForRepaint(RenderBoxModelObject*) { - if (parent()) - return parent()->clippedOverflowRectForRepaint(repaintContainer); + ASSERT_NOT_REACHED(); return IntRect(); } -void RenderObject::computeRectForRepaint(IntRect& rect, RenderBox* repaintContainer, bool fixed) +void RenderObject::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) { if (repaintContainer == this) return; if (RenderObject* o = parent()) { if (o->isBlockFlow()) { - RenderBlock* cb = static_cast<RenderBlock*>(o); + RenderBlock* cb = toRenderBlock(o); if (cb->hasColumns()) cb->adjustRectForColumns(rect); } @@ -1866,7 +1337,7 @@ void RenderObject::computeRectForRepaint(IntRect& rect, RenderBox* repaintContai return; } - o->computeRectForRepaint(rect, repaintContainer, fixed); + o->computeRectForRepaint(repaintContainer, rect, fixed); } } @@ -1878,8 +1349,8 @@ void RenderObject::dirtyLinesFromChangedChild(RenderObject*) void RenderObject::showTreeForThis() const { - if (element()) - element()->showTreeForThis(); + if (node()) + node()->showTreeForThis(); } #endif // NDEBUG @@ -1888,7 +1359,7 @@ Color RenderObject::selectionBackgroundColor() const { Color color; if (style()->userSelect() != SELECT_NONE) { - RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::SELECTION); + RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(SELECTION); if (pseudoStyle && pseudoStyle->backgroundColor().isValid()) color = pseudoStyle->backgroundColor().blendWithWhite(); else @@ -1906,7 +1377,7 @@ Color RenderObject::selectionForegroundColor() const if (style()->userSelect() == SELECT_NONE) return color; - if (RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::SELECTION)) { + if (RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(SELECTION)) { color = pseudoStyle->textFillColor(); if (!color.isValid()) color = pseudoStyle->color(); @@ -1924,7 +1395,7 @@ Node* RenderObject::draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& d return 0; for (const RenderObject* curr = this; curr; curr = curr->parent()) { - Node* elt = curr->element(); + Node* elt = curr->node(); if (elt && elt->nodeType() == Node::TEXT_NODE) { // Since there's no way for the author to address the -webkit-user-drag style for a text node, // we use our own judgement. @@ -1958,17 +1429,6 @@ void RenderObject::selectionStartEnd(int& spos, int& epos) const view()->selectionStartEnd(spos, epos); } -RenderBlock* RenderObject::createAnonymousBlock() -{ - RefPtr<RenderStyle> newStyle = RenderStyle::create(); - newStyle->inheritFrom(m_style.get()); - newStyle->setDisplay(BLOCK); - - RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); - newBox->setStyle(newStyle.release()); - return newBox; -} - void RenderObject::handleDynamicFloatPositionChange() { // We have gone from not affecting the inline status of the parent flow to suddenly @@ -1976,30 +1436,14 @@ void RenderObject::handleDynamicFloatPositionChange() // childrenInline() state and our state. setInline(style()->isDisplayInlineType()); if (isInline() != parent()->childrenInline()) { - if (!isInline()) { - if (parent()->isRenderInline()) { - // We have to split the parent flow. - RenderInline* parentInline = static_cast<RenderInline*>(parent()); - RenderBlock* newBox = parentInline->createAnonymousBlock(); - - RenderFlow* oldContinuation = parentInline->continuation(); - parentInline->setContinuation(newBox); - - RenderObject* beforeChild = nextSibling(); - parent()->removeChildNode(this); - parentInline->splitFlow(beforeChild, newBox, this, oldContinuation); - } else if (parent()->isRenderBlock()) { - RenderBlock* o = static_cast<RenderBlock*>(parent()); - o->makeChildrenNonInline(); - if (o->isAnonymousBlock() && o->parent()) - o->parent()->removeLeftoverAnonymousBlock(o); - // o may be dead here - } - } else { + if (!isInline()) + toRenderBoxModelObject(parent())->childBecameNonInline(this); + else { // An anonymous block must be made to wrap this inline. - RenderBlock* box = createAnonymousBlock(); - parent()->insertChildNode(box, this); - box->appendChildNode(parent()->removeChildNode(this)); + RenderBlock* block = toRenderBlock(parent())->createAnonymousBlock(); + RenderObjectChildList* childlist = parent()->virtualChildren(); + childlist->insertChildNode(parent(), block, this); + block->children()->appendChildNode(block, childlist->removeChildNode(parent(), this)); } } } @@ -2012,18 +1456,48 @@ void RenderObject::setAnimatableStyle(PassRefPtr<RenderStyle> style) setStyle(style); } +StyleDifference RenderObject::adjustStyleDifference(StyleDifference diff, unsigned contextSensitiveProperties) const +{ +#if USE(ACCELERATED_COMPOSITING) + // If transform changed, and we are not composited, need to do a layout. + if (contextSensitiveProperties & ContextSensitivePropertyTransform) + // Text nodes share style with their parents but transforms don't apply to them, + // hence the !isText() check. + // FIXME: when transforms are taken into account for overflow, we will need to do a layout. + if (!isText() && (!hasLayer() || !toRenderBoxModelObject(this)->layer()->isComposited())) + diff = StyleDifferenceLayout; + else if (diff < StyleDifferenceRecompositeLayer) + diff = StyleDifferenceRecompositeLayer; + + // If opacity changed, and we are not composited, need to repaint (also + // ignoring text nodes) + if (contextSensitiveProperties & ContextSensitivePropertyOpacity) + if (!isText() && (!hasLayer() || !toRenderBoxModelObject(this)->layer()->isComposited())) + diff = StyleDifferenceRepaintLayer; + else if (diff < StyleDifferenceRecompositeLayer) + diff = StyleDifferenceRecompositeLayer; +#else + UNUSED_PARAM(contextSensitiveProperties); +#endif + + // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint. + if (diff == StyleDifferenceRepaintLayer && !hasLayer()) + diff = StyleDifferenceRepaint; + + return diff; +} + void RenderObject::setStyle(PassRefPtr<RenderStyle> style) { if (m_style == style) return; - RenderStyle::Diff diff = RenderStyle::Equal; + StyleDifference diff = StyleDifferenceEqual; + unsigned contextSensitiveProperties = ContextSensitivePropertyNone; if (m_style) - diff = m_style->diff(style.get()); + diff = m_style->diff(style.get(), contextSensitiveProperties); - // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint. - if (diff == RenderStyle::RepaintLayer && !hasLayer()) - diff = RenderStyle::Repaint; + diff = adjustStyleDifference(diff, contextSensitiveProperties); styleWillChange(diff, style.get()); @@ -2036,7 +1510,32 @@ void RenderObject::setStyle(PassRefPtr<RenderStyle> style) updateImage(oldStyle ? oldStyle->borderImage().image() : 0, m_style ? m_style->borderImage().image() : 0); updateImage(oldStyle ? oldStyle->maskBoxImage().image() : 0, m_style ? m_style->maskBoxImage().image() : 0); + // We need to ensure that view->maximalOutlineSize() is valid for any repaints that happen + // during styleDidChange (it's used by clippedOverflowRectForRepaint()). + if (m_style->outlineWidth() > 0 && m_style->outlineSize() > maximalOutlineSize(PaintPhaseOutline)) + toRenderView(document()->renderer())->setMaximalOutlineSize(m_style->outlineSize()); + styleDidChange(diff, oldStyle.get()); + + if (!m_parent || isText()) + return; + + // Now that the layer (if any) has been updated, we need to adjust the diff again, + // check whether we should layout now, and decide if we need to repaint. + StyleDifference updatedDiff = adjustStyleDifference(diff, contextSensitiveProperties); + + if (diff <= StyleDifferenceLayoutPositionedMovementOnly) { + if (updatedDiff == StyleDifferenceLayout) + setNeedsLayoutAndPrefWidthsRecalc(); + else if (updatedDiff == StyleDifferenceLayoutPositionedMovementOnly) + setNeedsPositionedMovementLayout(); + } + + if (updatedDiff == StyleDifferenceRepaintLayer || updatedDiff == StyleDifferenceRepaint) { + // Do a repaint with the new style now, e.g., for example if we go from + // not having an outline to having an outline. + repaint(); + } } void RenderObject::setStyleInternal(PassRefPtr<RenderStyle> style) @@ -2044,7 +1543,7 @@ void RenderObject::setStyleInternal(PassRefPtr<RenderStyle> style) m_style = style; } -void RenderObject::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { if (m_style) { // If our z-index changes value or our visibility changes, @@ -2064,30 +1563,30 @@ void RenderObject::styleWillChange(RenderStyle::Diff diff, const RenderStyle* ne l->setHasVisibleContent(true); else if (l->hasVisibleContent() && (this == l->renderer() || l->renderer()->style()->visibility() != VISIBLE)) { l->dirtyVisibleContentStatus(); - if (diff > RenderStyle::RepaintLayer) + if (diff > StyleDifferenceRepaintLayer) repaint(); } } } } - if (m_parent && (diff == RenderStyle::Repaint || newStyle->outlineSize() < m_style->outlineSize())) + if (m_parent && (diff == StyleDifferenceRepaint || newStyle->outlineSize() < m_style->outlineSize())) repaint(); if (isFloating() && (m_style->floating() != newStyle->floating())) // For changes in float styles, we need to conceivably remove ourselves // from the floating objects list. - removeFromObjectLists(); + toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); else if (isPositioned() && (newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition)) // For changes in positioning styles, we need to conceivably remove ourselves // from the positioned objects list. - removeFromObjectLists(); + toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); s_affectsParentBlock = isFloatingOrPositioned() && (!newStyle->isFloating() && newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition) && parent() && (parent()->isBlockFlow() || parent()->isRenderInline()); // reset style flags - if (diff == RenderStyle::Layout || diff == RenderStyle::LayoutPositionedMovementOnly) { + if (diff == StyleDifferenceLayout || diff == StyleDifferenceLayoutPositionedMovementOnly) { m_floating = false; m_positioned = false; m_relPositioned = false; @@ -2113,24 +1612,21 @@ void RenderObject::styleWillChange(RenderStyle::Diff diff, const RenderStyle* ne } } -void RenderObject::styleDidChange(RenderStyle::Diff diff, const RenderStyle*) +void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle*) { - setHasBoxDecorations(m_style->hasBorder() || m_style->hasBackground() || m_style->hasAppearance() || m_style->boxShadow()); - if (s_affectsParentBlock) handleDynamicFloatPositionChange(); if (!m_parent) return; - if (diff == RenderStyle::Layout) + if (diff == StyleDifferenceLayout) setNeedsLayoutAndPrefWidthsRecalc(); - else if (diff == RenderStyle::LayoutPositionedMovementOnly) + else if (diff == StyleDifferenceLayoutPositionedMovementOnly) setNeedsPositionedMovementLayout(); - else if (diff == RenderStyle::RepaintLayer || diff == RenderStyle::Repaint) - // Do a repaint with the new style now, e.g., for example if we go from - // not having an outline to having an outline. - repaint(); + + // Don't check for repaint here; we need to wait until the layer has been + // updated by subclasses before we know if we have to repaint (in setStyle()). } void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers) @@ -2163,42 +1659,82 @@ IntRect RenderObject::viewRect() const FloatPoint RenderObject::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const { - RenderObject* o = parent(); - if (o) { - if (o->hasOverflowClip()) - localPoint -= toRenderBox(o)->layer()->scrolledContentOffset(); - return o->localToAbsolute(localPoint, fixed, useTransforms); - } - - return FloatPoint(); + TransformState transformState(TransformState::ApplyTransformDirection, localPoint); + mapLocalToContainer(0, fixed, useTransforms, transformState); + transformState.flatten(); + + return transformState.lastPlanarPoint(); } FloatPoint RenderObject::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const { - RenderObject* o = parent(); - if (o) { - FloatPoint localPoint = o->absoluteToLocal(containerPoint, fixed, useTransforms); - if (o->hasOverflowClip()) - localPoint += toRenderBox(o)->layer()->scrolledContentOffset(); - return localPoint; - } - return FloatPoint(); + TransformState transformState(TransformState::UnapplyInverseTransformDirection, containerPoint); + mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); + transformState.flatten(); + + return transformState.lastPlanarPoint(); } -FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, RenderBox* repaintContainer, bool fixed) const +void RenderObject::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const { if (repaintContainer == this) - return localQuad; + return; RenderObject* o = parent(); + if (!o) + return; + + if (o->hasOverflowClip()) + transformState.move(-toRenderBox(o)->layer()->scrolledContentOffset()); + + o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); +} + +void RenderObject::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const +{ + RenderObject* o = parent(); if (o) { - FloatQuad quad = localQuad; + o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); if (o->hasOverflowClip()) - quad -= toRenderBox(o)->layer()->scrolledContentOffset(); - return o->localToContainerQuad(quad, repaintContainer, fixed); + transformState.move(toRenderBox(o)->layer()->scrolledContentOffset()); } +} - return FloatQuad(); +TransformationMatrix RenderObject::transformFromContainer(const RenderObject* containerObject, const IntSize& offsetInContainer) const +{ + TransformationMatrix containerTransform; + containerTransform.translate(offsetInContainer.width(), offsetInContainer.height()); + RenderLayer* layer; + if (hasLayer() && (layer = toRenderBox(this)->layer()) && layer->transform()) + containerTransform.multLeft(layer->currentTransform()); + +#if ENABLE(3D_RENDERING) + if (containerObject && containerObject->style()->hasPerspective()) { + // Perpsective on the container affects us, so we have to factor it in here. + ASSERT(containerObject->hasLayer()); + FloatPoint perspectiveOrigin = toRenderBox(containerObject)->layer()->perspectiveOrigin(); + + TransformationMatrix perspectiveMatrix; + perspectiveMatrix.applyPerspective(containerObject->style()->perspective()); + + containerTransform.translateRight3d(-perspectiveOrigin.x(), -perspectiveOrigin.y(), 0); + containerTransform.multiply(perspectiveMatrix); + containerTransform.translateRight3d(perspectiveOrigin.x(), perspectiveOrigin.y(), 0); + } +#else + UNUSED_PARAM(containerObject); +#endif + + return containerTransform; +} + +FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, RenderBoxModelObject* repaintContainer, bool fixed) const +{ + TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint(), &localQuad); + mapLocalToContainer(repaintContainer, fixed, true, transformState); + transformState.flatten(); + + return transformState.lastPlanarQuad(); } IntSize RenderObject::offsetFromContainer(RenderObject* o) const @@ -2222,12 +1758,27 @@ IntRect RenderObject::localCaretRect(InlineBox*, int, int* extraWidthToEndOfLine RenderView* RenderObject::view() const { - return static_cast<RenderView*>(document()->renderer()); + return toRenderView(document()->renderer()); +} + +bool RenderObject::isRooted(RenderView** view) +{ + RenderObject* o = this; + while (o->parent()) + o = o->parent(); + + if (!o->isRenderView()) + return false; + + if (view) + *view = toRenderView(o); + + return true; } bool RenderObject::hasOutlineAnnotation() const { - return element() && element()->isLink() && document()->printing(); + return node() && node()->isLink() && document()->printing(); } RenderObject* RenderObject::container() const @@ -2269,51 +1820,19 @@ RenderObject* RenderObject::container() const return o; } -// This code has been written to anticipate the addition of CSS3-::outside and ::inside generated -// content (and perhaps XBL). That's why it uses the render tree and not the DOM tree. -RenderObject* RenderObject::hoverAncestor() const -{ - return (!isInline() && virtualContinuation()) ? virtualContinuation() : parent(); -} - bool RenderObject::isSelectionBorder() const { SelectionState st = selectionState(); return st == SelectionStart || st == SelectionEnd || st == SelectionBoth; } -void RenderObject::removeFromObjectLists() -{ - if (documentBeingDestroyed()) - return; - - if (isFloating()) { - RenderBlock* outermostBlock = containingBlock(); - for (RenderBlock* p = outermostBlock; p && !p->isRenderView(); p = p->containingBlock()) { - if (p->containsFloat(this)) - outermostBlock = p; - } - - if (outermostBlock) - outermostBlock->markAllDescendantsWithFloatsForLayout(toRenderBox(this), false); - } - - if (isPositioned()) { - RenderObject* p; - for (p = parent(); p; p = p->parent()) { - if (p->isRenderBlock()) - static_cast<RenderBlock*>(p)->removePositionedObject(toRenderBox(this)); - } - } -} - -bool RenderObject::documentBeingDestroyed() const -{ - return !document()->renderer(); -} - void RenderObject::destroy() { + // Destroy any leftover anonymous children. + RenderObjectChildList* children = virtualChildren(); + if (children) + children->destroyLeftoverChildren(); + // If this renderer is being autoscrolled, stop the autoscroll timer if (document()->frame() && document()->frame()->eventHandler()->autoscrollRenderer() == this) document()->frame()->eventHandler()->stopAutoscrollTimer(true); @@ -2333,12 +1852,11 @@ void RenderObject::destroy() remove(); - // FIXME: Would like to do this in RenderBox, but the timing is so complicated that this can't easily - // be moved into RenderBox::destroy. - RenderArena* arena = renderArena(); + // FIXME: Would like to do this in RenderBoxModelObject, but the timing is so complicated that this can't easily + // be moved into RenderBoxModelObject::destroy. if (hasLayer()) - toRenderBox(this)->layer()->destroy(arena); - arenaDelete(arena, this); + toRenderBoxModelObject(this)->destroyLayer(); + arenaDelete(renderArena(), this); } void RenderObject::arenaDelete(RenderArena* arena, void* base) @@ -2374,14 +1892,14 @@ void RenderObject::arenaDelete(RenderArena* arena, void* base) arena->free(*(size_t*)base, base); } -VisiblePosition RenderObject::positionForCoordinates(int, int) +VisiblePosition RenderObject::positionForCoordinates(int x, int y) { - return VisiblePosition(element(), caretMinOffset(), DOWNSTREAM); + return positionForPoint(IntPoint(x, y)); } -VisiblePosition RenderObject::positionForPoint(const IntPoint& point) +VisiblePosition RenderObject::positionForPoint(const IntPoint&) { - return positionForCoordinates(point.x(), point.y()); + return createVisiblePosition(caretMinOffset(), DOWNSTREAM); } void RenderObject::updateDragState(bool dragOn) @@ -2389,12 +1907,9 @@ void RenderObject::updateDragState(bool dragOn) bool valueChanged = (dragOn != m_isDragging); m_isDragging = dragOn; if (valueChanged && style()->affectedByDragRules()) - element()->setChanged(); + node()->setChanged(); for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) curr->updateDragState(dragOn); - RenderFlow* continuation = virtualContinuation(); - if (continuation) - continuation->updateDragState(dragOn); } bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const IntPoint& point, int tx, int ty, HitTestFilter hitTestFilter) @@ -2425,34 +1940,12 @@ void RenderObject::updateHitTestResult(HitTestResult& result, const IntPoint& po if (result.innerNode()) return; - Node* node = element(); - IntPoint localPoint(point); - if (isRenderView()) - node = document()->documentElement(); - else if (!isInline() && virtualContinuation()) - // We are in the margins of block elements that are part of a continuation. In - // this case we're actually still inside the enclosing inline element that was - // split. Go ahead and set our inner node accordingly. - node = virtualContinuation()->element(); - - if (node) { - if (node->renderer() && node->renderer()->virtualContinuation() && node->renderer() != this) { - // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space - // of the principal renderer's containing block. This will end up being the innerNonSharedNode. - RenderBlock* firstBlock = node->renderer()->containingBlock(); - - // Get our containing block. - RenderBox* block = toRenderBox(this); - if (isInline()) - block = containingBlock(); - - localPoint.move(block->x() - firstBlock->x(), block->y() - firstBlock->y()); - } - - result.setInnerNode(node); + Node* n = node(); + if (n) { + result.setInnerNode(n); if (!result.innerNonSharedNode()) - result.setInnerNonSharedNode(node); - result.setLocalPoint(localPoint); + result.setInnerNonSharedNode(n); + result.setLocalPoint(point); } } @@ -2461,78 +1954,9 @@ bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int /*x*/, return false; } -int RenderObject::verticalPositionHint(bool firstLine) const -{ - if (firstLine) // We're only really a first-line style if the document actually uses first-line rules. - firstLine = document()->usesFirstLineRules(); - int vpos = m_verticalPosition; - if (m_verticalPosition == PositionUndefined || firstLine) { - vpos = getVerticalPosition(firstLine); - if (!firstLine) - m_verticalPosition = vpos; - } - - return vpos; -} - -int RenderObject::getVerticalPosition(bool firstLine) const -{ - if (!isInline()) - return 0; - - // This method determines the vertical position for inline elements. - int vpos = 0; - EVerticalAlign va = style()->verticalAlign(); - if (va == TOP) - vpos = PositionTop; - else if (va == BOTTOM) - vpos = PositionBottom; - else { - bool checkParent = parent()->isInline() && !parent()->isInlineBlockOrInlineTable() && parent()->style()->verticalAlign() != TOP && parent()->style()->verticalAlign() != BOTTOM; - vpos = checkParent ? parent()->verticalPositionHint(firstLine) : 0; - // don't allow elements nested inside text-top to have a different valignment. - if (va == BASELINE) - return vpos; - - const Font& f = parent()->style(firstLine)->font(); - int fontsize = f.pixelSize(); - - if (va == SUB) - vpos += fontsize / 5 + 1; - else if (va == SUPER) - vpos -= fontsize / 3 + 1; - else if (va == TEXT_TOP) - vpos += baselinePosition(firstLine) - f.ascent(); - else if (va == MIDDLE) - vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine) / 2 + baselinePosition(firstLine); - else if (va == TEXT_BOTTOM) { - vpos += f.descent(); - if (!isReplaced()) - vpos -= style(firstLine)->font().descent(); - } else if (va == BASELINE_MIDDLE) - vpos += -lineHeight(firstLine) / 2 + baselinePosition(firstLine); - else if (va == LENGTH) - vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine)); - } - - return vpos; -} - int RenderObject::lineHeight(bool firstLine, bool /*isRootLineBox*/) const { - RenderStyle* s = style(firstLine); - - Length lh = s->lineHeight(); - - // its "unset", choose nice default - if (lh.isNegative()) - return s->font().lineSpacing(); - - if (lh.isPercent()) - return lh.calcMinValue(s->fontSize()); - - // its fixed - return lh.value(); + return style(firstLine)->computedLineHeight(); } int RenderObject::baselinePosition(bool firstLine, bool isRootLineBox) const @@ -2544,7 +1968,7 @@ int RenderObject::baselinePosition(bool firstLine, bool isRootLineBox) const void RenderObject::scheduleRelayout() { if (isRenderView()) { - FrameView* view = static_cast<RenderView*>(this)->frameView(); + FrameView* view = toRenderView(this)->frameView(); if (view) view->scheduleRelayout(); } else if (parent()) { @@ -2554,59 +1978,42 @@ void RenderObject::scheduleRelayout() } } -void RenderObject::removeLeftoverAnonymousBlock(RenderBlock*) -{ -} - -InlineBox* RenderObject::createInlineBox(bool, bool unusedIsRootLineBox, bool) -{ - ASSERT_UNUSED(unusedIsRootLineBox, !unusedIsRootLineBox); - return new (renderArena()) InlineBox(this); -} - -void RenderObject::dirtyLineBoxes(bool, bool) -{ -} - -InlineBox* RenderObject::inlineBoxWrapper() const -{ - return 0; -} - -void RenderObject::setInlineBoxWrapper(InlineBox*) -{ -} - -void RenderObject::deleteLineBoxWrapper() +void RenderObject::layout() { + ASSERT(needsLayout()); + RenderObject* child = firstChild(); + while (child) { + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + child = child->nextSibling(); + } + setNeedsLayout(false); } -RenderStyle* RenderObject::firstLineStyle() const +RenderStyle* RenderObject::firstLineStyleSlowCase() const { - if (!document()->usesFirstLineRules()) - return m_style.get(); + ASSERT(document()->usesFirstLineRules()); - RenderStyle* s = m_style.get(); - const RenderObject* obj = isText() ? parent() : this; - if (obj->isBlockFlow()) { - RenderBlock* firstLineBlock = obj->firstLineBlock(); - if (firstLineBlock) - s = firstLineBlock->getCachedPseudoStyle(RenderStyle::FIRST_LINE, style()); - } else if (!obj->isAnonymous() && obj->isRenderInline()) { - RenderStyle* parentStyle = obj->parent()->firstLineStyle(); - if (parentStyle != obj->parent()->style()) { - // A first-line style is in effect. We need to cache a first-line style - // for ourselves. - style()->setHasPseudoStyle(RenderStyle::FIRST_LINE_INHERITED); - s = obj->getCachedPseudoStyle(RenderStyle::FIRST_LINE_INHERITED, parentStyle); + RenderStyle* style = m_style.get(); + const RenderObject* renderer = isText() ? parent() : this; + if (renderer->isBlockFlow()) { + if (RenderBlock* firstLineBlock = renderer->firstLineBlock()) + style = firstLineBlock->getCachedPseudoStyle(FIRST_LINE, style); + } else if (!renderer->isAnonymous() && renderer->isRenderInline()) { + RenderStyle* parentStyle = renderer->parent()->firstLineStyle(); + if (parentStyle != renderer->parent()->style()) { + // A first-line style is in effect. Cache a first-line style for ourselves. + style->setHasPseudoStyle(FIRST_LINE_INHERITED); + style = renderer->getCachedPseudoStyle(FIRST_LINE_INHERITED, parentStyle); } } - return s; + + return style; } -RenderStyle* RenderObject::getCachedPseudoStyle(RenderStyle::PseudoId pseudo, RenderStyle* parentStyle) const +RenderStyle* RenderObject::getCachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle) const { - if (pseudo < RenderStyle::FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) + if (pseudo < FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) return 0; RenderStyle* cachedStyle = style()->getCachedPseudoStyle(pseudo); @@ -2619,26 +2026,26 @@ RenderStyle* RenderObject::getCachedPseudoStyle(RenderStyle::PseudoId pseudo, Re return 0; } -PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(RenderStyle::PseudoId pseudo, RenderStyle* parentStyle) const +PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle) const { - if (pseudo < RenderStyle::FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) + if (pseudo < FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) return 0; if (!parentStyle) parentStyle = style(); - Node* node = element(); - while (node && !node->isElementNode()) - node = node->parentNode(); - if (!node) + Node* n = node(); + while (n && !n->isElementNode()) + n = n->parentNode(); + if (!n) return 0; RefPtr<RenderStyle> result; - if (pseudo == RenderStyle::FIRST_LINE_INHERITED) { - result = document()->styleSelector()->styleForElement(static_cast<Element*>(node), parentStyle, false); - result->setStyleType(RenderStyle::FIRST_LINE_INHERITED); + if (pseudo == FIRST_LINE_INHERITED) { + result = document()->styleSelector()->styleForElement(static_cast<Element*>(n), parentStyle, false); + result->setStyleType(FIRST_LINE_INHERITED); } else - result = document()->styleSelector()->pseudoStyleForElement(pseudo, static_cast<Element*>(node), parentStyle); + result = document()->styleSelector()->pseudoStyleForElement(pseudo, static_cast<Element*>(n), parentStyle); return result.release(); } @@ -2681,10 +2088,10 @@ void RenderObject::getTextDecorationColors(int decorations, Color& underline, Co } } curr = curr->parent(); - if (curr && curr->isRenderBlock() && curr->virtualContinuation()) - curr = curr->virtualContinuation(); - } while (curr && decorations && (!quirksMode || !curr->element() || - (!curr->element()->hasTagName(aTag) && !curr->element()->hasTagName(fontTag)))); + if (curr && curr->isRenderBlock() && toRenderBlock(curr)->inlineContinuation()) + curr = toRenderBlock(curr)->inlineContinuation(); + } while (curr && decorations && (!quirksMode || !curr->node() || + (!curr->node()->hasTagName(aTag) && !curr->node()->hasTagName(fontTag)))); // If we bailed out, use the element we bailed out at (typically a <font> or <a> element). if (decorations && curr) { @@ -2697,10 +2104,6 @@ void RenderObject::getTextDecorationColors(int decorations, Color& underline, Co } } -void RenderObject::updateWidgetPosition() -{ -} - #if ENABLE(DASHBOARD_SUPPORT) void RenderObject::addDashboardRegions(Vector<DashboardRegionValue>& regions) { @@ -2762,23 +2165,6 @@ void RenderObject::collectDashboardRegions(Vector<DashboardRegionValue>& regions } #endif -bool RenderObject::avoidsFloats() const -{ - return isReplaced() || hasOverflowClip() || isHR(); -} - -bool RenderObject::shrinkToAvoidFloats() const -{ - // FIXME: Technically we should be able to shrink replaced elements on a line, but this is difficult to accomplish, since this - // involves doing a relayout during findNextLineBreak and somehow overriding the containingBlockWidth method to return the - // current remaining width on a line. - if (isInline() && !isHTMLMarquee() || !avoidsFloats()) - return false; - - // All auto-width objects that avoid floats should always use lineWidth. - return style()->width().isAuto(); -} - bool RenderObject::willRenderImage(CachedImage*) { // Without visibility we won't render (and therefore don't care about animation). @@ -2794,7 +2180,7 @@ int RenderObject::maximalOutlineSize(PaintPhase p) const { if (p != PaintPhaseOutline && p != PaintPhaseSelfOutline && p != PaintPhaseChildOutlines) return 0; - return static_cast<RenderView*>(document()->renderer())->maximalOutlineSize(); + return toRenderView(document()->renderer())->maximalOutlineSize(); } int RenderObject::caretMinOffset() const @@ -2805,7 +2191,7 @@ int RenderObject::caretMinOffset() const int RenderObject::caretMaxOffset() const { if (isReplaced()) - return element() ? max(1U, element()->childNodeCount()) : 1; + return node() ? max(1U, node()->childNodeCount()) : 1; if (isHR()) return 1; return 0; @@ -2821,6 +2207,11 @@ int RenderObject::previousOffset(int current) const return current - 1; } +int RenderObject::previousOffsetForBackwardDeletion(int current) const +{ + return current - 1; +} + int RenderObject::nextOffset(int current) const { return current + 1; @@ -2828,7 +2219,7 @@ int RenderObject::nextOffset(int current) const void RenderObject::adjustRectForOutlineAndShadow(IntRect& rect) const { - int outlineSize = !isInline() && virtualContinuation() ? virtualContinuation()->style()->outlineSize() : style()->outlineSize(); + int outlineSize = outlineStyleForRepaint()->outlineSize(); if (ShadowData* boxShadow = style()->boxShadow()) { int shadowLeft = 0; int shadowRight = 0; @@ -2861,6 +2252,107 @@ void RenderObject::imageChanged(CachedImage* image, const IntRect* rect) imageChanged(static_cast<WrappedImagePtr>(image), rect); } +RenderBoxModelObject* RenderObject::offsetParent() const +{ + // If any of the following holds true return null and stop this algorithm: + // A is the root element. + // A is the HTML body element. + // The computed value of the position property for element A is fixed. + if (isRoot() || isBody() || (isPositioned() && style()->position() == FixedPosition)) + return 0; + + // If A is an area HTML element which has a map HTML element somewhere in the ancestor + // chain return the nearest ancestor map HTML element and stop this algorithm. + // FIXME: Implement! + + // Return the nearest ancestor element of A for which at least one of the following is + // true and stop this algorithm if such an ancestor is found: + // * The computed value of the position property is not static. + // * It is the HTML body element. + // * The computed value of the position property of A is static and the ancestor + // is one of the following HTML elements: td, th, or table. + // * Our own extension: if there is a difference in the effective zoom + bool skipTables = isPositioned() || isRelPositioned(); + float currZoom = style()->effectiveZoom(); + RenderObject* curr = parent(); + while (curr && (!curr->node() || + (!curr->isPositioned() && !curr->isRelPositioned() && !curr->isBody()))) { + Node* element = curr->node(); + if (!skipTables && element) { + bool isTableElement = element->hasTagName(tableTag) || + element->hasTagName(tdTag) || + element->hasTagName(thTag); + +#if ENABLE(WML) + if (!isTableElement && element->isWMLElement()) + isTableElement = element->hasTagName(WMLNames::tableTag) || + element->hasTagName(WMLNames::tdTag); +#endif + + if (isTableElement) + break; + } + + float newZoom = curr->style()->effectiveZoom(); + if (currZoom != newZoom) + break; + currZoom = newZoom; + curr = curr->parent(); + } + return curr && curr->isBoxModelObject() ? toRenderBoxModelObject(curr) : 0; +} + +VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity) +{ + // If is is a non-anonymous renderer, then it's simple. + if (Node* node = this->node()) + return VisiblePosition(node, offset, affinity); + + // We don't want to cross the boundary between editable and non-editable + // regions of the document, but that is either impossible or at least + // extremely unlikely in any normal case because we stop as soon as we + // find a single non-anonymous renderer. + + // Find a nearby non-anonymous renderer. + RenderObject* child = this; + while (RenderObject* parent = child->parent()) { + // Find non-anonymous content after. + RenderObject* renderer = child; + while ((renderer = renderer->nextInPreOrder(parent))) { + if (Node* node = renderer->node()) + return VisiblePosition(node, 0, DOWNSTREAM); + } + + // Find non-anonymous content before. + renderer = child; + while ((renderer = renderer->previousInPreOrder())) { + if (renderer == parent) + break; + if (Node* node = renderer->node()) + return VisiblePosition(node, numeric_limits<int>::max(), DOWNSTREAM); + } + + // Use the parent itself unless it too is anonymous. + if (Node* node = parent->node()) + return VisiblePosition(node, 0, DOWNSTREAM); + + // Repeat at the next level up. + child = parent; + } + + // Everything was anonymous. Give up. + return VisiblePosition(); +} + +VisiblePosition RenderObject::createVisiblePosition(const Position& position) +{ + if (position.isNotNull()) + return VisiblePosition(position); + + ASSERT(!node()); + return createVisiblePosition(0, DOWNSTREAM); +} + #if ENABLE(SVG) FloatRect RenderObject::relativeBBox(bool) const diff --git a/WebCore/rendering/RenderObject.h b/WebCore/rendering/RenderObject.h index 889ce9b..a601962 100644 --- a/WebCore/rendering/RenderObject.h +++ b/WebCore/rendering/RenderObject.h @@ -28,7 +28,11 @@ #include "CachedResourceClient.h" #include "FloatQuad.h" #include "Document.h" +#include "RenderObjectChildList.h" #include "RenderStyle.h" +#include "TextAffinity.h" +#include "TransformationMatrix.h" +#include <wtf/UnusedParam.h> namespace WebCore { @@ -36,9 +40,13 @@ class AnimationController; class HitTestResult; class InlineBox; class InlineFlowBox; +class Position; +class RenderBoxModelObject; +class RenderInline; class RenderBlock; class RenderFlow; class RenderLayer; +class TransformState; class VisiblePosition; /* @@ -85,10 +93,16 @@ enum HitTestAction { HitTestForeground }; -// Values for verticalPosition. -const int PositionTop = -0x7fffffff; -const int PositionBottom = 0x7fffffff; -const int PositionUndefined = 0x80000000; +// Sides used when drawing borders and outlines. This is in RenderObject rather than RenderBoxModelObject since outlines can +// be drawn by SVG around bounding boxes. +enum BoxSide { + BSTop, + BSBottom, + BSLeft, + BSRight +}; + +const int caretWidth = 1; #if ENABLE(DASHBOARD_SUPPORT) struct DashboardRegionValue { @@ -110,8 +124,10 @@ struct DashboardRegionValue { // Base class for all rendering tree objects. class RenderObject : public CachedResourceClient { - friend class RenderContainer; + friend class RenderBlock; + friend class RenderBox; friend class RenderLayer; + friend class RenderObjectChildList; friend class RenderSVGContainer; public: // Anonymous objects should pass the document as their node, and they will then automatically be @@ -119,7 +135,7 @@ public: RenderObject(Node*); virtual ~RenderObject(); - virtual const char* renderName() const { return "RenderObject"; } + virtual const char* renderName() const = 0; RenderObject* parent() const { return m_parent; } bool isDescendantOf(const RenderObject*) const; @@ -127,8 +143,20 @@ public: RenderObject* previousSibling() const { return m_previous; } RenderObject* nextSibling() const { return m_next; } - virtual RenderObject* firstChild() const { return 0; } - virtual RenderObject* lastChild() const { return 0; } + RenderObject* firstChild() const + { + if (const RenderObjectChildList* children = virtualChildren()) + return children->firstChild(); + return 0; + } + RenderObject* lastChild() const + { + if (const RenderObjectChildList* children = virtualChildren()) + return children->lastChild(); + return 0; + } + virtual RenderObjectChildList* virtualChildren() { return 0; } + virtual const RenderObjectChildList* virtualChildren() const { return 0; } RenderObject* nextInPreOrder() const; RenderObject* nextInPreOrder(RenderObject* stayWithin) const; @@ -140,10 +168,11 @@ public: RenderObject* firstLeafChild() const; RenderObject* lastLeafChild() const; - // The following five functions are used when the render tree hierarchy changes to make sure layers get + // The following six functions are used when the render tree hierarchy changes to make sure layers get // properly added and removed. Since containership can be implemented by any subclass, and since a hierarchy // can contain a mixture of boxes and other object types, these functions need to be in the base class. RenderLayer* enclosingLayer() const; + RenderLayer* enclosingSelfPaintingLayer() const; void addLayers(RenderLayer* parentLayer, RenderObject* newObject); void removeLayers(RenderLayer* parentLayer); void moveLayers(RenderLayer* oldParent, RenderLayer* newParent); @@ -152,18 +181,8 @@ public: // Convenience function for getting to the nearest enclosing box of a RenderObject. RenderBox* enclosingBox() const; - virtual IntRect getOverflowClipRect(int /*tx*/, int /*ty*/) { return IntRect(0, 0, 0, 0); } - virtual IntRect getClipRect(int /*tx*/, int /*ty*/) { return IntRect(0, 0, 0, 0); } - bool hasClip() { return isPositioned() && style()->hasClip(); } - - virtual int getBaselineOfFirstLineBox() const { return -1; } - virtual int getBaselineOfLastLineBox() const { return -1; } - virtual bool isEmpty() const { return firstChild() == 0; } - virtual bool isEdited() const { return false; } - virtual void setEdited(bool) { } - #ifndef NDEBUG void setHasAXObject(bool flag) { m_hasAXObject = flag; } bool hasAXObject() const { return m_hasAXObject; } @@ -179,35 +198,15 @@ public: // again. We have to make sure the render tree updates as needed to accommodate the new // normal flow object. void handleDynamicFloatPositionChange(); - - // This function is a convenience helper for creating an anonymous block that inherits its - // style from this RenderObject. - RenderBlock* createAnonymousBlock(); - - // Whether or not a positioned element requires normal flow x/y to be computed - // to determine its position. - bool hasStaticX() const; - bool hasStaticY() const; - virtual void setStaticX(int /*staticX*/) { } - virtual void setStaticY(int /*staticY*/) { } - virtual int staticX() const { return 0; } - virtual int staticY() const { return 0; } - + // RenderObject tree manipulation ////////////////////////////////////////// - virtual bool canHaveChildren() const; + virtual bool canHaveChildren() const { return virtualChildren(); } virtual bool isChildAllowed(RenderObject*, RenderStyle*) const { return true; } virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + virtual void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild = 0) { return addChild(newChild, beforeChild); } virtual void removeChild(RenderObject*); virtual bool createsAnonymousWrapper() const { return false; } - - // raw tree manipulation - virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); - virtual void appendChildNode(RenderObject*, bool fullAppend = true); - virtual void insertChildNode(RenderObject* child, RenderObject* before, bool fullInsert = true); - // Designed for speed. Don't waste time doing a bunch of work like layer updating and repainting when we know that our - // change in parentage is not going to affect anything. - virtual void moveChildNode(RenderObject*); ////////////////////////////////////////// protected: @@ -219,6 +218,7 @@ protected: ////////////////////////////////////////// private: void addAbsoluteRectForLayer(IntRect& result); + void setLayerNeedsFullRepaint(); public: #ifndef NDEBUG @@ -244,19 +244,20 @@ public: virtual bool isApplet() const { return false; } virtual bool isBR() const { return false; } virtual bool isBlockFlow() const { return false; } + virtual bool isBoxModelObject() const { return false; } virtual bool isCounter() const { return false; } virtual bool isFieldset() const { return false; } virtual bool isFrame() const { return false; } virtual bool isFrameSet() const { return false; } virtual bool isImage() const { return false; } virtual bool isInlineBlockOrInlineTable() const { return false; } - virtual bool isInlineContinuation() const; virtual bool isListBox() const { return false; } virtual bool isListItem() const { return false; } virtual bool isListMarker() const { return false; } virtual bool isMedia() const { return false; } virtual bool isMenuList() const { return false; } virtual bool isRenderBlock() const { return false; } + virtual bool isRenderButton() const { return false; } virtual bool isRenderImage() const { return false; } virtual bool isRenderInline() const { return false; } virtual bool isRenderPart() const { return false; } @@ -267,20 +268,23 @@ public: virtual bool isTableCol() const { return false; } virtual bool isTableRow() const { return false; } virtual bool isTableSection() const { return false; } + virtual bool isTextControl() const { return false; } virtual bool isTextArea() const { return false; } virtual bool isTextField() const { return false; } virtual bool isWidget() const { return false; } - bool isRoot() const { return document()->documentElement() == node(); } + bool isRoot() const { return document()->documentElement() == m_node; } bool isBody() const; bool isHR() const; bool isHTMLMarquee() const; - virtual bool childrenInline() const { return false; } - virtual void setChildrenInline(bool) { } - - virtual RenderFlow* virtualContinuation() const { return 0; } + bool childrenInline() const { return m_childrenInline; } + void setChildrenInline(bool b = true) { m_childrenInline = b; } + bool hasColumns() const { return m_hasColumns; } + void setHasColumns(bool b = true) { m_hasColumns = b; } + bool cellWidthChanged() const { return m_cellWidthChanged; } + void setCellWidthChanged(bool b = true) { m_cellWidthChanged = b; } #if ENABLE(SVG) virtual bool isSVGRoot() const { return false; } @@ -295,15 +299,13 @@ public: virtual TransformationMatrix absoluteTransform() const; #endif - virtual bool isEditable() const; - bool isAnonymous() const { return m_isAnonymous; } void setIsAnonymous(bool b) { m_isAnonymous = b; } bool isAnonymousBlock() const { - return m_isAnonymous && style()->display() == BLOCK && style()->styleType() == RenderStyle::NOPSEUDO && !isListMarker(); + return m_isAnonymous && style()->display() == BLOCK && style()->styleType() == NOPSEUDO && !isListMarker(); } - + bool isInlineContinuation() const { return (node() ? node()->renderer() != this : false) && isRenderInline(); } bool isFloating() const { return m_floating; } bool isPositioned() const { return m_positioned; } // absolute or fixed positioning bool isRelPositioned() const { return m_relPositioned; } // relative positioning @@ -318,7 +320,7 @@ public: bool hasBoxDecorations() const { return m_paintBackground; } bool mustRepaintBackgroundOrBorder() const; - + bool needsLayout() const { return m_needsLayout || m_normalChildNeedsLayout || m_posChildNeedsLayout || m_needsPositionedMovementLayout; } bool selfNeedsLayout() const { return m_needsLayout; } bool needsPositionedMovementLayout() const { return m_needsPositionedMovementLayout; } @@ -330,28 +332,33 @@ public: bool isSelectionBorder() const; + bool hasClip() const { return isPositioned() && style()->hasClip(); } bool hasOverflowClip() const { return m_hasOverflowClip; } - virtual bool hasControlClip() const { return false; } - virtual IntRect controlClipRect(int /*tx*/, int /*ty*/) const { return IntRect(); } bool hasTransform() const { return m_hasTransform; } bool hasMask() const { return style() && style()->hasMask(); } + void drawLineForBoxSide(GraphicsContext*, int x1, int y1, int x2, int y2, BoxSide, + Color, const Color& textcolor, EBorderStyle, int adjbw1, int adjbw2); + void drawArcForBoxSide(GraphicsContext*, int x, int y, float thickness, IntSize radius, int angleStart, + int angleSpan, BoxSide, Color, const Color& textcolor, EBorderStyle, bool firstCorner); + public: // The pseudo element style can be cached or uncached. Use the cached method if the pseudo element doesn't respect // any pseudo classes (and therefore has no concept of changing state). - RenderStyle* getCachedPseudoStyle(RenderStyle::PseudoId, RenderStyle* parentStyle = 0) const; - PassRefPtr<RenderStyle> getUncachedPseudoStyle(RenderStyle::PseudoId, RenderStyle* parentStyle = 0) const; + RenderStyle* getCachedPseudoStyle(PseudoId, RenderStyle* parentStyle = 0) const; + PassRefPtr<RenderStyle> getUncachedPseudoStyle(PseudoId, RenderStyle* parentStyle = 0) const; - void updateDragState(bool dragOn); + virtual void updateDragState(bool dragOn); RenderView* view() const; - // don't even think about making this method virtual! - Node* element() const { return m_isAnonymous ? 0 : m_node; } + // Returns true if this renderer is rooted, and optionally returns the hosting view (the root of the hierarchy). + bool isRooted(RenderView** = 0); + + Node* node() const { return m_isAnonymous ? 0 : m_node; } Document* document() const { return m_node->document(); } void setNode(Node* node) { m_node = node; } - Node* node() const { return m_node; } bool hasOutlineAnnotation() const; bool hasOutline() const { return style()->hasOutline() || hasOutlineAnnotation(); } @@ -361,7 +368,10 @@ public: * positioned elements */ RenderObject* container() const; - RenderObject* hoverAncestor() const; + virtual RenderObject* hoverAncestor() const { return parent(); } + + // IE Extension that can be called on any RenderObject. See the implementation for the details. + RenderBoxModelObject* offsetParent() const; void markContainingBlocksForLayout(bool scheduleRelayout = true, RenderObject* newRoot = 0); void setNeedsLayout(bool b, bool markParents = true); @@ -369,7 +379,6 @@ public: void setNeedsPositionedMovementLayout(); void setPrefWidthsDirty(bool, bool markParents = true); void invalidateContainerPrefWidths(); - virtual void invalidateCounters() { } void setNeedsLayoutAndPrefWidthsRecalc() { @@ -395,21 +404,9 @@ public: void updateFillImages(const FillLayer*, const FillLayer*); void updateImage(StyleImage*, StyleImage*); - virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun = false); - virtual void dirtyLineBoxes(bool fullLayout, bool isRootLineBox = false); - - // For inline replaced elements, this function returns the inline box that owns us. Enables - // the replaced RenderObject to quickly determine what line it is contained on and to easily - // iterate over structures on the line. - virtual InlineBox* inlineBoxWrapper() const; - virtual void setInlineBoxWrapper(InlineBox*); - virtual void deleteLineBoxWrapper(); - // for discussion of lineHeight see CSS2 spec virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; // for the vertical-align property of inline elements - // the difference between this objects baseline position and the lines baseline position. - virtual int verticalPositionHint(bool firstLine) const; // the offset of baseline from the top of the object. virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; @@ -419,7 +416,7 @@ public: */ struct PaintInfo { PaintInfo(GraphicsContext* newContext, const IntRect& newRect, PaintPhase newPhase, bool newForceBlackText, - RenderObject* newPaintingRoot, ListHashSet<RenderFlow*>* newOutlineObjects) + RenderObject* newPaintingRoot, ListHashSet<RenderInline*>* newOutlineObjects) : context(newContext) , rect(newRect) , phase(newPhase) @@ -434,38 +431,13 @@ public: PaintPhase phase; bool forceBlackText; RenderObject* paintingRoot; // used to draw just one element and its visual kids - ListHashSet<RenderFlow*>* outlineObjects; // used to list outlines that should be painted by a block with inline children + ListHashSet<RenderInline*>* outlineObjects; // used to list outlines that should be painted by a block with inline children }; virtual void paint(PaintInfo&, int tx, int ty); - 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*, bool begin = true, bool end = true); - - // RenderBox implements this. - virtual void paintBoxDecorations(PaintInfo&, int /*tx*/, int /*ty*/) { } - virtual void paintMask(PaintInfo&, int /*tx*/, int /*ty*/) { } - virtual void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, - int /*clipY*/, int /*clipH*/, int /*tx*/, int /*ty*/, int /*width*/, int /*height*/, - InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver) { } - /* - * Calculates the actual width of the object (only for non inline - * objects) - */ - virtual void calcWidth() { } - - /* - * This function should cause the Element to calculate its - * width and height and the layout of its content - * - * when the Element calls setNeedsLayout(false), layout() is no - * longer called during relayouts, as long as there is no - * style sheet change. When that occurs, m_needsLayout will be - * set to true and the Element receives layout() calls - * again. - */ - virtual void layout() = 0; + // Recursive function that computes the size and position of this object and all its descendants. + virtual void layout(); /* This function performs a layout only if one is needed. */ void layoutIfNeeded() { if (needsLayout()) layout(); } @@ -478,19 +450,19 @@ public: // repaint and do not need a relayout virtual void updateFromElement() { } - virtual void updateWidgetPosition(); - #if ENABLE(DASHBOARD_SUPPORT) - void addDashboardRegions(Vector<DashboardRegionValue>&); + virtual void addDashboardRegions(Vector<DashboardRegionValue>&); void collectDashboardRegions(Vector<DashboardRegionValue>&); #endif bool hitTest(const HitTestRequest&, HitTestResult&, const IntPoint&, int tx, int ty, HitTestFilter = HitTestAll); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); - void updateHitTestResult(HitTestResult&, const IntPoint&); + virtual void updateHitTestResult(HitTestResult&, const IntPoint&); - virtual VisiblePosition positionForCoordinates(int x, int y); - VisiblePosition positionForPoint(const IntPoint&); + VisiblePosition positionForCoordinates(int x, int y); + virtual VisiblePosition positionForPoint(const IntPoint&); + VisiblePosition createVisiblePosition(int offset, EAffinity); + VisiblePosition createVisiblePosition(const Position&); virtual void dirtyLinesFromChangedChild(RenderObject*); @@ -509,21 +481,10 @@ public: // returns the containing block level element for this element. RenderBlock* containingBlock() const; - // return just the width of the containing block - virtual int containingBlockWidth() const; - // return just the height of the containing block - virtual int containingBlockHeight() const; - - // used by flexible boxes to impose a flexed width/height override - virtual int overrideSize() const { return 0; } - virtual int overrideWidth() const { return 0; } - virtual int overrideHeight() const { return 0; } - virtual void setOverrideSize(int /*overrideSize*/) { } - // Convert the given local point to absolute coordinates // FIXME: Temporary. If useTransforms is true, take transforms into account. Eventually localToAbsolute() will always be transform-aware. - virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; - virtual FloatPoint absoluteToLocal(FloatPoint, bool fixed = false, bool useTransforms = false) const; + FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; + FloatPoint absoluteToLocal(FloatPoint, bool fixed = false, bool useTransforms = false) const; // Convert a local quad to absolute coordinates, taking transforms into account. FloatQuad localToAbsoluteQuad(const FloatQuad& quad, bool fixed = false) const @@ -531,19 +492,19 @@ public: return localToContainerQuad(quad, 0, fixed); } // Convert a local quad into the coordinate system of container, taking transforms into account. - virtual FloatQuad localToContainerQuad(const FloatQuad&, RenderBox* repaintContainer, bool fixed = false) const; + FloatQuad localToContainerQuad(const FloatQuad&, RenderBoxModelObject* repaintContainer, bool fixed = false) const; // Return the offset from the container() renderer (excluding transforms) virtual IntSize offsetFromContainer(RenderObject*) const; - virtual void addLineBoxRects(Vector<IntRect>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); + virtual void absoluteRectsForRange(Vector<IntRect>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); virtual void absoluteRects(Vector<IntRect>&, int, int, bool = true) { } // FIXME: useTransforms should go away eventually IntRect absoluteBoundingBoxRect(bool useTransforms = false); // Build an array of quads in absolute coords for line boxes - virtual void collectAbsoluteLineBoxQuads(Vector<FloatQuad>&, unsigned /*startOffset*/ = 0, unsigned /*endOffset*/ = UINT_MAX, bool /*useSelectionHeight*/ = false) { } + virtual void absoluteQuadsForRange(Vector<FloatQuad>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); virtual void absoluteQuads(Vector<FloatQuad>&, bool /*topLevel*/ = true) { } // the rect that will be painted if this object is passed as the paintingRoot @@ -553,28 +514,23 @@ public: virtual int maxPrefWidth() const { return 0; } RenderStyle* style() const { return m_style.get(); } - RenderStyle* firstLineStyle() const; + RenderStyle* firstLineStyle() const { return document()->usesFirstLineRules() ? firstLineStyleSlowCase() : style(); } RenderStyle* style(bool firstLine) const { return firstLine ? firstLineStyle() : style(); } - + + // Anonymous blocks that are part of of a continuation chain will return their inline continuation's outline style instead. + // This is typically only relevant when repainting. + virtual RenderStyle* outlineStyleForRepaint() const { return style(); } + void getTextDecorationColors(int decorations, Color& underline, Color& overline, Color& linethrough, bool quirksMode = false); - enum BorderSide { - BSTop, - BSBottom, - BSLeft, - BSRight - }; - - void drawBorderArc(GraphicsContext*, int x, int y, float thickness, IntSize radius, int angleStart, - int angleSpan, BorderSide, Color, const Color& textcolor, EBorderStyle, bool firstCorner); - void drawBorder(GraphicsContext*, int x1, int y1, int x2, int y2, BorderSide, - Color, const Color& textcolor, EBorderStyle, int adjbw1, int adjbw2); - // Return the RenderBox in the container chain which is responsible for painting this object, or 0 // if painting is root-relative. This is the container that should be passed to the 'forRepaint' // methods. - RenderBox* containerForRepaint() const; + RenderBoxModelObject* containerForRepaint() const; + // Actually do the repaint of rect r for this object which has been computed in the coordinate space + // of repaintContainer. If repaintContainer is 0, repaint via the view. + void repaintUsingContainer(RenderBoxModelObject* repaintContainer, const IntRect& r, bool immediate = false); // Repaint the entire object. Called when, e.g., the color of a border changes, or when a border // style changes. @@ -584,7 +540,7 @@ public: void repaintRectangle(const IntRect&, bool immediate = false); // Repaint only if our old bounds and new bounds are different. - bool repaintAfterLayoutIfNeeded(const IntRect& oldBounds, const IntRect& oldOutlineBox); + bool repaintAfterLayoutIfNeeded(RenderBoxModelObject* repaintContainer, const IntRect& oldBounds, const IntRect& oldOutlineBox); // Repaint only if the object moved. virtual void repaintDuringLayoutIfMoved(const IntRect& rect); @@ -600,32 +556,22 @@ public: { return clippedOverflowRectForRepaint(0); } - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); - - IntRect rectWithOutlineForRepaint(RenderBox* repaintContainer, int outlineWidth); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); + virtual IntRect rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth); // Given a rect in the object's coordinate space, compute a rect suitable for repainting // that rect in view coordinates. void computeAbsoluteRepaintRect(IntRect& r, bool fixed = false) { - return computeRectForRepaint(r, 0, fixed); + return computeRectForRepaint(0, r, fixed); } // Given a rect in the object's coordinate space, compute a rect suitable for repainting // that rect in the coordinate space of repaintContainer. - virtual void computeRectForRepaint(IntRect&, RenderBox* repaintContainer, bool fixed = false); + virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); virtual unsigned int length() const { return 1; } bool isFloatingOrPositioned() const { return (isFloating() || isPositioned()); } - virtual bool containsFloats() { return false; } - virtual bool containsFloat(RenderObject*) { return false; } - virtual bool hasOverhangingFloats() { return false; } - - virtual bool avoidsFloats() const; - bool shrinkToAvoidFloats() const; - - // positioning of inline children (bidi) - virtual void position(InlineBox*) { } bool isTransparent() const { return style()->opacity() < 1.0f; } float opacity() const { return style()->opacity(); } @@ -635,6 +581,9 @@ public: // Applied as a "slop" to dirty rect checks during the outline painting phase's dirty-rect checks. int maximalOutlineSize(PaintPhase) const; + void setHasMarkupTruncation(bool b = true) { m_hasMarkupTruncation = b; } + bool hasMarkupTruncation() const { return m_hasMarkupTruncation; } + enum SelectionState { SelectionNone, // The object is not selected. SelectionStart, // The object either contains the start of a selection run or is the start of a run @@ -645,20 +594,21 @@ public: // The current selection state for an object. For blocks, the state refers to the state of the leaf // descendants (as described above in the SelectionState enum declaration). - virtual SelectionState selectionState() const { return SelectionNone; } + SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState);; } // Sets the selection state for an object. - virtual void setSelectionState(SelectionState state) { if (parent()) parent()->setSelectionState(state); } + virtual void setSelectionState(SelectionState state) { m_selectionState = state; } // A single rectangle that encompasses all of the selected objects within this object. Used to determine the tightest // possible bounding box for the selection. - virtual IntRect selectionRect(bool) { return IntRect(); } + IntRect selectionRect(bool clipToVisibleContent = true) { return selectionRectForRepaint(0, clipToVisibleContent); } + virtual IntRect selectionRectForRepaint(RenderBoxModelObject* /*repaintContainer*/, bool /*clipToVisibleContent*/ = true) { return IntRect(); } // Whether or not an object can be part of the leaf elements of the selection. virtual bool canBeSelectionLeaf() const { return false; } // Whether or not a block has selected children. - virtual bool hasSelectedChildren() const { return false; } + bool hasSelectedChildren() const { return m_selectionState != SelectionNone; } // Obtains the selection colors that should be used when painting a selection. Color selectionBackgroundColor() const; @@ -667,30 +617,6 @@ public: // Whether or not a given block needs to paint selection gaps. virtual bool shouldPaintSelectionGaps() const { return false; } - // This struct is used when the selection changes to cache the old and new state of the selection for each RenderObject. - struct SelectionInfo { - SelectionInfo() - : m_object(0) - , m_state(SelectionNone) - { - } - - SelectionInfo(RenderObject* o, bool clipToVisibleContent) - : m_object(o) - , m_rect(o->needsLayout() ? IntRect() : o->selectionRect(clipToVisibleContent)) - , m_state(o->selectionState()) - { - } - - RenderObject* object() const { return m_object; } - IntRect rect() const { return m_rect; } - SelectionState state() const { return m_state; } - - RenderObject* m_object; - IntRect m_rect; - SelectionState m_state; - }; - Node* draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const; /** @@ -701,12 +627,11 @@ public: */ virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); - virtual int lowestPosition(bool /*includeOverflowInterior*/ = true, bool /*includeSelf*/ = true) const { return 0; } - virtual int rightmostPosition(bool /*includeOverflowInterior*/ = true, bool /*includeSelf*/ = true) const { return 0; } - virtual int leftmostPosition(bool /*includeOverflowInterior*/ = true, bool /*includeSelf*/ = true) const { return 0; } - virtual void calcVerticalMargins() { } - void removeFromObjectLists(); + bool isTopMarginQuirk() const { return m_topMarginQuirk; } + bool isBottomMarginQuirk() const { return m_bottomMarginQuirk; } + void setTopMarginQuirk(bool b = true) { m_topMarginQuirk = b; } + void setBottomMarginQuirk(bool b = true) { m_bottomMarginQuirk = b; } // When performing a global document tear-down, the renderer of the document is cleared. We use this // as a hook to detect the case of document destruction and don't waste time doing unnecessary work. @@ -724,13 +649,14 @@ public: virtual unsigned caretMaxRenderedOffset() const; virtual int previousOffset(int current) const; + virtual int previousOffsetForBackwardDeletion(int current) const; virtual int nextOffset(int current) const; virtual void imageChanged(CachedImage*, const IntRect* = 0); virtual void imageChanged(WrappedImagePtr, const IntRect* = 0) { } virtual bool willRenderImage(CachedImage*); - virtual void selectionStartEnd(int& spos, int& epos) const; + void selectionStartEnd(int& spos, int& epos) const; RenderObject* paintingRootForChildren(PaintInfo& paintInfo) const { @@ -742,24 +668,22 @@ public: { return !paintInfo.paintingRoot || paintInfo.paintingRoot == this; } -#ifdef ANDROID_LAYOUT - virtual bool hasChildTable() const { return false; } -#endif bool hasOverrideSize() const { return m_hasOverrideSize; } void setHasOverrideSize(bool b) { m_hasOverrideSize = b; } void remove() { if (parent()) parent()->removeChild(this); } - void invalidateVerticalPosition() { m_verticalPosition = PositionUndefined; } - - virtual void removeLeftoverAnonymousBlock(RenderBlock* child); - - virtual void capsLockStateMayHaveChanged() { } - AnimationController* animation() const; bool visibleToHitTesting() const { return style()->visibility() == VISIBLE && style()->pointerEvents() != PE_NONE; } + + // Map points and quads through elements, potentially via 3d transforms. You should never need to call these directly; use + // localToAbsolute/absoluteToLocal methods instead. + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; + virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const; + + TransformationMatrix transformFromContainer(const RenderObject* container, const IntSize& offsetInContainer) const; virtual void addFocusRingRects(GraphicsContext*, int /*tx*/, int /*ty*/) { }; @@ -768,28 +692,60 @@ public: return outlineBoundsForRepaint(0); } + bool replacedHasOverflow() const { return m_replacedHasOverflow; } + void setReplacedHasOverflow(bool b = true) { m_replacedHasOverflow = b; } + protected: // Overrides should call the superclass at the end - virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); // Overrides should call the superclass at the start - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); - - virtual void printBoxDecorations(GraphicsContext*, int /*x*/, int /*y*/, int /*w*/, int /*h*/, int /*tx*/, int /*ty*/) { } + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); void paintOutline(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*); void addPDFURLRect(GraphicsContext*, const IntRect&); virtual IntRect viewRect() const; - int getVerticalPosition(bool firstLine) const; - void adjustRectForOutlineAndShadow(IntRect&) const; void arenaDelete(RenderArena*, void* objectBase); - virtual IntRect outlineBoundsForRepaint(RenderBox* /*repaintContainer*/) const { return IntRect(); } + virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/) const { return IntRect(); } + class LayoutRepainter { + public: + LayoutRepainter(RenderObject& object, bool checkForRepaint, const IntRect* oldBounds = 0) + : m_object(object) + , m_repaintContainer(0) + , m_checkForRepaint(checkForRepaint) + { + if (m_checkForRepaint) { + m_repaintContainer = m_object.containerForRepaint(); + m_oldBounds = oldBounds ? *oldBounds : m_object.clippedOverflowRectForRepaint(m_repaintContainer); + m_oldOutlineBox = m_object.outlineBoundsForRepaint(m_repaintContainer); + } + } + + // Return true if it repainted. + bool repaintAfterLayout() + { + return m_checkForRepaint ? m_object.repaintAfterLayoutIfNeeded(m_repaintContainer, m_oldBounds, m_oldOutlineBox) : false; + } + + bool checkForRepaint() const { return m_checkForRepaint; } + + private: + RenderObject& m_object; + RenderBoxModelObject* m_repaintContainer; + IntRect m_oldBounds; + IntRect m_oldOutlineBox; + bool m_checkForRepaint; + }; + private: + RenderStyle* firstLineStyleSlowCase() const; + StyleDifference adjustStyleDifference(StyleDifference, unsigned contextSensitiveProperties) const; + RefPtr<RenderStyle> m_style; Node* m_node; @@ -802,8 +758,8 @@ private: bool m_hasAXObject; bool m_setNeedsLayoutForbidden : 1; #endif - mutable int m_verticalPosition; + // 32 bits have been used here. THERE ARE NO FREE BITS AVAILABLE. bool m_needsLayout : 1; bool m_needsPositionedMovementLayout :1; bool m_normalChildNeedsLayout : 1; @@ -835,10 +791,145 @@ public: bool m_everHadLayout : 1; private: + // These bitfields are moved here from subclasses to pack them together + // from RenderBlock + bool m_childrenInline : 1; + bool m_topMarginQuirk : 1; + bool m_bottomMarginQuirk : 1; + bool m_hasMarkupTruncation : 1; + unsigned m_selectionState : 3; // SelectionState + bool m_hasColumns : 1; + + // from RenderTableCell + bool m_cellWidthChanged : 1; + + // from RenderReplaced + bool m_replacedHasOverflow : 1; + +private: // Store state between styleWillChange and styleDidChange static bool s_affectsParentBlock; }; +inline bool RenderObject::documentBeingDestroyed() const +{ + return !document()->renderer(); +} + +inline void RenderObject::setNeedsLayout(bool b, bool markParents) +{ + bool alreadyNeededLayout = m_needsLayout; + m_needsLayout = b; + if (b) { + ASSERT(!isSetNeedsLayoutForbidden()); + if (!alreadyNeededLayout) { + if (markParents) + markContainingBlocksForLayout(); + if (hasLayer()) + setLayerNeedsFullRepaint(); + } + } else { + m_everHadLayout = true; + m_posChildNeedsLayout = false; + m_normalChildNeedsLayout = false; + m_needsPositionedMovementLayout = false; + } +} + +inline void RenderObject::setChildNeedsLayout(bool b, bool markParents) +{ + bool alreadyNeededLayout = m_normalChildNeedsLayout; + m_normalChildNeedsLayout = b; + if (b) { + ASSERT(!isSetNeedsLayoutForbidden()); + if (!alreadyNeededLayout && markParents) + markContainingBlocksForLayout(); + } else { + m_posChildNeedsLayout = false; + m_normalChildNeedsLayout = false; + m_needsPositionedMovementLayout = false; + } +} + +inline void RenderObject::setNeedsPositionedMovementLayout() +{ + bool alreadyNeededLayout = needsLayout(); + m_needsPositionedMovementLayout = true; + if (!alreadyNeededLayout) { + markContainingBlocksForLayout(); + if (hasLayer()) + setLayerNeedsFullRepaint(); + } +} + +inline bool objectIsRelayoutBoundary(const RenderObject *obj) +{ + // FIXME: In future it may be possible to broaden this condition in order to improve performance. + // Table cells are excluded because even when their CSS height is fixed, their height() + // may depend on their contents. + return obj->isTextControl() + || (obj->hasOverflowClip() && !obj->style()->width().isIntrinsicOrAuto() && !obj->style()->height().isIntrinsicOrAuto() && !obj->style()->height().isPercent() && !obj->isTableCell()) +#if ENABLE(SVG) + || obj->isSVGRoot() +#endif + ; +} + +inline void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderObject* newRoot) +{ + ASSERT(!scheduleRelayout || !newRoot); + + RenderObject* o = container(); + RenderObject* last = this; + + while (o) { + // Don't mark the outermost object of an unrooted subtree. That object will be + // marked when the subtree is added to the document. + RenderObject* container = o->container(); + if (!container && !o->isRenderView()) + return; + if (!last->isText() && (last->style()->position() == FixedPosition || last->style()->position() == AbsolutePosition)) { + if ((last->style()->top().isAuto() && last->style()->bottom().isAuto()) || last->style()->top().isStatic()) { + RenderObject* parent = last->parent(); + if (!parent->normalChildNeedsLayout()) { + parent->setChildNeedsLayout(true, false); + if (parent != newRoot) + parent->markContainingBlocksForLayout(scheduleRelayout, newRoot); + } + } + if (o->m_posChildNeedsLayout) + return; + o->m_posChildNeedsLayout = true; + ASSERT(!o->isSetNeedsLayoutForbidden()); + } else { + if (o->m_normalChildNeedsLayout) + return; + o->m_normalChildNeedsLayout = true; + ASSERT(!o->isSetNeedsLayoutForbidden()); + } + + if (o == newRoot) + return; + + last = o; + if (scheduleRelayout && objectIsRelayoutBoundary(last)) + break; + o = container; + } + + if (scheduleRelayout) + last->scheduleRelayout(); +} + +inline void makeMatrixRenderable(TransformationMatrix& matrix) +{ +#if !ENABLE(3D_RENDERING) + matrix.makeAffine(); +#else + UNUSED_PARAM(matrix); +#endif +} + } // namespace WebCore #ifndef NDEBUG diff --git a/WebCore/rendering/RenderObjectChildList.cpp b/WebCore/rendering/RenderObjectChildList.cpp new file mode 100644 index 0000000..32e856f --- /dev/null +++ b/WebCore/rendering/RenderObjectChildList.cpp @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2009 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 COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderObjectChildList.h" + +#include "AXObjectCache.h" +#include "RenderBlock.h" +#include "RenderCounter.h" +#include "RenderImageGeneratedContent.h" +#include "RenderInline.h" +#include "RenderLayer.h" +#include "RenderListItem.h" +#include "RenderStyle.h" +#include "RenderTextFragment.h" +#include "RenderView.h" + +namespace WebCore { + +static void updateListMarkerNumbers(RenderObject* child) +{ + for (RenderObject* r = child; r; r = r->nextSibling()) { + if (r->isListItem()) + static_cast<RenderListItem*>(r)->updateValue(); + } +} + + +void RenderObjectChildList::destroyLeftoverChildren() +{ + while (firstChild()) { + if (firstChild()->isListMarker() || (firstChild()->style()->styleType() == FIRST_LETTER && !firstChild()->isText())) + firstChild()->remove(); // List markers are owned by their enclosing list and so don't get destroyed by this container. Similarly, first letters are destroyed by their remaining text fragment. + else { + // Destroy any anonymous children remaining in the render tree, as well as implicit (shadow) DOM elements like those used in the engine-based text fields. + if (firstChild()->node()) + firstChild()->node()->setRenderer(0); + firstChild()->destroy(); + } + } +} + +RenderObject* RenderObjectChildList::removeChildNode(RenderObject* owner, RenderObject* oldChild, bool fullRemove) +{ + ASSERT(oldChild->parent() == owner); + + // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or + // that a positioned child got yanked). We also repaint, so that the area exposed when the child + // disappears gets repainted properly. + if (!owner->documentBeingDestroyed() && fullRemove && oldChild->m_everHadLayout) { + oldChild->setNeedsLayoutAndPrefWidthsRecalc(); + oldChild->repaint(); + } + + // If we have a line box wrapper, delete it. + if (oldChild->isBox()) + toRenderBox(oldChild)->deleteLineBoxWrapper(); + + if (!owner->documentBeingDestroyed() && fullRemove) { + // if we remove visible child from an invisible parent, we don't know the layer visibility any more + RenderLayer* layer = 0; + if (owner->style()->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->hasLayer()) { + layer = owner->enclosingLayer(); + layer->dirtyVisibleContentStatus(); + } + + // Keep our layer hierarchy updated. + if (oldChild->firstChild() || oldChild->hasLayer()) { + if (!layer) + layer = owner->enclosingLayer(); + oldChild->removeLayers(layer); + } + + // renumber ordered lists + if (oldChild->isListItem()) + updateListMarkerNumbers(oldChild->nextSibling()); + + if (oldChild->isPositioned() && owner->childrenInline()) + owner->dirtyLinesFromChangedChild(oldChild); + } + + // If oldChild is the start or end of the selection, then clear the selection to + // avoid problems of invalid pointers. + // FIXME: The SelectionController should be responsible for this when it + // is notified of DOM mutations. + if (!owner->documentBeingDestroyed() && oldChild->isSelectionBorder()) + owner->view()->clearSelection(); + + // remove the child + if (oldChild->previousSibling()) + oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); + if (oldChild->nextSibling()) + oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); + + if (firstChild() == oldChild) + setFirstChild(oldChild->nextSibling()); + if (lastChild() == oldChild) + setLastChild(oldChild->previousSibling()); + + oldChild->setPreviousSibling(0); + oldChild->setNextSibling(0); + oldChild->setParent(0); + + if (AXObjectCache::accessibilityEnabled()) + owner->document()->axObjectCache()->childrenChanged(owner); + + return oldChild; +} + +void RenderObjectChildList::appendChildNode(RenderObject* owner, RenderObject* newChild, bool fullAppend) +{ + ASSERT(newChild->parent() == 0); + ASSERT(!owner->isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableRow() && !newChild->isTableCell())); + + newChild->setParent(owner); + RenderObject* lChild = lastChild(); + + if (lChild) { + newChild->setPreviousSibling(lChild); + lChild->setNextSibling(newChild); + } else + setFirstChild(newChild); + + setLastChild(newChild); + + if (fullAppend) { + // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children + // and don't have a layer attached to ourselves. + RenderLayer* layer = 0; + if (newChild->firstChild() || newChild->hasLayer()) { + layer = owner->enclosingLayer(); + newChild->addLayers(layer, newChild); + } + + // if the new child is visible but this object was not, tell the layer it has some visible content + // that needs to be drawn and layer visibility optimization can't be used + if (owner->style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->hasLayer()) { + if (!layer) + layer = owner->enclosingLayer(); + if (layer) + layer->setHasVisibleContent(true); + } + + if (!newChild->isFloatingOrPositioned() && owner->childrenInline()) + owner->dirtyLinesFromChangedChild(newChild); + } + + newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy. + if (!owner->normalChildNeedsLayout()) + owner->setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. + + if (AXObjectCache::accessibilityEnabled()) + owner->document()->axObjectCache()->childrenChanged(owner); +} + +void RenderObjectChildList::insertChildNode(RenderObject* owner, RenderObject* child, RenderObject* beforeChild, bool fullInsert) +{ + if (!beforeChild) { + appendChildNode(owner, child); + return; + } + + ASSERT(!child->parent()); + while (beforeChild->parent() != owner && beforeChild->parent()->isAnonymousBlock()) + beforeChild = beforeChild->parent(); + ASSERT(beforeChild->parent() == owner); + + ASSERT(!owner->isBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell())); + + if (beforeChild == firstChild()) + setFirstChild(child); + + RenderObject* prev = beforeChild->previousSibling(); + child->setNextSibling(beforeChild); + beforeChild->setPreviousSibling(child); + if (prev) + prev->setNextSibling(child); + child->setPreviousSibling(prev); + + child->setParent(owner); + + if (fullInsert) { + // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children + // and don't have a layer attached to ourselves. + RenderLayer* layer = 0; + if (child->firstChild() || child->hasLayer()) { + layer = owner->enclosingLayer(); + child->addLayers(layer, child); + } + + // if the new child is visible but this object was not, tell the layer it has some visible content + // that needs to be drawn and layer visibility optimization can't be used + if (owner->style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) { + if (!layer) + layer = owner->enclosingLayer(); + if (layer) + layer->setHasVisibleContent(true); + } + + + if (!child->isFloating() && owner->childrenInline()) + owner->dirtyLinesFromChangedChild(child); + } + + child->setNeedsLayoutAndPrefWidthsRecalc(); + if (!owner->normalChildNeedsLayout()) + owner->setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. + + if (AXObjectCache::accessibilityEnabled()) + owner->document()->axObjectCache()->childrenChanged(owner); +} + +static RenderObject* beforeAfterContainer(RenderObject* container, PseudoId type) +{ + if (type == BEFORE) { + RenderObject* first = container; + do { + // Skip list markers. + first = first->firstChild(); + while (first && first->isListMarker()) + first = first->nextSibling(); + } while (first && first->isAnonymous() && first->style()->styleType() == NOPSEUDO); + if (first && first->style()->styleType() != type) + return 0; + return first; + } + if (type == AFTER) { + RenderObject* last = container; + do { + last = last->lastChild(); + } while (last && last->isAnonymous() && last->style()->styleType() == NOPSEUDO && !last->isListMarker()); + if (last && last->style()->styleType() != type) + return 0; + return last; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +static RenderObject* findBeforeAfterParent(RenderObject* object) +{ + // Only table parts need to search for the :before or :after parent + if (!(object->isTable() || object->isTableSection() || object->isTableRow())) + return object; + + RenderObject* beforeAfterParent = object; + while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterParent->isImage())) + beforeAfterParent = beforeAfterParent->firstChild(); + return beforeAfterParent; +} + +static void invalidateCountersInContainer(RenderObject* container) +{ + if (!container) + return; + container = findBeforeAfterParent(container); + if (!container) + return; + for (RenderObject* content = container->firstChild(); content; content = content->nextSibling()) { + if (content->isCounter()) + static_cast<RenderCounter*>(content)->invalidate(); + } +} + +void RenderObjectChildList::invalidateCounters(RenderObject* owner) +{ + ASSERT(!owner->documentBeingDestroyed()); + invalidateCountersInContainer(beforeAfterContainer(owner, BEFORE)); + invalidateCountersInContainer(beforeAfterContainer(owner, AFTER)); +} + +void RenderObjectChildList::updateBeforeAfterContent(RenderObject* owner, PseudoId type, RenderObject* styledObject) +{ + // Double check that the document did in fact use generated content rules. Otherwise we should not have been called. + ASSERT(owner->document()->usesBeforeAfterRules()); + + // In CSS2, before/after pseudo-content cannot nest. Check this first. + if (owner->style()->styleType() == BEFORE || owner->style()->styleType() == AFTER) + return; + + if (!styledObject) + styledObject = owner; + + RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type); + RenderObject* child = beforeAfterContainer(owner, type); + + // Whether or not we currently have generated content attached. + bool oldContentPresent = child; + + // Whether or not we now want generated content. + bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE; + + // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate + // :after content and not :before content. + if (newContentWanted && type == BEFORE && owner->isRenderInline() && toRenderInline(owner)->isInlineContinuation()) + newContentWanted = false; + + // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object, + // then we don't generate the :after content. + if (newContentWanted && type == AFTER && owner->isRenderInline() && toRenderInline(owner)->continuation()) + newContentWanted = false; + + // If we don't want generated content any longer, or if we have generated content, but it's no longer + // identical to the new content data we want to build render objects for, then we nuke all + // of the old generated content. + if (!newContentWanted || (oldContentPresent && Node::diff(child->style(), pseudoElementStyle) == Node::Detach)) { + // Nuke the child. + if (child && child->style()->styleType() == type) { + oldContentPresent = false; + child->destroy(); + child = (type == BEFORE) ? owner->virtualChildren()->firstChild() : owner->virtualChildren()->lastChild(); + } + } + + // If we have no pseudo-element style or if the pseudo-element style's display type is NONE, then we + // have no generated content and can now return. + if (!newContentWanted) + return; + + if (owner->isRenderInline() && !pseudoElementStyle->isDisplayInlineType() && pseudoElementStyle->floating() == FNONE && + !(pseudoElementStyle->position() == AbsolutePosition || pseudoElementStyle->position() == FixedPosition)) + // According to the CSS2 spec (the end of section 12.1), the only allowed + // display values for the pseudo style are NONE and INLINE for inline flows. + // FIXME: CSS2.1 lifted this restriction, but block display types will crash. + // For now we at least relax the restriction to allow all inline types like inline-block + // and inline-table. + pseudoElementStyle->setDisplay(INLINE); + + if (oldContentPresent) { + if (child && child->style()->styleType() == type) { + // We have generated content present still. We want to walk this content and update our + // style information with the new pseudo-element style. + child->setStyle(pseudoElementStyle); + + RenderObject* beforeAfterParent = findBeforeAfterParent(child); + if (!beforeAfterParent) + return; + + // Note that if we ever support additional types of generated content (which should be way off + // in the future), this code will need to be patched. + for (RenderObject* genChild = beforeAfterParent->firstChild(); genChild; genChild = genChild->nextSibling()) { + if (genChild->isText()) + // Generated text content is a child whose style also needs to be set to the pseudo-element style. + genChild->setStyle(pseudoElementStyle); + else if (genChild->isImage()) { + // Images get an empty style that inherits from the pseudo. + RefPtr<RenderStyle> style = RenderStyle::create(); + style->inheritFrom(pseudoElementStyle); + genChild->setStyle(style.release()); + } else + // Must be a first-letter container. updateFirstLetter() will take care of it. + ASSERT(genChild->style()->styleType() == FIRST_LETTER); + } + } + return; // We've updated the generated content. That's all we needed to do. + } + + RenderObject* insertBefore = (type == BEFORE) ? owner->virtualChildren()->firstChild() : 0; + + // Generated content consists of a single container that houses multiple children (specified + // by the content property). This generated content container gets the pseudo-element style set on it. + RenderObject* generatedContentContainer = 0; + + // Walk our list of generated content and create render objects for each. + for (const ContentData* content = pseudoElementStyle->contentData(); content; content = content->next()) { + RenderObject* renderer = 0; + switch (content->type()) { + case CONTENT_NONE: + break; + case CONTENT_TEXT: + renderer = new (owner->renderArena()) RenderTextFragment(owner->document() /* anonymous object */, content->text()); + renderer->setStyle(pseudoElementStyle); + break; + case CONTENT_OBJECT: { + RenderImageGeneratedContent* image = new (owner->renderArena()) RenderImageGeneratedContent(owner->document()); // anonymous object + RefPtr<RenderStyle> style = RenderStyle::create(); + style->inheritFrom(pseudoElementStyle); + image->setStyle(style.release()); + if (StyleImage* styleImage = content->image()) + image->setStyleImage(styleImage); + renderer = image; + break; + } + case CONTENT_COUNTER: + renderer = new (owner->renderArena()) RenderCounter(owner->document(), *content->counter()); + renderer->setStyle(pseudoElementStyle); + break; + } + + if (renderer) { + if (!generatedContentContainer) { + // Make a generated box that might be any display type now that we are able to drill down into children + // to find the original content properly. + generatedContentContainer = RenderObject::createObject(owner->document(), pseudoElementStyle); + generatedContentContainer->setStyle(pseudoElementStyle); + owner->addChild(generatedContentContainer, insertBefore); + } + generatedContentContainer->addChild(renderer); + } + } +} + +} // namespace WebCore diff --git a/WebCore/rendering/RenderObjectChildList.h b/WebCore/rendering/RenderObjectChildList.h new file mode 100644 index 0000000..bf8800a --- /dev/null +++ b/WebCore/rendering/RenderObjectChildList.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009 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 COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderObjectChildList_h +#define RenderObjectChildList_h + +#include "RenderStyleConstants.h" + +namespace WebCore { + +class RenderObject; + +class RenderObjectChildList { +public: + RenderObjectChildList() + : m_firstChild(0) + , m_lastChild(0) + { + } + + RenderObject* firstChild() const { return m_firstChild; } + RenderObject* lastChild() const { return m_lastChild; } + + // FIXME: Temporary while RenderBox still exists. Eventually this will just happen during insert/append/remove methods on the child list, and nobody + // will need to manipulate firstChild or lastChild directly. + void setFirstChild(RenderObject* child) { m_firstChild = child; } + void setLastChild(RenderObject* child) { m_lastChild = child; } + + void destroyLeftoverChildren(); + + RenderObject* removeChildNode(RenderObject* owner, RenderObject*, bool fullRemove = true); + void appendChildNode(RenderObject* owner, RenderObject*, bool fullAppend = true); + void insertChildNode(RenderObject* owner, RenderObject* child, RenderObject* before, bool fullInsert = true); + + void updateBeforeAfterContent(RenderObject* owner, PseudoId type, RenderObject* styledObject = 0); + void invalidateCounters(RenderObject* owner); + +private: + RenderObject* m_firstChild; + RenderObject* m_lastChild; +}; + +} // namespace WebCore + +#endif // RenderObjectChildList_h diff --git a/WebCore/rendering/RenderPart.cpp b/WebCore/rendering/RenderPart.cpp index 6f79a00..71e5f4e 100644 --- a/WebCore/rendering/RenderPart.cpp +++ b/WebCore/rendering/RenderPart.cpp @@ -37,7 +37,7 @@ namespace WebCore { using namespace HTMLNames; -RenderPart::RenderPart(HTMLFrameOwnerElement* node) +RenderPart::RenderPart(Element* node) : RenderWidget(node) { // init RenderObject attributes @@ -81,35 +81,4 @@ void RenderPart::deleteWidget() delete m_widget; } -// FIXME: This should not be necessary. Remove this once WebKit knows to properly schedule -// layouts using WebCore when objects resize. -void RenderPart::updateWidgetPosition() -{ - if (!m_widget) - return; - - FloatPoint absPos = localToAbsolute(); - absPos.move(borderLeft() + paddingLeft(), borderTop() + paddingTop()); - int w = width() - borderLeft() - borderRight() - paddingLeft() - paddingRight(); - int h = height() - borderTop() - borderBottom() - paddingTop() - paddingBottom(); - IntRect newBounds(absPos.x(), absPos.y(), w, h); - bool boundsChanged = newBounds != m_widget->frameRect(); - if (boundsChanged) { - // The widget changed positions. Update the frame geometry. - RenderArena *arena = ref(); - element()->ref(); - m_widget->setFrameRect(newBounds); - element()->deref(); - deref(arena); - } - - // if the frame bounds got changed, or if view needs layout (possibly indicating - // content size is wrong) we have to do a layout to set the right widget size - if (m_widget && m_widget->isFrameView()) { - FrameView* frameView = static_cast<FrameView*>(m_widget); - if (boundsChanged || frameView->needsLayout()) - frameView->layout(); - } -} - } diff --git a/WebCore/rendering/RenderPart.h b/WebCore/rendering/RenderPart.h index e339468..5a7ba26 100644 --- a/WebCore/rendering/RenderPart.h +++ b/WebCore/rendering/RenderPart.h @@ -34,7 +34,7 @@ class HTMLFrameOwnerElement; class RenderPart : public RenderWidget { public: - RenderPart(HTMLFrameOwnerElement*); + RenderPart(Element*); virtual ~RenderPart(); virtual bool isRenderPart() const { return true; } @@ -42,10 +42,6 @@ public: virtual void setWidget(Widget*); - // FIXME: This should not be necessary. - // Remove this once WebKit knows to properly schedule layouts using WebCore when objects resize. - virtual void updateWidgetPosition(); - bool hasFallbackContent() const { return m_hasFallbackContent; } virtual void viewCleared(); diff --git a/WebCore/rendering/RenderPartObject.cpp b/WebCore/rendering/RenderPartObject.cpp index 4df631f..828026e 100644 --- a/WebCore/rendering/RenderPartObject.cpp +++ b/WebCore/rendering/RenderPartObject.cpp @@ -34,21 +34,21 @@ #include "HTMLNames.h" #include "HTMLObjectElement.h" #include "HTMLParamElement.h" +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLMediaElement.h" +#include "HTMLVideoElement.h" +#endif #include "MIMETypeRegistry.h" #include "Page.h" #include "PluginData.h" #include "RenderView.h" #include "Text.h" -#ifdef FLATTEN_IFRAME -#include "RenderView.h" -#endif - namespace WebCore { using namespace HTMLNames; -RenderPartObject::RenderPartObject(HTMLFrameOwnerElement* element) +RenderPartObject::RenderPartObject(Element* element) : RenderPart(element) { // init RenderObject attributes @@ -132,10 +132,17 @@ static String serviceTypeForClassId(const String& classId, const PluginData* plu static inline bool shouldUseEmbedDescendant(HTMLObjectElement* objectElement, const PluginData* pluginData) { +#if PLATFORM(MAC) + UNUSED_PARAM(objectElement); + UNUSED_PARAM(pluginData); + // On Mac, we always want to use the embed descendant. + return true; +#else // If we have both an <object> and <embed>, we always want to use the <embed> except when we have // an ActiveX plug-in and plan to use it. return !(havePlugin(pluginData, activeXType()) && serviceTypeForClassId(objectElement->classId(), pluginData) == activeXType()); +#endif } void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins) @@ -146,8 +153,8 @@ void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins) Vector<String> paramValues; Frame* frame = m_view->frame(); - if (element()->hasTagName(objectTag)) { - HTMLObjectElement* o = static_cast<HTMLObjectElement*>(element()); + if (node()->hasTagName(objectTag)) { + HTMLObjectElement* o = static_cast<HTMLObjectElement*>(node()); o->setNeedWidgetUpdate(false); if (!o->isFinishedParsingChildren()) @@ -261,8 +268,8 @@ void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins) bool success = frame->loader()->requestObject(this, url, AtomicString(o->name()), serviceType, paramNames, paramValues); if (!success && m_hasFallbackContent) o->renderFallbackContent(); - } else if (element()->hasTagName(embedTag)) { - HTMLEmbedElement *o = static_cast<HTMLEmbedElement*>(element()); + } else if (node()->hasTagName(embedTag)) { + HTMLEmbedElement *o = static_cast<HTMLEmbedElement*>(node()); o->setNeedWidgetUpdate(false); url = o->url(); serviceType = o->serviceType(); @@ -294,6 +301,30 @@ void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins) 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"; + frame->loader()->requestObject(this, url, nullAtom, serviceType, paramNames, paramValues); + } +#endif } void RenderPartObject::layout() @@ -349,8 +380,8 @@ void RenderPartObject::layout() calcWidth(); calcHeight(); #endif - - adjustOverflowForBoxShadow(); + + adjustOverflowForBoxShadowAndReflect(); RenderPart::layout(); @@ -416,12 +447,12 @@ void RenderPartObject::calcHeight() { void RenderPartObject::viewCleared() { - if (element() && m_widget && m_widget->isFrameView()) { + if (node() && m_widget && m_widget->isFrameView()) { FrameView* view = static_cast<FrameView*>(m_widget); int marginw = -1; int marginh = -1; - if (element()->hasTagName(iframeTag)) { - HTMLIFrameElement* frame = static_cast<HTMLIFrameElement*>(element()); + if (node()->hasTagName(iframeTag)) { + HTMLIFrameElement* frame = static_cast<HTMLIFrameElement*>(node()); marginw = frame->getMarginWidth(); marginh = frame->getMarginHeight(); } diff --git a/WebCore/rendering/RenderPartObject.h b/WebCore/rendering/RenderPartObject.h index 2b21644..9eb9f21 100644 --- a/WebCore/rendering/RenderPartObject.h +++ b/WebCore/rendering/RenderPartObject.h @@ -31,7 +31,7 @@ namespace WebCore { class RenderPartObject : public RenderPart { public: - RenderPartObject(HTMLFrameOwnerElement*); + RenderPartObject(Element*); virtual ~RenderPartObject(); virtual const char* renderName() const { return "RenderPartObject"; } diff --git a/WebCore/rendering/RenderPath.cpp b/WebCore/rendering/RenderPath.cpp index 1f73c70..1340694 100644 --- a/WebCore/rendering/RenderPath.cpp +++ b/WebCore/rendering/RenderPath.cpp @@ -81,7 +81,7 @@ FloatPoint RenderPath::mapAbsolutePointToLocal(const FloatPoint& point) const // absolute transform? double localX; double localY; - absoluteTransform().inverse().map(point.x(), point.y(), &localX, &localY); + absoluteTransform().inverse().map(point.x(), point.y(), localX, localY); return FloatPoint::narrowPrecision(localX, localY); } @@ -150,33 +150,27 @@ const Path& RenderPath::path() const bool RenderPath::calculateLocalTransform() { TransformationMatrix oldTransform = m_localTransform; - m_localTransform = static_cast<SVGStyledTransformableElement*>(element())->animatedLocalTransform(); + m_localTransform = static_cast<SVGStyledTransformableElement*>(node())->animatedLocalTransform(); return (m_localTransform != oldTransform); } void RenderPath::layout() { - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout() && selfNeedsLayout(); - if (checkForRepaint) { - oldBounds = m_absoluteBounds; - oldOutlineBox = absoluteOutlineBounds(); - } - + // FIXME: using m_absoluteBounds breaks if containerForRepaint() is not the root + LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout(), &m_absoluteBounds); + calculateLocalTransform(); - setPath(static_cast<SVGStyledTransformableElement*>(element())->toPathData()); + setPath(static_cast<SVGStyledTransformableElement*>(node())->toPathData()); m_absoluteBounds = absoluteClippedOverflowRect(); - if (checkForRepaint) - repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + repainter.repaintAfterLayout(); setNeedsLayout(false); } -IntRect RenderPath::clippedOverflowRectForRepaint(RenderBox* /*repaintContainer*/) +IntRect RenderPath::clippedOverflowRectForRepaint(RenderBoxModelObject* /*repaintContainer*/) { // FIXME: handle non-root repaintContainer FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true)); @@ -243,7 +237,7 @@ void RenderPath::paint(PaintInfo& paintInfo, int, int) paintInfo.context->setShouldAntialias(false); fillAndStrokePath(m_path, paintInfo.context, style(), this); - if (static_cast<SVGStyledElement*>(element())->supportsMarkers()) + if (static_cast<SVGStyledElement*>(node())->supportsMarkers()) m_markerBounds = drawMarkersIfNeeded(paintInfo.context, paintInfo.rect, m_path); finishRenderSVGContent(this, paintInfo, boundingBox, filter, savedInfo.context); @@ -418,7 +412,7 @@ FloatRect RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatR { Document* doc = document(); - SVGElement* svgElement = static_cast<SVGElement*>(element()); + SVGElement* svgElement = static_cast<SVGElement*>(node()); ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); @@ -476,7 +470,7 @@ FloatRect RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatR return bounds; } -IntRect RenderPath::outlineBoundsForRepaint(RenderBox* /*repaintContainer*/) const +IntRect RenderPath::outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/) const { // FIXME: handle non-root repaintContainer IntRect result = m_absoluteBounds; diff --git a/WebCore/rendering/RenderPath.h b/WebCore/rendering/RenderPath.h index 81b5c52..6157171 100644 --- a/WebCore/rendering/RenderPath.h +++ b/WebCore/rendering/RenderPath.h @@ -59,7 +59,7 @@ public: virtual TransformationMatrix localTransform() const; virtual void layout(); - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual bool requiresLayer() const { return false; } virtual int lineHeight(bool b, bool isRootLineBox = false) const; virtual int baselinePosition(bool b, bool isRootLineBox = false) const; @@ -75,7 +75,7 @@ public: private: FloatPoint mapAbsolutePointToLocal(const FloatPoint&) const; - virtual IntRect outlineBoundsForRepaint(RenderBox* repaintContainer) const; + virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer) const; mutable Path m_path; mutable FloatRect m_fillBBox; diff --git a/WebCore/rendering/RenderReplaced.cpp b/WebCore/rendering/RenderReplaced.cpp index e74e227..783fc26 100644 --- a/WebCore/rendering/RenderReplaced.cpp +++ b/WebCore/rendering/RenderReplaced.cpp @@ -28,6 +28,7 @@ #include "RenderLayer.h" #include "RenderTheme.h" #include "RenderView.h" +#include "VisiblePosition.h" using namespace std; @@ -42,8 +43,6 @@ const int cDefaultHeight = 150; RenderReplaced::RenderReplaced(Node* node) : RenderBox(node) , m_intrinsicSize(cDefaultWidth, cDefaultHeight) - , m_selectionState(SelectionNone) - , m_hasOverflow(false) { setReplaced(true); } @@ -51,19 +50,17 @@ RenderReplaced::RenderReplaced(Node* node) RenderReplaced::RenderReplaced(Node* node, const IntSize& intrinsicSize) : RenderBox(node) , m_intrinsicSize(intrinsicSize) - , m_selectionState(SelectionNone) - , m_hasOverflow(false) { setReplaced(true); } RenderReplaced::~RenderReplaced() { - if (m_hasOverflow) + if (replacedHasOverflow()) gOverflowRectMap->remove(this); } -void RenderReplaced::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBox::styleDidChange(diff, oldStyle); @@ -77,23 +74,16 @@ void RenderReplaced::layout() { ASSERT(needsLayout()); - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout(); - if (checkForRepaint) { - oldBounds = absoluteClippedOverflowRect(); - oldOutlineBox = absoluteOutlineBounds(); - } + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); setHeight(minimumReplacedHeight()); calcWidth(); calcHeight(); - adjustOverflowForBoxShadow(); - - if (checkForRepaint) - repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + adjustOverflowForBoxShadowAndReflect(); + repainter.repaintAfterLayout(); + setNeedsLayout(false); } @@ -137,8 +127,24 @@ void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty) drawSelectionTint = false; } + bool clipToBorderRadius = style()->overflowX() != OVISIBLE && style()->hasBorderRadius(); + if (clipToBorderRadius) { + // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. + paintInfo.context->save(); + paintInfo.context->addRoundedRectClip(IntRect(tx, ty, width(), height()), + style()->borderTopLeftRadius(), + style()->borderTopRightRadius(), + style()->borderBottomLeftRadius(), + style()->borderBottomRightRadius()); + } + paintReplaced(paintInfo, tx, ty); - + + if (clipToBorderRadius) + paintInfo.context->restore(); + + // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of + // surrounding content. if (drawSelectionTint) { IntRect selectionPaintingRect = localSelectionRect(); selectionPaintingRect.move(tx, ty); @@ -215,11 +221,11 @@ unsigned RenderReplaced::caretMaxRenderedOffset() const return 1; } -VisiblePosition RenderReplaced::positionForCoordinates(int xPos, int yPos) +VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point) { InlineBox* box = inlineBoxWrapper(); if (!box) - return VisiblePosition(element(), 0, DOWNSTREAM); + return createVisiblePosition(0, DOWNSTREAM); // FIXME: This code is buggy if the replaced element is relative positioned. @@ -228,22 +234,22 @@ VisiblePosition RenderReplaced::positionForCoordinates(int xPos, int yPos) int top = root->topOverflow(); int bottom = root->nextRootBox() ? root->nextRootBox()->topOverflow() : root->bottomOverflow(); - if (yPos + y() < top) - return VisiblePosition(element(), caretMinOffset(), DOWNSTREAM); // coordinates are above + if (point.y() + y() < top) + return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above - if (yPos + y() >= bottom) - return VisiblePosition(element(), caretMaxOffset(), DOWNSTREAM); // coordinates are below + if (point.y() + y() >= bottom) + return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below - if (element()) { - if (xPos <= width() / 2) - return VisiblePosition(element(), 0, DOWNSTREAM); - return VisiblePosition(element(), 1, DOWNSTREAM); + if (node()) { + if (point.x() <= width() / 2) + return createVisiblePosition(0, DOWNSTREAM); + return createVisiblePosition(1, DOWNSTREAM); } - return RenderBox::positionForCoordinates(xPos, yPos); + return RenderBox::positionForPoint(point); } -IntRect RenderReplaced::selectionRect(bool clipToVisibleContent) +IntRect RenderReplaced::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent) { ASSERT(!needsLayout()); @@ -252,11 +258,9 @@ IntRect RenderReplaced::selectionRect(bool clipToVisibleContent) IntRect rect = localSelectionRect(); if (clipToVisibleContent) - computeAbsoluteRepaintRect(rect); - else { - FloatPoint absPos = localToAbsolute(FloatPoint()); - rect.move(absPos.x(), absPos.y()); - } + computeRectForRepaint(repaintContainer, rect); + else + rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox(); return rect; } @@ -280,7 +284,7 @@ IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const void RenderReplaced::setSelectionState(SelectionState s) { - m_selectionState = s; + RenderBox::setSelectionState(s); if (m_inlineBoxWrapper) { RootInlineBox* line = m_inlineBoxWrapper->root(); if (line) @@ -303,7 +307,7 @@ bool RenderReplaced::isSelected() const if (s == SelectionStart) return selectionStart == 0; - int end = element()->hasChildNodes() ? element()->childNodeCount() : 1; + int end = node()->hasChildNodes() ? node()->childNodeCount() : 1; if (s == SelectionEnd) return selectionEnd == end; if (s == SelectionBoth) @@ -323,7 +327,7 @@ void RenderReplaced::setIntrinsicSize(const IntSize& size) m_intrinsicSize = size; } -void RenderReplaced::adjustOverflowForBoxShadow() +void RenderReplaced::adjustOverflowForBoxShadowAndReflect() { IntRect overflow; for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) { @@ -333,21 +337,29 @@ void RenderReplaced::adjustOverflowForBoxShadow() overflow.unite(shadow); } + // Now that we have an overflow rect including shadow, let's make sure that + // the reflection (which can also include the shadow) is also included. + if (hasReflection()) { + if (overflow.isEmpty()) + overflow = borderBoxRect(); + overflow.unite(reflectedRect(overflow)); + } + if (!overflow.isEmpty()) { if (!gOverflowRectMap) gOverflowRectMap = new OverflowRectMap(); overflow.unite(borderBoxRect()); gOverflowRectMap->set(this, overflow); - m_hasOverflow = true; - } else if (m_hasOverflow) { + setReplacedHasOverflow(true); + } else if (replacedHasOverflow()) { gOverflowRectMap->remove(this); - m_hasOverflow = false; + setReplacedHasOverflow(false); } } int RenderReplaced::overflowHeight(bool) const { - if (m_hasOverflow) { + if (replacedHasOverflow()) { IntRect *r = &gOverflowRectMap->find(this)->second; return r->height() + r->y(); } @@ -357,7 +369,7 @@ int RenderReplaced::overflowHeight(bool) const int RenderReplaced::overflowWidth(bool) const { - if (m_hasOverflow) { + if (replacedHasOverflow()) { IntRect *r = &gOverflowRectMap->find(this)->second; return r->width() + r->x(); } @@ -367,7 +379,7 @@ int RenderReplaced::overflowWidth(bool) const int RenderReplaced::overflowLeft(bool) const { - if (m_hasOverflow) + if (replacedHasOverflow()) return gOverflowRectMap->get(this).x(); return 0; @@ -375,7 +387,7 @@ int RenderReplaced::overflowLeft(bool) const int RenderReplaced::overflowTop(bool) const { - if (m_hasOverflow) + if (replacedHasOverflow()) return gOverflowRectMap->get(this).y(); return 0; @@ -383,20 +395,20 @@ int RenderReplaced::overflowTop(bool) const IntRect RenderReplaced::overflowRect(bool) const { - if (m_hasOverflow) + if (replacedHasOverflow()) return gOverflowRectMap->find(this)->second; return borderBoxRect(); } -IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBox* repaintContainer) +IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) return IntRect(); - // The selectionRect can project outside of the overflowRect, so use - // that for repainting to avoid selection painting glitches - IntRect r = localSelectionRect(false); + // The selectionRect can project outside of the overflowRect, so take their union + // for repainting to avoid selection painting glitches. + IntRect r = unionRect(localSelectionRect(false), overflowRect(false)); RenderView* v = view(); if (v) { @@ -412,7 +424,7 @@ IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBox* repaintContaine if (v) r.inflate(style()->outlineSize()); } - computeRectForRepaint(r, repaintContainer); + computeRectForRepaint(repaintContainer, r); return r; } diff --git a/WebCore/rendering/RenderReplaced.h b/WebCore/rendering/RenderReplaced.h index c87db58..4937446 100644 --- a/WebCore/rendering/RenderReplaced.h +++ b/WebCore/rendering/RenderReplaced.h @@ -34,6 +34,8 @@ public: virtual const char* renderName() const { return "RenderReplaced"; } + virtual bool canHaveChildren() const { return false; } + virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; @@ -53,33 +55,29 @@ public: virtual int overflowTop(bool includeInterior = true) const; virtual IntRect overflowRect(bool includeInterior = true) const; - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual unsigned caretMaxRenderedOffset() const; - virtual VisiblePosition positionForCoordinates(int x, int y); + virtual VisiblePosition positionForPoint(const IntPoint&); virtual bool canBeSelectionLeaf() const { return true; } - virtual SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState); } virtual void setSelectionState(SelectionState); - virtual IntRect selectionRect(bool clipToVisibleContent = true); + virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent = true); bool isSelected() const; protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); void setIntrinsicSize(const IntSize&); virtual void intrinsicSizeChanged(); bool shouldPaint(PaintInfo&, int& tx, int& ty); - void adjustOverflowForBoxShadow(); + void adjustOverflowForBoxShadowAndReflect(); IntRect localSelectionRect(bool checkWhetherSelected = true) const; private: IntSize m_intrinsicSize; - - unsigned m_selectionState : 3; // SelectionState - bool m_hasOverflow : 1; }; } diff --git a/WebCore/rendering/RenderReplica.cpp b/WebCore/rendering/RenderReplica.cpp index 183dd2e..0eb9f8f 100644 --- a/WebCore/rendering/RenderReplica.cpp +++ b/WebCore/rendering/RenderReplica.cpp @@ -35,7 +35,13 @@ namespace WebCore { RenderReplica::RenderReplica(Node* n) : RenderBox(n) -{} +{ + // This is a hack. Replicas are synthetic, and don't pick up the attributes of the + // renderers being replicated, so they always report that they are inline, non-replaced. + // However, we need transforms to be applied to replicas for reflections, so have to pass + // the if (!isInline() || isReplaced()) check before setHasTransform(). + setReplaced(true); +} RenderReplica::~RenderReplica() {} diff --git a/WebCore/rendering/RenderSVGContainer.cpp b/WebCore/rendering/RenderSVGContainer.cpp index 6e17c05..28c5672 100644 --- a/WebCore/rendering/RenderSVGContainer.cpp +++ b/WebCore/rendering/RenderSVGContainer.cpp @@ -39,8 +39,6 @@ namespace WebCore { RenderSVGContainer::RenderSVGContainer(SVGStyledElement* node) : RenderObject(node) - , m_firstChild(0) - , m_lastChild(0) , m_width(0) , m_height(0) , m_drawsContents(true) @@ -51,143 +49,6 @@ RenderSVGContainer::~RenderSVGContainer() { } -bool RenderSVGContainer::canHaveChildren() const -{ - return true; -} - -void RenderSVGContainer::addChild(RenderObject* newChild, RenderObject* beforeChild) -{ - insertChildNode(newChild, beforeChild); -} - -void RenderSVGContainer::removeChild(RenderObject* oldChild) -{ - // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode - // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on - // layout anyway). - oldChild->removeFromObjectLists(); - - removeChildNode(oldChild); -} - -void RenderSVGContainer::destroy() -{ - destroyLeftoverChildren(); - RenderObject::destroy(); -} - -void RenderSVGContainer::destroyLeftoverChildren() -{ - while (m_firstChild) { - // Destroy any anonymous children remaining in the render tree, as well as implicit (shadow) DOM elements like those used in the engine-based text fields. - if (m_firstChild->element()) - m_firstChild->element()->setRenderer(0); - - m_firstChild->destroy(); - } -} - -RenderObject* RenderSVGContainer::removeChildNode(RenderObject* oldChild, bool fullRemove) -{ - ASSERT(oldChild->parent() == this); - - // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or - // that a positioned child got yanked). We also repaint, so that the area exposed when the child - // disappears gets repainted properly. - if (!documentBeingDestroyed() && fullRemove) { - oldChild->setNeedsLayoutAndPrefWidthsRecalc(); - oldChild->repaint(); - } - - // If we have a line box wrapper, delete it. - oldChild->deleteLineBoxWrapper(); - - if (!documentBeingDestroyed() && fullRemove) { - // If oldChild is the start or end of the selection, then clear the selection to - // avoid problems of invalid pointers. - // FIXME: The SelectionController should be responsible for this when it - // is notified of DOM mutations. - if (oldChild->isSelectionBorder()) - view()->clearSelection(); - } - - // remove the child - if (oldChild->previousSibling()) - oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); - if (oldChild->nextSibling()) - oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); - - if (m_firstChild == oldChild) - m_firstChild = oldChild->nextSibling(); - if (m_lastChild == oldChild) - m_lastChild = oldChild->previousSibling(); - - oldChild->setPreviousSibling(0); - oldChild->setNextSibling(0); - oldChild->setParent(0); - - if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->childrenChanged(this); - - return oldChild; -} - -void RenderSVGContainer::appendChildNode(RenderObject* newChild, bool) -{ - ASSERT(!newChild->parent()); - ASSERT(newChild->element()->isSVGElement()); - - newChild->setParent(this); - RenderObject* lChild = m_lastChild; - - if (lChild) { - newChild->setPreviousSibling(lChild); - lChild->setNextSibling(newChild); - } else - m_firstChild = newChild; - - m_lastChild = newChild; - - newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy. - if (!normalChildNeedsLayout()) - setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. - - if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->childrenChanged(this); -} - -void RenderSVGContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild, bool) -{ - if (!beforeChild) { - appendChildNode(child); - return; - } - - ASSERT(!child->parent()); - ASSERT(beforeChild->parent() == this); - ASSERT(child->element()->isSVGElement()); - - if (beforeChild == m_firstChild) - m_firstChild = child; - - RenderObject* prev = beforeChild->previousSibling(); - child->setNextSibling(beforeChild); - beforeChild->setPreviousSibling(child); - if (prev) - prev->setNextSibling(child); - child->setPreviousSibling(prev); - - child->setParent(this); - - child->setNeedsLayoutAndPrefWidthsRecalc(); - if (!normalChildNeedsLayout()) - setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. - - if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->childrenChanged(this); -} - bool RenderSVGContainer::drawsContents() const { return m_drawsContents; @@ -226,13 +87,8 @@ void RenderSVGContainer::layout() // Arbitrary affine transforms are incompatible with LayoutState. view()->disableLayoutState(); - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout() && selfWillPaint(); - if (checkForRepaint) { - oldBounds = m_absoluteBounds; - oldOutlineBox = absoluteOutlineBounds(); - } + // FIXME: using m_absoluteBounds breaks if containerForRepaint() is not the root + LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfWillPaint(), &m_absoluteBounds); calculateLocalTransform(); @@ -250,8 +106,7 @@ void RenderSVGContainer::layout() calcBounds(); - if (checkForRepaint) - repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + repainter.repaintAfterLayout(); view()->enableLayoutState(); setNeedsLayout(false); @@ -264,7 +119,7 @@ int RenderSVGContainer::calcReplacedWidth() const return max(0, style()->width().value()); case Percent: { - const int cw = containingBlockWidth(); + const int cw = containingBlock()->availableWidth(); return cw > 0 ? max(0, style()->width().calcMinValue(cw)) : 0; } default: @@ -357,7 +212,7 @@ TransformationMatrix RenderSVGContainer::viewportTransform() const return TransformationMatrix(); } -IntRect RenderSVGContainer::clippedOverflowRectForRepaint(RenderBox* repaintContainer) +IntRect RenderSVGContainer::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { FloatRect repaintRect; @@ -425,7 +280,7 @@ bool RenderSVGContainer::nodeAtPoint(const HitTestRequest& request, HitTestResul return false; } -IntRect RenderSVGContainer::outlineBoundsForRepaint(RenderBox* /*repaintContainer*/) const +IntRect RenderSVGContainer::outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/) const { // FIXME: handle non-root repaintContainer IntRect result = m_absoluteBounds; diff --git a/WebCore/rendering/RenderSVGContainer.h b/WebCore/rendering/RenderSVGContainer.h index e498a8a..2c1be65 100644 --- a/WebCore/rendering/RenderSVGContainer.h +++ b/WebCore/rendering/RenderSVGContainer.h @@ -37,29 +37,14 @@ public: RenderSVGContainer(SVGStyledElement*); ~RenderSVGContainer(); - virtual RenderObject* firstChild() const { return m_firstChild; } - virtual RenderObject* lastChild() const { return m_lastChild; } + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } int width() const { return m_width; } int height() const { return m_height; } - virtual bool canHaveChildren() const; - virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); - virtual void removeChild(RenderObject*); - - virtual void destroy(); - void destroyLeftoverChildren(); - - virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); - virtual void appendChildNode(RenderObject*, bool fullAppend = true); - virtual void insertChildNode(RenderObject* child, RenderObject* before, bool fullInsert = true); - - // Designed for speed. Don't waste time doing a bunch of work like layer updating and repainting when we know that our - // change in parentage is not going to affect anything. - virtual void moveChildNode(RenderObject* child) { appendChildNode(child->parent()->removeChildNode(child, false), false); } - - virtual void calcPrefWidths() { setPrefWidthsDirty(false); } - // Some containers do not want it's children // to be drawn, because they may be 'referenced' // Example: <marker> children in SVG @@ -76,7 +61,7 @@ public: virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel = true); virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); @@ -95,14 +80,13 @@ protected: void calcBounds(); - virtual IntRect outlineBoundsForRepaint(RenderBox* /*repaintContainer*/) const; + virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/) const; private: int calcReplacedWidth() const; int calcReplacedHeight() const; - RenderObject* m_firstChild; - RenderObject* m_lastChild; + RenderObjectChildList m_children; int m_width; int m_height; diff --git a/WebCore/rendering/RenderSVGGradientStop.cpp b/WebCore/rendering/RenderSVGGradientStop.cpp index d0dc881..b81e7f4 100644 --- a/WebCore/rendering/RenderSVGGradientStop.cpp +++ b/WebCore/rendering/RenderSVGGradientStop.cpp @@ -42,7 +42,7 @@ RenderSVGGradientStop::~RenderSVGGradientStop() { } -void RenderSVGGradientStop::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderSVGGradientStop::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderObject::styleDidChange(diff, oldStyle); @@ -61,7 +61,7 @@ void RenderSVGGradientStop::layout() SVGGradientElement* RenderSVGGradientStop::gradientElement() const { - Node* parentNode = element()->parent(); + Node* parentNode = node()->parent(); if (parentNode->hasTagName(linearGradientTag) || parentNode->hasTagName(radialGradientTag)) return static_cast<SVGGradientElement*>(parentNode); return 0; diff --git a/WebCore/rendering/RenderSVGGradientStop.h b/WebCore/rendering/RenderSVGGradientStop.h index 86de6d0..7b7df5c 100644 --- a/WebCore/rendering/RenderSVGGradientStop.h +++ b/WebCore/rendering/RenderSVGGradientStop.h @@ -41,14 +41,13 @@ namespace WebCore { virtual void layout(); - // This override is needed to prevent crashing on <svg><stop /></svg> - // RenderObject's default impl asks the parent Object and RenderSVGRoot - // asks all child RenderObjects for overflow rects, thus infinite loop. + // This override is needed to prevent an assert on <svg><stop /></svg> + // RenderObject's default impl asserts. // https://bugs.webkit.org/show_bug.cgi?id=20400 - virtual IntRect absoluteClippedOverflowRect() { return IntRect(); } + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject*) { return IntRect(); } protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); private: SVGGradientElement* gradientElement() const; diff --git a/WebCore/rendering/RenderSVGHiddenContainer.cpp b/WebCore/rendering/RenderSVGHiddenContainer.cpp index 23ae13e..dd76946 100644 --- a/WebCore/rendering/RenderSVGHiddenContainer.cpp +++ b/WebCore/rendering/RenderSVGHiddenContainer.cpp @@ -71,7 +71,7 @@ void RenderSVGHiddenContainer::paint(PaintInfo&, int, int) // This subtree does not paint. } -IntRect RenderSVGHiddenContainer::clippedOverflowRectForRepaint(RenderBox* /*repaintContainer*/) +IntRect RenderSVGHiddenContainer::clippedOverflowRectForRepaint(RenderBoxModelObject* /*repaintContainer*/) { return IntRect(); } diff --git a/WebCore/rendering/RenderSVGHiddenContainer.h b/WebCore/rendering/RenderSVGHiddenContainer.h index 282e3f3..6568f3f 100644 --- a/WebCore/rendering/RenderSVGHiddenContainer.h +++ b/WebCore/rendering/RenderSVGHiddenContainer.h @@ -51,7 +51,7 @@ namespace WebCore { virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel = true); virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); diff --git a/WebCore/rendering/RenderSVGImage.cpp b/WebCore/rendering/RenderSVGImage.cpp index 503663a..a13ee03 100644 --- a/WebCore/rendering/RenderSVGImage.cpp +++ b/WebCore/rendering/RenderSVGImage.cpp @@ -129,7 +129,7 @@ void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& s bool RenderSVGImage::calculateLocalTransform() { TransformationMatrix oldTransform = m_localTransform; - m_localTransform = static_cast<SVGStyledTransformableElement*>(element())->animatedLocalTransform(); + m_localTransform = static_cast<SVGStyledTransformableElement*>(node())->animatedLocalTransform(); return (m_localTransform != oldTransform); } @@ -137,13 +137,7 @@ void RenderSVGImage::layout() { ASSERT(needsLayout()); - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout(); - if (checkForRepaint) { - oldBounds = absoluteClippedOverflowRect(); - oldOutlineBox = absoluteOutlineBounds(); - } + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); calculateLocalTransform(); @@ -158,9 +152,8 @@ void RenderSVGImage::layout() calculateAbsoluteBounds(); - if (checkForRepaint) - repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); - + repainter.repaintAfterLayout(); + setNeedsLayout(false); } @@ -205,7 +198,7 @@ bool RenderSVGImage::nodeAtPoint(const HitTestRequest&, HitTestResult& result, i bool isVisible = (style()->visibility() == VISIBLE); if (isVisible || !hitRules.requireVisible) { double localX, localY; - absoluteTransform().inverse().map(_x, _y, &localX, &localY); + absoluteTransform().inverse().map(_x, _y, localX, localY); if (hitRules.canHitFill) { if (m_localBounds.contains(narrowPrecisionToFloat(localX), narrowPrecisionToFloat(localY))) { @@ -228,7 +221,7 @@ void RenderSVGImage::imageChanged(WrappedImagePtr image, const IntRect* rect) RenderImage::imageChanged(image, rect); // We override to invalidate a larger rect, since SVG images can draw outside their "bounds" - repaintRectangle(absoluteClippedOverflowRect()); + repaintRectangle(absoluteClippedOverflowRect()); // FIXME: Isn't this just repaint()? } void RenderSVGImage::calculateAbsoluteBounds() @@ -249,7 +242,7 @@ void RenderSVGImage::calculateAbsoluteBounds() m_absoluteBounds = enclosingIntRect(absoluteRect); } -IntRect RenderSVGImage::clippedOverflowRectForRepaint(RenderBox* /*repaintContainer*/) +IntRect RenderSVGImage::clippedOverflowRectForRepaint(RenderBoxModelObject* /*repaintContainer*/) { // FIXME: handle non-root repaintContainer return m_absoluteBounds; diff --git a/WebCore/rendering/RenderSVGImage.h b/WebCore/rendering/RenderSVGImage.h index cb440d2..4aedfef 100644 --- a/WebCore/rendering/RenderSVGImage.h +++ b/WebCore/rendering/RenderSVGImage.h @@ -43,7 +43,7 @@ namespace WebCore { virtual TransformationMatrix localTransform() const { return m_localTransform; } virtual FloatRect relativeBBox(bool includeStroke = true) const; - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); diff --git a/WebCore/rendering/RenderSVGInline.cpp b/WebCore/rendering/RenderSVGInline.cpp index 81e0924..a4016a1 100644 --- a/WebCore/rendering/RenderSVGInline.cpp +++ b/WebCore/rendering/RenderSVGInline.cpp @@ -36,28 +36,9 @@ RenderSVGInline::RenderSVGInline(Node* n) { } -InlineBox* RenderSVGInline::createInlineBox(bool unusedMakePlaceHolderBox, bool unusedIsRootLineBox, bool) +InlineFlowBox* RenderSVGInline::createFlowBox() { -#if ASSERT_DISABLED - UNUSED_PARAM(unusedIsRootLineBox); - UNUSED_PARAM(unusedMakePlaceHolderBox); -#endif - - ASSERT(!(!unusedIsRootLineBox && (isReplaced() || unusedMakePlaceHolderBox))); - - ASSERT(isRenderInline()); - - InlineFlowBox* flowBox = new (renderArena()) SVGInlineFlowBox(this); - - if (!m_firstLineBox) - m_firstLineBox = m_lastLineBox = flowBox; - else { - m_lastLineBox->setNextLineBox(flowBox); - flowBox->setPreviousLineBox(m_lastLineBox); - m_lastLineBox = flowBox; - } - - return flowBox; + return new (renderArena()) SVGInlineFlowBox(this); } } diff --git a/WebCore/rendering/RenderSVGInline.h b/WebCore/rendering/RenderSVGInline.h index 060ba58..cb8bf95 100644 --- a/WebCore/rendering/RenderSVGInline.h +++ b/WebCore/rendering/RenderSVGInline.h @@ -28,13 +28,17 @@ #include "RenderInline.h" namespace WebCore { + class RenderSVGInline : public RenderInline { public: - RenderSVGInline(Node*); - virtual const char* renderName() const { return "RenderSVGInline"; } - virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun = false); - virtual bool requiresLayer() const { return false; } - }; + RenderSVGInline(Node*); + virtual const char* renderName() const { return "RenderSVGInline"; } + virtual bool requiresLayer() const { return false; } + +private: + virtual InlineFlowBox* createFlowBox(); +}; + } #endif // ENABLE(SVG) diff --git a/WebCore/rendering/RenderSVGInlineText.cpp b/WebCore/rendering/RenderSVGInlineText.cpp index 215e9fe..b98eba0 100644 --- a/WebCore/rendering/RenderSVGInlineText.cpp +++ b/WebCore/rendering/RenderSVGInlineText.cpp @@ -56,7 +56,7 @@ RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> str) } -void RenderSVGInlineText::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderSVGInlineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { // Skip RenderText's work. RenderObject::styleDidChange(diff, oldStyle); @@ -68,25 +68,24 @@ void RenderSVGInlineText::styleDidChange(RenderStyle::Diff diff, const RenderSty void RenderSVGInlineText::absoluteRects(Vector<IntRect>& rects, int, int, bool) { - rects.append(computeAbsoluteRectForRange(0, textLength())); + rects.append(computeRepaintRectForRange(0, 0, textLength())); } void RenderSVGInlineText::absoluteQuads(Vector<FloatQuad>& quads, bool) { - quads.append(FloatRect(computeAbsoluteRectForRange(0, textLength()))); + quads.append(FloatRect(computeRepaintRectForRange(0, 0, textLength()))); } -IntRect RenderSVGInlineText::selectionRect(bool) +IntRect RenderSVGInlineText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool /*clipToVisibleContent*/) { ASSERT(!needsLayout()); - IntRect rect; if (selectionState() == SelectionNone) - return rect; + return IntRect(); // Early exit if we're ie. a <text> within a <defs> section. if (isChildOfHiddenContainer(this)) - return rect; + return IntRect(); // Now calculate startPos and endPos for painting selection. // We include a selection while endPos > 0 @@ -104,23 +103,22 @@ IntRect RenderSVGInlineText::selectionRect(bool) } if (startPos == endPos) - return rect; + return IntRect(); - return computeAbsoluteRectForRange(startPos, endPos); + return computeRepaintRectForRange(repaintContainer, startPos, endPos); } -IntRect RenderSVGInlineText::computeAbsoluteRectForRange(int startPos, int endPos) +IntRect RenderSVGInlineText::computeRepaintRectForRange(RenderBoxModelObject* /*repaintContainer*/, int startPos, int endPos) { - IntRect rect; - RenderBlock* cb = containingBlock(); if (!cb || !cb->container()) - return rect; + return IntRect(); RenderSVGRoot* root = findSVGRootObject(parent()); if (!root) - return rect; + return IntRect(); + IntRect rect; for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) rect.unite(box->selectionRect(0, 0, startPos, endPos)); @@ -129,15 +127,15 @@ IntRect RenderSVGInlineText::computeAbsoluteRectForRange(int startPos, int endPo // Remove HTML parent translation offsets here! These need to be retrieved from the RenderSVGRoot object. // But do take the containingBlocks's container position into account, ie. SVG text in scrollable <div>. - TransformationMatrix htmlParentCtm = root->RenderContainer::absoluteTransform(); + TransformationMatrix htmlParentCtm = root->RenderBox::absoluteTransform(); - FloatRect fixedRect(narrowPrecisionToFloat(rect.x() + absPos.x() - (firstTextBox() ? firstTextBox()->xPos() : 0) - htmlParentCtm.e()), - narrowPrecisionToFloat(rect.y() + absPos.y() - (firstTextBox() ? firstTextBox()->yPos() : 0) - htmlParentCtm.f()), rect.width(), rect.height()); - // FIXME: broken with CSS transforms + FloatRect fixedRect(narrowPrecisionToFloat(rect.x() + absPos.x() - htmlParentCtm.e()), + narrowPrecisionToFloat(rect.y() + absPos.y() - htmlParentCtm.f()), rect.width(), rect.height()); + // FIXME: broken with CSS transforms, and non-zero repaintContainer return enclosingIntRect(absoluteTransform().mapRect(fixedRect)); } -InlineTextBox* RenderSVGInlineText::createInlineTextBox() +InlineTextBox* RenderSVGInlineText::createTextBox() { return new (renderArena()) SVGInlineTextBox(this); } @@ -149,35 +147,41 @@ IntRect RenderSVGInlineText::localCaretRect(InlineBox*, int, int*) return IntRect(); } -VisiblePosition RenderSVGInlineText::positionForCoordinates(int x, int y) +VisiblePosition RenderSVGInlineText::positionForPoint(const IntPoint& point) { SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(firstTextBox()); if (!textBox || textLength() == 0) - return VisiblePosition(element(), 0, DOWNSTREAM); + return createVisiblePosition(0, DOWNSTREAM); SVGRootInlineBox* rootBox = textBox->svgRootInlineBox(); RenderBlock* object = rootBox ? rootBox->block() : 0; if (!object) - return VisiblePosition(element(), 0, DOWNSTREAM); + return createVisiblePosition(0, DOWNSTREAM); - int offset = 0; + int closestOffsetInBox = 0; + // FIXME: This approach is wrong. The correct code would first find the + // closest SVGInlineTextBox to the point, and *then* ask only that inline box + // what the closest text offset to that point is. This code instead walks + // through all boxes in order, so when you click "near" a box, you'll actually + // end up returning the nearest offset in the last box, even if the + // nearest offset to your click is contained in another box. for (SVGInlineTextBox* box = textBox; box; box = static_cast<SVGInlineTextBox*>(box->nextTextBox())) { - if (box->svgCharacterHitsPosition(x + object->x(), y + object->y(), offset)) { + if (box->svgCharacterHitsPosition(point.x() + object->x(), point.y() + object->y(), closestOffsetInBox)) { // If we're not at the end/start of the box, stop looking for other selected boxes. if (box->direction() == LTR) { - if (offset <= (int) box->end() + 1) + if (closestOffsetInBox <= (int) box->end() + 1) break; } else { - if (offset > (int) box->start()) + if (closestOffsetInBox > (int) box->start()) break; } } } - return VisiblePosition(element(), offset, DOWNSTREAM); + return createVisiblePosition(closestOffsetInBox, DOWNSTREAM); } void RenderSVGInlineText::destroy() diff --git a/WebCore/rendering/RenderSVGInlineText.h b/WebCore/rendering/RenderSVGInlineText.h index 55fd838..e7f776a 100644 --- a/WebCore/rendering/RenderSVGInlineText.h +++ b/WebCore/rendering/RenderSVGInlineText.h @@ -35,23 +35,23 @@ public: RenderSVGInlineText(Node*, PassRefPtr<StringImpl>); virtual const char* renderName() const { return "RenderSVGInlineText"; } - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle*); + virtual void styleDidChange(StyleDifference, const RenderStyle*); virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel = true); virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); virtual bool requiresLayer() const { return false; } - virtual IntRect selectionRect(bool clipToVisibleContent = true); + virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent = true); virtual bool isSVGText() const { return true; } - virtual InlineTextBox* createInlineTextBox(); virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); - virtual VisiblePosition positionForCoordinates(int x, int y); + virtual VisiblePosition positionForPoint(const IntPoint&); virtual void destroy(); private: - IntRect computeAbsoluteRectForRange(int startPos, int endPos); + virtual InlineTextBox* createTextBox(); + IntRect computeRepaintRectForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos); }; } diff --git a/WebCore/rendering/RenderSVGRoot.cpp b/WebCore/rendering/RenderSVGRoot.cpp index 54a30df..658b92d 100644 --- a/WebCore/rendering/RenderSVGRoot.cpp +++ b/WebCore/rendering/RenderSVGRoot.cpp @@ -27,24 +27,23 @@ #include "RenderSVGRoot.h" #include "GraphicsContext.h" -#include "RenderPath.h" #include "RenderSVGContainer.h" #include "RenderView.h" #include "SVGLength.h" #include "SVGRenderSupport.h" -#include "SVGResourceClipper.h" -#include "SVGResourceFilter.h" -#include "SVGResourceMasker.h" #include "SVGSVGElement.h" #include "SVGStyledElement.h" -#include "SVGURIReference.h" + +#if ENABLE(SVG_FILTERS) +#include "SVGResourceFilter.h" +#endif using namespace std; namespace WebCore { RenderSVGRoot::RenderSVGRoot(SVGStyledElement* node) - : RenderContainer(node) + : RenderBox(node) { setReplaced(true); } @@ -91,17 +90,14 @@ void RenderSVGRoot::layout() // Arbitrary affine transforms are incompatible with LayoutState. view()->disableLayoutState(); - IntRect oldBounds = m_absoluteBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout() && selfNeedsLayout(); - if (checkForRepaint) - oldOutlineBox = absoluteOutlineBounds(); + // FIXME: using m_absoluteBounds breaks if containerForRepaint() is not the root + LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout(), &m_absoluteBounds); calcWidth(); calcHeight(); m_absoluteBounds = absoluteClippedOverflowRect(); - SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); setWidth(static_cast<int>(width() * svg->currentScale())); setHeight(static_cast<int>(height() * svg->currentScale())); @@ -113,8 +109,7 @@ void RenderSVGRoot::layout() ASSERT(!child->needsLayout()); } - if (checkForRepaint) - repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + repainter.repaintAfterLayout(); view()->enableLayoutState(); setNeedsLayout(false); @@ -135,10 +130,10 @@ void RenderSVGRoot::applyContentTransforms(PaintInfo& paintInfo, int parentX, in } // Respect scroll offset caused by html parents - TransformationMatrix ctm = RenderContainer::absoluteTransform(); + TransformationMatrix ctm = RenderBox::absoluteTransform(); paintInfo.rect.move(static_cast<int>(ctm.e()), static_cast<int>(ctm.f())); - SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); paintInfo.context->concatCTM(TransformationMatrix().scale(svg->currentScale())); if (!viewport().isEmpty()) { @@ -158,7 +153,6 @@ void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) calcViewport(); - SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); // A value of zero disables rendering of the element. if (viewport().width() <= 0. || viewport().height() <= 0.) return; @@ -186,16 +180,17 @@ void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) FloatRect boundingBox = relativeBBox(true); if (childPaintInfo.phase == PaintPhaseForeground) - prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); + prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); + SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); childPaintInfo.context->concatCTM(svg->viewBoxToViewTransform(width(), height())); - RenderContainer::paint(childPaintInfo, 0, 0); + RenderBox::paint(childPaintInfo, 0, 0); if (childPaintInfo.phase == PaintPhaseForeground) finishRenderSVGContent(this, childPaintInfo, boundingBox, filter, paintInfo.context); childPaintInfo.context->restore(); - + if ((childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE) paintOutline(childPaintInfo.context, m_absoluteBounds.x(), m_absoluteBounds.y(), m_absoluteBounds.width(), m_absoluteBounds.height(), style()); } @@ -207,31 +202,24 @@ FloatRect RenderSVGRoot::viewport() const void RenderSVGRoot::calcViewport() { - SVGElement* svgelem = static_cast<SVGElement*>(element()); - if (svgelem->hasTagName(SVGNames::svgTag)) { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); - if (!selfNeedsLayout() && !svg->hasRelativeValues()) - return; - - float w, h; - SVGLength width = svg->width(); - if (width.unitType() == LengthTypePercentage && svg->hasSetContainerSize()) - w = svg->relativeWidthValue(); - else - w = width.value(svg); - - SVGLength height = svg->height(); - if (height.unitType() == LengthTypePercentage && svg->hasSetContainerSize()) - h = svg->relativeHeightValue(); - else - h = height.value(svg); + if (!selfNeedsLayout() && !svg->hasRelativeValues()) + return; - m_viewport = FloatRect(0, 0, w, h); + SVGLength width = svg->width(); + SVGLength height = svg->height(); + if (!svg->hasSetContainerSize()) { + m_viewport = FloatRect(0, 0, width.value(svg), height.value(svg)); + return; } + m_viewport = FloatRect(0, 0, (width.unitType() == LengthTypePercentage) ? + svg->relativeWidthValue() : width.value(svg), + (height.unitType() == LengthTypePercentage) ? + svg->relativeHeightValue() : height.value(svg)); } -IntRect RenderSVGRoot::clippedOverflowRectForRepaint(RenderBox* repaintContainer) +IntRect RenderSVGRoot::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { IntRect repaintRect; @@ -267,9 +255,9 @@ void RenderSVGRoot::absoluteQuads(Vector<FloatQuad>& quads, bool) TransformationMatrix RenderSVGRoot::absoluteTransform() const { - TransformationMatrix ctm = RenderContainer::absoluteTransform(); + TransformationMatrix ctm = RenderBox::absoluteTransform(); ctm.translate(x(), y()); - SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); ctm.scale(svg->currentScale()); ctm.translate(svg->currentTranslate().x(), svg->currentTranslate().y()); ctm.translate(viewport().x(), viewport().y()); @@ -300,7 +288,7 @@ TransformationMatrix RenderSVGRoot::localTransform() const bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) { - TransformationMatrix ctm = RenderContainer::absoluteTransform(); + TransformationMatrix ctm = RenderBox::absoluteTransform(); int sx = (_tx - static_cast<int>(ctm.e())); // scroll offset int sy = (_ty - static_cast<int>(ctm.f())); // scroll offset @@ -316,7 +304,7 @@ bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& re overflowBox.move(tx, ty); ctm.translate(viewport().x(), viewport().y()); double localX, localY; - ctm.inverse().map(_x - _tx, _y - _ty, &localX, &localY); + ctm.inverse().map(_x - _tx, _y - _ty, localX, localY); if (!overflowBox.contains((int)localX, (int)localY)) return false; } @@ -333,13 +321,6 @@ bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& re return false; } -void RenderSVGRoot::position(InlineBox* box) -{ - RenderContainer::position(box); - if (m_absoluteBounds.isEmpty()) - setNeedsLayout(true, false); -} - } #endif // ENABLE(SVG) diff --git a/WebCore/rendering/RenderSVGRoot.h b/WebCore/rendering/RenderSVGRoot.h index 048fc8b..3d0a141 100644 --- a/WebCore/rendering/RenderSVGRoot.h +++ b/WebCore/rendering/RenderSVGRoot.h @@ -24,7 +24,7 @@ #define RenderSVGRoot_h #if ENABLE(SVG) -#include "RenderContainer.h" +#include "RenderBox.h" #include "FloatRect.h" namespace WebCore { @@ -32,11 +32,16 @@ namespace WebCore { class SVGStyledElement; class TransformationMatrix; -class RenderSVGRoot : public RenderContainer { +class RenderSVGRoot : public RenderBox { public: RenderSVGRoot(SVGStyledElement*); ~RenderSVGRoot(); + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + virtual bool isSVGRoot() const { return true; } virtual const char* renderName() const { return "RenderSVGRoot"; } @@ -47,7 +52,7 @@ public: virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); @@ -63,13 +68,12 @@ public: FloatRect viewport() const; virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); - - virtual void position(InlineBox*); private: void calcViewport(); void applyContentTransforms(PaintInfo&, int parentX, int parentY); + RenderObjectChildList m_children; FloatRect m_viewport; IntRect m_absoluteBounds; }; diff --git a/WebCore/rendering/RenderSVGTSpan.cpp b/WebCore/rendering/RenderSVGTSpan.cpp index 49c45df..d493c45 100644 --- a/WebCore/rendering/RenderSVGTSpan.cpp +++ b/WebCore/rendering/RenderSVGTSpan.cpp @@ -48,11 +48,11 @@ void RenderSVGTSpan::absoluteRects(Vector<IntRect>& rects, int, int, bool) if (!object) return; - int xRef = object->x() + x(); - int yRef = object->y() + y(); + int xRef = object->x(); + int yRef = object->y(); for (InlineRunBox* curr = firstBox; curr; curr = curr->nextLineBox()) { - FloatRect rect(xRef + curr->xPos(), yRef + curr->yPos(), curr->width(), curr->height()); + FloatRect rect(xRef + curr->x(), yRef + curr->y(), curr->width(), curr->height()); // FIXME: broken with CSS transforms rects.append(enclosingIntRect(absoluteTransform().mapRect(rect))); } @@ -68,11 +68,11 @@ void RenderSVGTSpan::absoluteQuads(Vector<FloatQuad>& quads, bool) if (!object) return; - int xRef = object->x() + x(); - int yRef = object->y() + y(); + int xRef = object->x(); + int yRef = object->y(); for (InlineRunBox* curr = firstBox; curr; curr = curr->nextLineBox()) { - FloatRect rect(xRef + curr->xPos(), yRef + curr->yPos(), curr->width(), curr->height()); + FloatRect rect(xRef + curr->x(), yRef + curr->y(), curr->width(), curr->height()); // FIXME: broken with CSS transforms quads.append(absoluteTransform().mapRect(rect)); } diff --git a/WebCore/rendering/RenderSVGText.cpp b/WebCore/rendering/RenderSVGText.cpp index ee5ab34..8fef1f3 100644 --- a/WebCore/rendering/RenderSVGText.cpp +++ b/WebCore/rendering/RenderSVGText.cpp @@ -49,7 +49,7 @@ RenderSVGText::RenderSVGText(SVGTextElement* node) { } -IntRect RenderSVGText::clippedOverflowRectForRepaint(RenderBox* /*repaintContainer*/) +IntRect RenderSVGText::clippedOverflowRectForRepaint(RenderBoxModelObject* /*repaintContainer*/) { // FIXME: handle non-root repaintContainer FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true)); @@ -70,7 +70,7 @@ IntRect RenderSVGText::clippedOverflowRectForRepaint(RenderBox* /*repaintContain bool RenderSVGText::calculateLocalTransform() { TransformationMatrix oldTransform = m_localTransform; - m_localTransform = static_cast<SVGTextElement*>(element())->animatedLocalTransform(); + m_localTransform = static_cast<SVGTextElement*>(node())->animatedLocalTransform(); return (oldTransform != m_localTransform); } @@ -81,16 +81,11 @@ void RenderSVGText::layout() // FIXME: This is a hack to avoid the RenderBlock::layout() partial repainting code which is not (yet) SVG aware setNeedsLayout(true); - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout(); - if (checkForRepaint) { - oldBounds = m_absoluteBounds; - oldOutlineBox = absoluteOutlineBounds(); - } + // FIXME: using m_absoluteBounds breaks if containerForRepaint() is not the root + LayoutRepainter repainter(*this, checkForRepaintDuringLayout(), &m_absoluteBounds); // Best guess for a relative starting point - SVGTextElement* text = static_cast<SVGTextElement*>(element()); + SVGTextElement* text = static_cast<SVGTextElement*>(node()); int xOffset = (int)(text->x()->getFirst().value(text)); int yOffset = (int)(text->y()->getFirst().value(text)); setLocation(xOffset, yOffset); @@ -101,27 +96,14 @@ void RenderSVGText::layout() m_absoluteBounds = absoluteClippedOverflowRect(); - bool repainted = false; - if (checkForRepaint) - repainted = repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); - + repainter.repaintAfterLayout(); + setNeedsLayout(false); } -InlineBox* RenderSVGText::createInlineBox(bool, bool, bool) +RootInlineBox* RenderSVGText::createRootBox() { - ASSERT(!isRenderInline()); - InlineFlowBox* flowBox = new (renderArena()) SVGRootInlineBox(this); - - if (!m_firstLineBox) - m_firstLineBox = m_lastLineBox = flowBox; - else { - m_lastLineBox->setNextLineBox(flowBox); - flowBox->setPreviousLineBox(m_lastLineBox); - m_lastLineBox = flowBox; - } - - return flowBox; + return new (renderArena()) SVGRootInlineBox(this); } bool RenderSVGText::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) @@ -133,7 +115,7 @@ bool RenderSVGText::nodeAtPoint(const HitTestRequest& request, HitTestResult& re || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill))) { TransformationMatrix totalTransform = absoluteTransform(); double localX, localY; - totalTransform.inverse().map(_x, _y, &localX, &localY); + totalTransform.inverse().map(_x, _y, localX, localY); FloatPoint hitPoint(_x, _y); return RenderBlock::nodeAtPoint(request, result, (int)localX, (int)localY, _tx, _ty, hitTestAction); } @@ -150,7 +132,7 @@ void RenderSVGText::absoluteRects(Vector<IntRect>& rects, int, int, bool) FloatPoint absPos = localToAbsolute(); - TransformationMatrix htmlParentCtm = root->RenderContainer::absoluteTransform(); + TransformationMatrix htmlParentCtm = root->RenderBox::absoluteTransform(); // Don't use relativeBBox here, as it's unites the selection rects. Makes it hard // to spot errors, if there are any using WebInspector. Individually feed them into 'rects'. @@ -159,7 +141,7 @@ void RenderSVGText::absoluteRects(Vector<IntRect>& rects, int, int, bool) InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(runBox); for (InlineBox* box = flowBox->firstChild(); box; box = box->nextOnLine()) { - FloatRect boxRect(box->xPos(), box->yPos(), box->width(), box->height()); + FloatRect boxRect(box->x(), box->y(), box->width(), box->height()); boxRect.move(narrowPrecisionToFloat(absPos.x() - htmlParentCtm.e()), narrowPrecisionToFloat(absPos.y() - htmlParentCtm.f())); // FIXME: broken with CSS transforms rects.append(enclosingIntRect(absoluteTransform().mapRect(boxRect))); @@ -175,7 +157,7 @@ void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads, bool) FloatPoint absPos = localToAbsolute(); - TransformationMatrix htmlParentCtm = root->RenderContainer::absoluteTransform(); + TransformationMatrix htmlParentCtm = root->RenderBox::absoluteTransform(); // Don't use relativeBBox here, as it's unites the selection rects. Makes it hard // to spot errors, if there are any using WebInspector. Individually feed them into 'rects'. @@ -184,7 +166,7 @@ void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads, bool) InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(runBox); for (InlineBox* box = flowBox->firstChild(); box; box = box->nextOnLine()) { - FloatRect boxRect(box->xPos(), box->yPos(), box->width(), box->height()); + FloatRect boxRect(box->x(), box->y(), box->width(), box->height()); boxRect.move(narrowPrecisionToFloat(absPos.x() - htmlParentCtm.e()), narrowPrecisionToFloat(absPos.y() - htmlParentCtm.f())); // FIXME: broken with CSS transforms quads.append(absoluteTransform().mapRect(boxRect)); @@ -208,7 +190,7 @@ FloatRect RenderSVGText::relativeBBox(bool includeStroke) const InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(runBox); for (InlineBox* box = flowBox->firstChild(); box; box = box->nextOnLine()) - repaintRect.unite(FloatRect(box->xPos(), box->yPos(), box->width(), box->height())); + repaintRect.unite(FloatRect(box->x(), box->y(), box->width(), box->height())); } // SVG needs to include the strokeWidth(), not the textStrokeWidth(). diff --git a/WebCore/rendering/RenderSVGText.h b/WebCore/rendering/RenderSVGText.h index 4592f4e..2cd71fa 100644 --- a/WebCore/rendering/RenderSVGText.h +++ b/WebCore/rendering/RenderSVGText.h @@ -53,12 +53,12 @@ public: virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual FloatRect relativeBBox(bool includeStroke = true) const; - virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun = false); - private: + virtual RootInlineBox* createRootBox(); + TransformationMatrix m_localTransform; IntRect m_absoluteBounds; }; diff --git a/WebCore/rendering/RenderSVGTextPath.cpp b/WebCore/rendering/RenderSVGTextPath.cpp index 2d2894f..4ae29bf 100644 --- a/WebCore/rendering/RenderSVGTextPath.cpp +++ b/WebCore/rendering/RenderSVGTextPath.cpp @@ -45,7 +45,7 @@ RenderSVGTextPath::RenderSVGTextPath(Node* n) Path RenderSVGTextPath::layoutPath() const { - SVGTextPathElement* textPathElement = static_cast<SVGTextPathElement*>(element()); + SVGTextPathElement* textPathElement = static_cast<SVGTextPathElement*>(node()); String pathId = SVGURIReference::getTarget(textPathElement->href()); Element* targetElement = textPathElement->document()->getElementById(pathId); if (!targetElement || !targetElement->hasTagName(SVGNames::pathTag)) @@ -65,17 +65,17 @@ Path RenderSVGTextPath::layoutPath() const float RenderSVGTextPath::startOffset() const { - return static_cast<SVGTextPathElement*>(element())->startOffset().valueAsPercentage(); + return static_cast<SVGTextPathElement*>(node())->startOffset().valueAsPercentage(); } bool RenderSVGTextPath::exactAlignment() const { - return static_cast<SVGTextPathElement*>(element())->spacing() == SVG_TEXTPATH_SPACINGTYPE_EXACT; + return static_cast<SVGTextPathElement*>(node())->spacing() == SVG_TEXTPATH_SPACINGTYPE_EXACT; } bool RenderSVGTextPath::stretchMethod() const { - return static_cast<SVGTextPathElement*>(element())->method() == SVG_TEXTPATH_METHODTYPE_STRETCH; + return static_cast<SVGTextPathElement*>(node())->method() == SVG_TEXTPATH_METHODTYPE_STRETCH; } void RenderSVGTextPath::absoluteRects(Vector<IntRect>& rects, int, int) @@ -88,11 +88,11 @@ void RenderSVGTextPath::absoluteRects(Vector<IntRect>& rects, int, int) if (!object) return; - int xRef = object->x() + x(); - int yRef = object->y() + y(); + int xRef = object->x(); + int yRef = object->y(); for (InlineRunBox* curr = firstBox; curr; curr = curr->nextLineBox()) { - FloatRect rect(xRef + curr->xPos(), yRef + curr->yPos(), curr->width(), curr->height()); + FloatRect rect(xRef + curr->x(), yRef + curr->y(), curr->width(), curr->height()); // FIXME: broken with CSS transforms rects.append(enclosingIntRect(absoluteTransform().mapRect(rect))); } @@ -108,11 +108,11 @@ void RenderSVGTextPath::absoluteQuads(Vector<FloatQuad>& quads, bool) if (!object) return; - int xRef = object->x() + x(); - int yRef = object->y() + y(); + int xRef = object->x(); + int yRef = object->y(); for (InlineRunBox* curr = firstBox; curr; curr = curr->nextLineBox()) { - FloatRect rect(xRef + curr->xPos(), yRef + curr->yPos(), curr->width(), curr->height()); + FloatRect rect(xRef + curr->x(), yRef + curr->y(), curr->width(), curr->height()); // FIXME: broken with CSS transforms quads.append(absoluteTransform().mapRect(rect)); } diff --git a/WebCore/rendering/RenderSVGTransformableContainer.cpp b/WebCore/rendering/RenderSVGTransformableContainer.cpp index 17d64f3..6d65b55 100644 --- a/WebCore/rendering/RenderSVGTransformableContainer.cpp +++ b/WebCore/rendering/RenderSVGTransformableContainer.cpp @@ -38,7 +38,7 @@ RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGStyledTransf bool RenderSVGTransformableContainer::calculateLocalTransform() { TransformationMatrix oldTransform = m_localTransform; - m_localTransform = static_cast<SVGStyledTransformableElement*>(element())->animatedLocalTransform(); + m_localTransform = static_cast<SVGStyledTransformableElement*>(node())->animatedLocalTransform(); return (m_localTransform != oldTransform); } diff --git a/WebCore/rendering/RenderSVGViewportContainer.cpp b/WebCore/rendering/RenderSVGViewportContainer.cpp index 4282efc..7885c4c 100644 --- a/WebCore/rendering/RenderSVGViewportContainer.cpp +++ b/WebCore/rendering/RenderSVGViewportContainer.cpp @@ -52,12 +52,9 @@ void RenderSVGViewportContainer::layout() // Arbitrary affine transforms are incompatible with LayoutState. view()->disableLayoutState(); - IntRect oldBounds = m_absoluteBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout() && selfNeedsLayout(); - if (checkForRepaint) - oldOutlineBox = absoluteOutlineBounds(); - + // FIXME: using m_absoluteBounds breaks if containerForRepaint() is not the root + LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout(), &m_absoluteBounds); + calcBounds(); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { @@ -68,8 +65,7 @@ void RenderSVGViewportContainer::layout() ASSERT(!child->needsLayout()); } - if (checkForRepaint) - repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + repainter.repaintAfterLayout(); view()->enableLayoutState(); setNeedsLayout(false); @@ -109,9 +105,9 @@ FloatRect RenderSVGViewportContainer::viewport() const void RenderSVGViewportContainer::calcViewport() { - SVGElement* svgelem = static_cast<SVGElement*>(element()); + SVGElement* svgelem = static_cast<SVGElement*>(node()); if (svgelem->hasTagName(SVGNames::svgTag)) { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); if (!selfNeedsLayout() && !svg->hasRelativeValues()) return; @@ -125,7 +121,7 @@ void RenderSVGViewportContainer::calcViewport() if (!selfNeedsLayout()) return; - SVGMarkerElement* svg = static_cast<SVGMarkerElement*>(element()); + SVGMarkerElement* svg = static_cast<SVGMarkerElement*>(node()); float w = svg->markerWidth().value(svg); float h = svg->markerHeight().value(svg); m_viewport = FloatRect(0, 0, w, h); @@ -134,11 +130,11 @@ void RenderSVGViewportContainer::calcViewport() TransformationMatrix RenderSVGViewportContainer::viewportTransform() const { - if (element()->hasTagName(SVGNames::svgTag)) { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + if (node()->hasTagName(SVGNames::svgTag)) { + SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); return svg->viewBoxToViewTransform(viewport().width(), viewport().height()); - } else if (element()->hasTagName(SVGNames::markerTag)) { - SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(element()); + } else if (node()->hasTagName(SVGNames::markerTag)) { + SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node()); return marker->viewBoxToViewTransform(viewport().width(), viewport().height()); } @@ -163,7 +159,7 @@ bool RenderSVGViewportContainer::nodeAtPoint(const HitTestRequest& request, HitT TransformationMatrix ctm = RenderObject::absoluteTransform(); ctm.translate(viewport().x(), viewport().y()); double localX, localY; - ctm.inverse().map(_x - _tx, _y - _ty, &localX, &localY); + ctm.inverse().map(_x - _tx, _y - _ty, localX, localY); if (!overflowBox.contains((int)localX, (int)localY)) return false; } @@ -173,7 +169,7 @@ bool RenderSVGViewportContainer::nodeAtPoint(const HitTestRequest& request, HitT // Respect parent translation offset for non-outermost <svg> elements. // Outermost <svg> element is handled by RenderSVGRoot. - if (element()->hasTagName(SVGNames::svgTag)) { + if (node()->hasTagName(SVGNames::svgTag)) { sx = _tx; sy = _ty; } diff --git a/WebCore/rendering/RenderScrollbar.cpp b/WebCore/rendering/RenderScrollbar.cpp index d6dd9cc..db24a06 100644 --- a/WebCore/rendering/RenderScrollbar.cpp +++ b/WebCore/rendering/RenderScrollbar.cpp @@ -117,7 +117,7 @@ ScrollbarPart RenderScrollbar::partForStyleResolve() return s_styleResolvePart; } -PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, RenderStyle::PseudoId pseudoId) +PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId) { s_styleResolvePart = partType; s_styleResolveScrollbar = this; @@ -158,23 +158,23 @@ void RenderScrollbar::updateScrollbarParts(bool destroy) } } -static RenderStyle::PseudoId pseudoForScrollbarPart(ScrollbarPart part) +static PseudoId pseudoForScrollbarPart(ScrollbarPart part) { switch (part) { case BackButtonStartPart: case ForwardButtonStartPart: case BackButtonEndPart: case ForwardButtonEndPart: - return RenderStyle::SCROLLBAR_BUTTON; + return SCROLLBAR_BUTTON; case BackTrackPart: case ForwardTrackPart: - return RenderStyle::SCROLLBAR_TRACK_PIECE; + return SCROLLBAR_TRACK_PIECE; case ThumbPart: - return RenderStyle::SCROLLBAR_THUMB; + return SCROLLBAR_THUMB; case TrackBGPart: - return RenderStyle::SCROLLBAR_TRACK; + return SCROLLBAR_TRACK; default: - return RenderStyle::SCROLLBAR; + return SCROLLBAR; } } diff --git a/WebCore/rendering/RenderScrollbar.h b/WebCore/rendering/RenderScrollbar.h index cc43a00..524c4e8 100644 --- a/WebCore/rendering/RenderScrollbar.h +++ b/WebCore/rendering/RenderScrollbar.h @@ -26,13 +26,14 @@ #ifndef RenderScrollbar_h #define RenderScrollbar_h +#include "RenderStyleConstants.h" #include "Scrollbar.h" -#include "RenderStyle.h" #include <wtf/HashMap.h> namespace WebCore { class RenderBox; +class RenderStyle; class RenderScrollbarPart; class RenderStyle; @@ -71,7 +72,7 @@ public: int minimumThumbLength(); private: - PassRefPtr<RenderStyle> getScrollbarPseudoStyle(ScrollbarPart, RenderStyle::PseudoId); + PassRefPtr<RenderStyle> getScrollbarPseudoStyle(ScrollbarPart, PseudoId); void updateScrollbarPart(ScrollbarPart, bool destroy = false); RenderBox* m_owner; diff --git a/WebCore/rendering/RenderScrollbarPart.cpp b/WebCore/rendering/RenderScrollbarPart.cpp index 6749d8c..0f29aeb 100644 --- a/WebCore/rendering/RenderScrollbarPart.cpp +++ b/WebCore/rendering/RenderScrollbarPart.cpp @@ -122,20 +122,20 @@ void RenderScrollbarPart::calcPrefWidths() setPrefWidthsDirty(false); } -void RenderScrollbarPart::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +void RenderScrollbarPart::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { RenderBlock::styleWillChange(diff, newStyle); setInline(false); } -void RenderScrollbarPart::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderScrollbarPart::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); setInline(false); setPositioned(false); setFloating(false); setHasOverflowClip(false); - if (oldStyle && m_scrollbar && m_part != NoPart && diff >= RenderStyle::Repaint) + if (oldStyle && m_scrollbar && m_part != NoPart && diff >= StyleDifferenceRepaint) m_scrollbar->theme()->invalidatePart(m_scrollbar, m_part); } diff --git a/WebCore/rendering/RenderScrollbarPart.h b/WebCore/rendering/RenderScrollbarPart.h index 7dae6e7..114bbff 100644 --- a/WebCore/rendering/RenderScrollbarPart.h +++ b/WebCore/rendering/RenderScrollbarPart.h @@ -48,8 +48,8 @@ public: void paintIntoRect(GraphicsContext*, int tx, int ty, const IntRect&); protected: - virtual void styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle); - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleWillChange(StyleDifference diff, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); private: diff --git a/WebCore/rendering/RenderSelectionInfo.h b/WebCore/rendering/RenderSelectionInfo.h new file mode 100644 index 0000000..e7b7b78 --- /dev/null +++ b/WebCore/rendering/RenderSelectionInfo.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 SelectionInfo_h +#define SelectionInfo_h + +#include "IntRect.h" +#include "RenderBox.h" + +namespace WebCore { + +class RenderSelectionInfoBase { +public: + RenderSelectionInfoBase() + : m_object(0) + , m_repaintContainer(0) + , m_state(RenderObject::SelectionNone) + { + } + + RenderSelectionInfoBase(RenderObject* o) + : m_object(o) + , m_repaintContainer(o->containerForRepaint()) + , m_state(o->selectionState()) + { + } + + RenderObject* object() const { return m_object; } + RenderBoxModelObject* repaintContainer() const { return m_repaintContainer; } + RenderObject::SelectionState state() const { return m_state; } + +protected: + RenderObject* m_object; + RenderBoxModelObject* m_repaintContainer; + RenderObject::SelectionState m_state; +}; + +// This struct is used when the selection changes to cache the old and new state of the selection for each RenderObject. +class RenderSelectionInfo : public RenderSelectionInfoBase { +public: + RenderSelectionInfo(RenderObject* o, bool clipToVisibleContent) + : RenderSelectionInfoBase(o) + , m_rect(o->needsLayout() ? IntRect() : o->selectionRectForRepaint(m_repaintContainer, clipToVisibleContent)) + { + } + + void repaint() + { + m_object->repaintUsingContainer(m_repaintContainer, m_rect); + } + + IntRect rect() const { return m_rect; } + +private: + IntRect m_rect; // relative to repaint container +}; + + +// This struct is used when the selection changes to cache the old and new state of the selection for each RenderBlock. +class RenderBlockSelectionInfo : public RenderSelectionInfoBase { +public: + RenderBlockSelectionInfo(RenderBlock* b) + : RenderSelectionInfoBase(b) + , m_rects(b->needsLayout() ? GapRects() : block()->selectionGapRectsForRepaint(m_repaintContainer)) + { + } + + void repaint() + { + m_object->repaintUsingContainer(m_repaintContainer, m_rects); + } + + RenderBlock* block() const { return toRenderBlock(m_object); } + GapRects rects() const { return m_rects; } + +private: + GapRects m_rects; // relative to repaint container +}; + +} // namespace WebCore + + +#endif // SelectionInfo_h diff --git a/WebCore/rendering/RenderSlider.cpp b/WebCore/rendering/RenderSlider.cpp index 25f3e40..08ebadf 100644 --- a/WebCore/rendering/RenderSlider.cpp +++ b/WebCore/rendering/RenderSlider.cpp @@ -1,6 +1,5 @@ -/** - * - * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. +/* + * Copyright (C) 2006, 2007, 2008, 2009 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 @@ -33,7 +32,9 @@ #include "HTMLNames.h" #include "MediaControlElements.h" #include "MouseEvent.h" +#include "RenderLayer.h" #include "RenderTheme.h" +#include "RenderView.h" #include <wtf/MathExtras.h> #ifdef ANDROID_LAYOUT @@ -46,44 +47,115 @@ namespace WebCore { using namespace HTMLNames; -const int defaultTrackLength = 129; +static const int defaultTrackLength = 129; + +// FIXME: The SliderRange class and functions are entirely based on the DOM, +// and could be put with HTMLInputElement (possibly with a new name) instead of here. +struct SliderRange { + bool isIntegral; + double minimum; + double maximum; + + explicit SliderRange(HTMLInputElement*); + double clampValue(double value); + + // Map value into 0-1 range + double proportionFromValue(double value) + { + if (minimum == maximum) + return 0; + + return (value - minimum) / (maximum - minimum); + } + + // Map from 0-1 range to value + double valueFromProportion(double proportion) + { + return minimum + proportion * (maximum - minimum); + } + + double valueFromElement(HTMLInputElement*, bool* wasClamped = 0); +}; + +SliderRange::SliderRange(HTMLInputElement* element) +{ + // FIXME: What's the right way to handle an integral range with non-integral minimum and maximum? + // Currently values are guaranteed to be integral but could be outside the range in that case. + + isIntegral = !equalIgnoringCase(element->getAttribute(precisionAttr), "float"); -class HTMLSliderThumbElement : public HTMLDivElement { + // FIXME: This treats maximum strings that can't be parsed as 0, but perhaps 100 would be more appropriate. + const AtomicString& maxString = element->getAttribute(maxAttr); + maximum = maxString.isNull() ? 100.0 : maxString.toDouble(); + + // If the maximum is smaller, use it as the minimum. + minimum = min(element->getAttribute(minAttr).toDouble(), maximum); +} + +double SliderRange::clampValue(double value) +{ + double clampedValue = max(minimum, min(value, maximum)); + return isIntegral ? round(clampedValue) : clampedValue; +} + +double SliderRange::valueFromElement(HTMLInputElement* element, bool* wasClamped) +{ + String valueString = element->value(); + double oldValue = valueString.isNull() ? (minimum + maximum) / 2 : valueString.toDouble(); + double newValue = clampValue(oldValue); + + if (wasClamped) + *wasClamped = valueString.isNull() || newValue != oldValue; + + return newValue; +} + +// Returns a value between 0 and 1. +// As with SliderRange, this could be on HTMLInputElement instead of here. +static double sliderPosition(HTMLInputElement* element) +{ + SliderRange range(element); + return range.proportionFromValue(range.valueFromElement(element)); +} + +class SliderThumbElement : public HTMLDivElement { public: - HTMLSliderThumbElement(Document*, Node* shadowParent = 0); - + SliderThumbElement(Document*, Node* shadowParent); + + bool inDragMode() const { return m_inDragMode; } + virtual void defaultEventHandler(Event*); + +private: virtual bool isShadowNode() const { return true; } virtual Node* shadowParentNode() { return m_shadowParent; } - - bool inDragMode() const { return m_inDragMode; } -private: + Node* m_shadowParent; FloatPoint m_initialClickPoint; // initial click point in RenderSlider-local coordinates int m_initialPosition; bool m_inDragMode; }; -HTMLSliderThumbElement::HTMLSliderThumbElement(Document* doc, Node* shadowParent) - : HTMLDivElement(divTag, doc) +SliderThumbElement::SliderThumbElement(Document* document, Node* shadowParent) + : HTMLDivElement(divTag, document) , m_shadowParent(shadowParent) - , m_initialClickPoint(IntPoint()) , m_initialPosition(0) , m_inDragMode(false) { } -void HTMLSliderThumbElement::defaultEventHandler(Event* event) +void SliderThumbElement::defaultEventHandler(Event* event) { const AtomicString& eventType = event->type(); if (eventType == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); RenderSlider* slider; - if (document()->frame() && renderer() && renderer()->parent() && + if (document()->frame() && renderer() && (slider = static_cast<RenderSlider*>(renderer()->parent())) && slider->mouseEventIsInThumb(mouseEvent)) { + // Cache the initial point where the mouse down occurred, in slider coordinates - m_initialClickPoint = slider->absoluteToLocal(FloatPoint(mouseEvent->pageX(), mouseEvent->pageY()), false, true); + m_initialClickPoint = slider->absoluteToLocal(mouseEvent->absoluteLocation(), false, true); // Cache the initial position of the thumb. m_initialPosition = slider->currentPosition(); m_inDragMode = true; @@ -106,16 +178,11 @@ void HTMLSliderThumbElement::defaultEventHandler(Event* event) // Move the slider MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); RenderSlider* slider = static_cast<RenderSlider*>(renderer()->parent()); - FloatPoint curPoint = slider->absoluteToLocal(FloatPoint(mouseEvent->pageX(), mouseEvent->pageY()), false, true); - int newPosition = slider->positionForOffset( - IntPoint(m_initialPosition + curPoint.x() - m_initialClickPoint.x() - + (renderBox()->width() / 2), - m_initialPosition + curPoint.y() - m_initialClickPoint.y() - + (renderBox()->height() / 2))); - if (slider->currentPosition() != newPosition) { - slider->setCurrentPosition(newPosition); - slider->valueChanged(); - } + + FloatPoint curPoint = slider->absoluteToLocal(mouseEvent->absoluteLocation(), false, true); + IntPoint eventOffset(m_initialPosition + curPoint.x() - m_initialClickPoint.x() + renderBox()->width() / 2, + m_initialPosition + curPoint.y() - m_initialClickPoint.y() + renderBox()->height() / 2); + slider->setValueForPosition(slider->positionForOffset(eventOffset)); event->setDefaultHandled(); return; } @@ -126,7 +193,6 @@ void HTMLSliderThumbElement::defaultEventHandler(Event* event) RenderSlider::RenderSlider(HTMLInputElement* element) : RenderBlock(element) - , m_thumb(0) { } @@ -171,20 +237,20 @@ void RenderSlider::calcPrefWidths() setPrefWidthsDirty(false); } -void RenderSlider::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderSlider::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); - + if (m_thumb) - m_thumb->renderer()->setStyle(createThumbStyle(style(), m_thumb->renderer()->style())); - + m_thumb->renderer()->setStyle(createThumbStyle(style())); + setReplaced(isInline()); } -PassRefPtr<RenderStyle> RenderSlider::createThumbStyle(const RenderStyle* parentStyle, const RenderStyle* oldStyle) +PassRefPtr<RenderStyle> RenderSlider::createThumbStyle(const RenderStyle* parentStyle) { RefPtr<RenderStyle> style; - RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::SLIDER_THUMB); + RenderStyle* pseudoStyle = getCachedPseudoStyle(SLIDER_THUMB); if (pseudoStyle) // We may be sharing style with another slider, but we must not share the thumb style. style = RenderStyle::clone(pseudoStyle); @@ -195,16 +261,11 @@ PassRefPtr<RenderStyle> RenderSlider::createThumbStyle(const RenderStyle* parent style->inheritFrom(parentStyle); style->setDisplay(BLOCK); - style->setPosition(RelativePosition); - if (oldStyle) { - style->setLeft(oldStyle->left()); - style->setTop(oldStyle->top()); - } if (parentStyle->appearance() == SliderVerticalPart) - style->setAppearance(SliderThumbVerticalPart); + style->setAppearance(SliderThumbVerticalPart); else if (parentStyle->appearance() == SliderHorizontalPart) - style->setAppearance(SliderThumbHorizontalPart); + style->setAppearance(SliderThumbHorizontalPart); else if (parentStyle->appearance() == MediaSliderPart) style->setAppearance(MediaSliderThumbPart); @@ -212,54 +273,97 @@ PassRefPtr<RenderStyle> RenderSlider::createThumbStyle(const RenderStyle* parent } void RenderSlider::layout() -{ - bool relayoutChildren = false; - - if (m_thumb && m_thumb->renderer()) { - -#ifdef ANDROID_LAYOUT - int oldVisibleWidth = m_visibleWidth; -#endif - - int oldWidth = width(); - calcWidth(); - int oldHeight = height(); - calcHeight(); - - if (oldWidth != width() || oldHeight != height()) - relayoutChildren = true; +{ + ASSERT(needsLayout()); -#ifdef ANDROID_LAYOUT - const Settings* settings = document()->settings(); - ASSERT(settings); - if (oldVisibleWidth != m_visibleWidth - && settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) - relayoutChildren = true; -#endif - - // Allow the theme to set the size of the thumb - if (m_thumb->renderer()->style()->hasAppearance()) - theme()->adjustSliderThumbSize(m_thumb->renderer()); + RenderBox* thumb = m_thumb ? toRenderBox(m_thumb->renderer()) : 0; + + IntSize baseSize(borderLeft() + paddingLeft() + paddingRight() + borderRight(), + borderTop() + paddingTop() + paddingBottom() + borderBottom()); + + if (thumb) { + // Allow the theme to set the size of the thumb. + if (thumb->style()->hasAppearance()) { + // FIXME: This should pass the style, not the renderer, to the theme. + theme()->adjustSliderThumbSize(thumb); + } + + baseSize.expand(thumb->style()->width().calcMinValue(0), thumb->style()->height().calcMinValue(0)); + } + + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); + + IntSize oldSize = size(); + + setSize(baseSize); + calcWidth(); + calcHeight(); + + IntRect overflowRect(IntPoint(), size()); + if (thumb) { + if (oldSize != size()) + thumb->setChildNeedsLayout(true, false); + + LayoutStateMaintainer statePusher(view(), this, size()); + + IntRect oldThumbRect = thumb->frameRect(); + + thumb->layoutIfNeeded(); + + IntRect thumbRect; + + thumbRect.setWidth(thumb->style()->width().calcMinValue(contentWidth())); + thumbRect.setHeight(thumb->style()->height().calcMinValue(contentHeight())); + + double fraction = sliderPosition(static_cast<HTMLInputElement*>(node())); + IntRect contentRect = contentBoxRect(); if (style()->appearance() == SliderVerticalPart) { - // FIXME: Handle percentage widths correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104 - m_thumb->renderer()->style()->setLeft(Length(contentWidth() / 2 - m_thumb->renderer()->style()->width().value() / 2, Fixed)); + thumbRect.setX(contentRect.x() + (contentRect.width() - thumbRect.width()) / 2); + thumbRect.setY(contentRect.y() + static_cast<int>(nextafter((contentRect.height() - thumbRect.height()) + 1, 0) * (1 - fraction))); } else { - // FIXME: Handle percentage heights correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104 - m_thumb->renderer()->style()->setTop(Length(contentHeight() / 2 - m_thumb->renderer()->style()->height().value() / 2, Fixed)); + thumbRect.setX(contentRect.x() + static_cast<int>(nextafter((contentRect.width() - thumbRect.width()) + 1, 0) * fraction)); + thumbRect.setY(contentRect.y() + (contentRect.height() - thumbRect.height()) / 2); } - if (relayoutChildren) - setPositionFromValue(true); + thumb->setFrameRect(thumbRect); + + if (thumb->checkForRepaintDuringLayout()) + thumb->repaintDuringLayoutIfMoved(oldThumbRect); + + statePusher.pop(); + + IntRect thumbOverflowRect = thumb->overflowRect(); + thumbOverflowRect.move(thumb->x(), thumb->y()); + overflowRect.unite(thumbOverflowRect); } - RenderBlock::layoutBlock(relayoutChildren); + // FIXME: m_overflowWidth and m_overflowHeight should be renamed + // m_overflowRight and m_overflowBottom. + m_overflowLeft = overflowRect.x(); + m_overflowTop = overflowRect.y(); + m_overflowWidth = overflowRect.right(); + m_overflowHeight = overflowRect.bottom(); + + repainter.repaintAfterLayout(); + + setNeedsLayout(false); } void RenderSlider::updateFromElement() { + HTMLInputElement* element = static_cast<HTMLInputElement*>(node()); + + // Send the value back to the element if the range changes it. + SliderRange range(element); + bool clamped; + double value = range.valueFromElement(element, &clamped); + if (clamped) + element->setValueFromRenderer(String::number(value)); + + // Layout will take care of the thumb's size and position. if (!m_thumb) { - m_thumb = new HTMLSliderThumbElement(document(), node()); + m_thumb = new SliderThumbElement(document(), node()); RefPtr<RenderStyle> thumbStyle = createThumbStyle(style()); m_thumb->setRenderer(m_thumb->createRenderer(renderArena(), thumbStyle.get())); m_thumb->renderer()->setStyle(thumbStyle.release()); @@ -267,8 +371,7 @@ void RenderSlider::updateFromElement() m_thumb->setInDocument(true); addChild(m_thumb->renderer()); } - setPositionFromValue(); - setNeedsLayout(true, false); + setNeedsLayout(true); } bool RenderSlider::mouseEventIsInThumb(MouseEvent* evt) @@ -279,92 +382,45 @@ bool RenderSlider::mouseEventIsInThumb(MouseEvent* evt) #if ENABLE(VIDEO) if (style()->appearance() == MediaSliderPart) { MediaControlInputElement *sliderThumb = static_cast<MediaControlInputElement*>(m_thumb->renderer()->node()); - IntPoint absPoint(evt->pageX(), evt->pageY()); - return sliderThumb->hitTest(absPoint); - } else -#endif - { - FloatPoint localPoint = m_thumb->renderBox()->absoluteToLocal(FloatPoint(evt->pageX(), evt->pageY()), false, true); - IntRect thumbBounds = m_thumb->renderBox()->borderBoxRect(); - return thumbBounds.contains(roundedIntPoint(localPoint)); + return sliderThumb->hitTest(evt->absoluteLocation()); } +#endif + + FloatPoint localPoint = m_thumb->renderBox()->absoluteToLocal(evt->absoluteLocation(), false, true); + IntRect thumbBounds = m_thumb->renderBox()->borderBoxRect(); + return thumbBounds.contains(roundedIntPoint(localPoint)); } void RenderSlider::setValueForPosition(int position) { if (!m_thumb || !m_thumb->renderer()) return; - - const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr); - const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr); - const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr); - - double minVal = minStr.isNull() ? 0.0 : minStr.toDouble(); - double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble(); - minVal = min(minVal, maxVal); // Make sure the range is sane. - - // Calculate the new value based on the position - double factor = (double)position / (double)trackSize(); - if (style()->appearance() == SliderVerticalPart) - factor = 1.0 - factor; - double val = minVal + factor * (maxVal - minVal); - - val = max(minVal, min(val, maxVal)); // Make sure val is within min/max. - // Force integer value if not float. - if (!equalIgnoringCase(precision, "float")) - val = lround(val); + HTMLInputElement* element = static_cast<HTMLInputElement*>(node()); - static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val)); - - if (position != currentPosition()) { - setCurrentPosition(position); - static_cast<HTMLInputElement*>(node())->onChange(); - } -} - -double RenderSlider::setPositionFromValue(bool inLayout) -{ - if (!m_thumb || !m_thumb->renderer()) - return 0; - - if (!inLayout) - document()->updateLayout(); - - String value = static_cast<HTMLInputElement*>(node())->value(); - const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr); - const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr); - const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr); - - double minVal = minStr.isNull() ? 0.0 : minStr.toDouble(); - double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble(); - minVal = min(minVal, maxVal); // Make sure the range is sane. - - double oldVal = value.isNull() ? (maxVal + minVal)/2.0 : value.toDouble(); - double val = max(minVal, min(oldVal, maxVal)); // Make sure val is within min/max. - - // Force integer value if not float. - if (!equalIgnoringCase(precision, "float")) - val = lround(val); - - // Calculate the new position based on the value - double factor = (val - minVal) / (maxVal - minVal); + // Calculate the new value based on the position, and send it to the element. + SliderRange range(element); + double fraction = static_cast<double>(position) / trackSize(); if (style()->appearance() == SliderVerticalPart) - factor = 1.0 - factor; + fraction = 1 - fraction; + double value = range.clampValue(range.valueFromProportion(fraction)); + element->setValueFromRenderer(String::number(value)); - setCurrentPosition((int)(factor * trackSize())); - - if (value.isNull() || val != oldVal) - static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val)); - - return val; + // Also update the position if appropriate. + if (position != currentPosition()) { + setNeedsLayout(true); + + // FIXME: It seems like this could send extra change events if the same value is set + // multiple times with no layout in between. + element->onChange(); + } } int RenderSlider::positionForOffset(const IntPoint& p) { if (!m_thumb || !m_thumb->renderer()) return 0; - + int position; if (style()->appearance() == SliderVerticalPart) position = p.y() - m_thumb->renderBox()->height() / 2; @@ -374,55 +430,44 @@ int RenderSlider::positionForOffset(const IntPoint& p) return max(0, min(position, trackSize())); } -void RenderSlider::valueChanged() -{ - setValueForPosition(currentPosition()); - static_cast<HTMLInputElement*>(node())->onChange(); -} - int RenderSlider::currentPosition() { - if (!m_thumb || !m_thumb->renderer()) - return 0; - - if (style()->appearance() == SliderVerticalPart) - return m_thumb->renderer()->style()->top().value(); - return m_thumb->renderer()->style()->left().value(); -} - -void RenderSlider::setCurrentPosition(int pos) -{ - if (!m_thumb || !m_thumb->renderer()) - return; + ASSERT(m_thumb); + ASSERT(m_thumb->renderer()); if (style()->appearance() == SliderVerticalPart) - m_thumb->renderer()->style()->setTop(Length(pos, Fixed)); - else - m_thumb->renderer()->style()->setLeft(Length(pos, Fixed)); - - m_thumb->renderBox()->layer()->updateLayerPosition(); - repaint(); - m_thumb->renderer()->repaint(); + return toRenderBox(m_thumb->renderer())->y() - contentBoxRect().y(); + return toRenderBox(m_thumb->renderer())->x() - contentBoxRect().x(); } int RenderSlider::trackSize() { - if (!m_thumb || !m_thumb->renderer()) - return 0; + ASSERT(m_thumb); + ASSERT(m_thumb->renderer()); if (style()->appearance() == SliderVerticalPart) return contentHeight() - m_thumb->renderBox()->height(); return contentWidth() - m_thumb->renderBox()->width(); } -void RenderSlider::forwardEvent(Event* evt) +void RenderSlider::forwardEvent(Event* event) { - m_thumb->defaultEventHandler(evt); + if (event->isMouseEvent()) { + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + if (event->type() == eventNames().mousedownEvent && mouseEvent->button() == LeftButton) { + if (!mouseEventIsInThumb(mouseEvent)) { + IntPoint eventOffset = roundedIntPoint(absoluteToLocal(mouseEvent->absoluteLocation(), false, true)); + setValueForPosition(positionForOffset(eventOffset)); + } + } + } + + m_thumb->defaultEventHandler(event); } bool RenderSlider::inDragMode() const { - return m_thumb->inDragMode(); + return m_thumb && m_thumb->inDragMode(); } } // namespace WebCore diff --git a/WebCore/rendering/RenderSlider.h b/WebCore/rendering/RenderSlider.h index 95ceb0b..f1eab9c 100644 --- a/WebCore/rendering/RenderSlider.h +++ b/WebCore/rendering/RenderSlider.h @@ -1,6 +1,5 @@ -/** - * - * Copyright (C) 2006 Apple Computer, Inc. +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,16 +25,19 @@ namespace WebCore { - class HTMLDivElement; class HTMLInputElement; - class HTMLSliderThumbElement; class MouseEvent; + class SliderThumbElement; class RenderSlider : public RenderBlock { public: RenderSlider(HTMLInputElement*); - ~RenderSlider(); + virtual ~RenderSlider(); + + void forwardEvent(Event*); + bool inDragMode() const; + private: virtual const char* renderName() const { return "RenderSlider"; } virtual bool isSlider() const { return true; } @@ -43,30 +45,25 @@ namespace WebCore { virtual void calcPrefWidths(); virtual void layout(); virtual void updateFromElement(); - - virtual bool mouseEventIsInThumb(MouseEvent*); + + bool mouseEventIsInThumb(MouseEvent*); void setValueForPosition(int position); - double setPositionFromValue(bool inLayout = false); + void setPositionFromValue(); int positionForOffset(const IntPoint&); - void valueChanged(); - int currentPosition(); - void setCurrentPosition(int pos); - - void forwardEvent(Event*); - bool inDragMode() const; - protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); - - private: - PassRefPtr<RenderStyle> createThumbStyle(const RenderStyle* parentStyle, const RenderStyle* oldStyle = 0); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + PassRefPtr<RenderStyle> createThumbStyle(const RenderStyle* parentStyle); + int trackSize(); - RefPtr<HTMLSliderThumbElement> m_thumb; -}; + RefPtr<SliderThumbElement> m_thumb; + + friend class SliderThumbElement; + }; } // namespace WebCore diff --git a/WebCore/rendering/RenderTable.cpp b/WebCore/rendering/RenderTable.cpp index 784a59a..f4b1033 100644 --- a/WebCore/rendering/RenderTable.cpp +++ b/WebCore/rendering/RenderTable.cpp @@ -55,8 +55,6 @@ RenderTable::RenderTable(Node* node) , m_firstBody(0) , m_tableLayout(0) , m_currentBorder(0) - , m_frame(Void) - , m_rules(None) , m_hasColElements(false) , m_needsSectionRecalc(0) , m_hSpacing(0) @@ -76,7 +74,7 @@ RenderTable::~RenderTable() delete m_tableLayout; } -void RenderTable::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); @@ -116,8 +114,8 @@ void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) if (!beforeChild && isAfterContent(lastChild())) beforeChild = lastChild(); - bool wrapInAnonymousSection = true; - bool isTableElement = element() && element()->hasTagName(tableTag); + bool wrapInAnonymousSection = !child->isPositioned(); + bool isTableElement = node() && node()->hasTagName(tableTag); if (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION) { // First caption wins. @@ -129,7 +127,7 @@ void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) m_caption = 0; } if (!m_caption) - m_caption = static_cast<RenderBlock*>(child); + m_caption = toRenderBlock(child); wrapInAnonymousSection = false; } else if (child->isTableCol()) { m_hasColElements = true; @@ -172,21 +170,16 @@ void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) } } else if (child->isTableCell() || child->isTableRow()) { wrapInAnonymousSection = true; - } else { + } else // Allow a form to just sit at the top level. - wrapInAnonymousSection = !isTableElement || !child->element() || !(child->element()->hasTagName(formTag) && document()->isHTMLDocument()); - - // FIXME: Allow the delete button container element to sit at the top level. This is needed until http://bugs.webkit.org/show_bug.cgi?id=11363 is fixed. - if (wrapInAnonymousSection && child->element() && child->element()->isHTMLElement() && static_cast<HTMLElement*>(child->element())->id() == DeleteButtonController::containerElementIdentifier) - wrapInAnonymousSection = false; - } + wrapInAnonymousSection = !isTableElement || !child->node() || !(child->node()->hasTagName(formTag) && document()->isHTMLDocument()); if (!wrapInAnonymousSection) { // If the next renderer is actually wrapped in an anonymous table section, we need to go up and find that. while (beforeChild && !beforeChild->isTableSection() && !beforeChild->isTableCol() && beforeChild->style()->display() != TABLE_CAPTION) beforeChild = beforeChild->parent(); - RenderContainer::addChild(child, beforeChild); + RenderBox::addChild(child, beforeChild); return; } @@ -214,6 +207,12 @@ void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) section->addChild(child); } +void RenderTable::removeChild(RenderObject* oldChild) +{ + RenderBox::removeChild(oldChild); + setNeedsSectionRecalc(); +} + void RenderTable::calcWidth() { #ifdef ANDROID_LAYOUT @@ -240,7 +239,7 @@ void RenderTable::calcWidth() } else { // An auto width table should shrink to fit within the line width if necessary in order to // avoid overlapping floats. - availableWidth = cb->lineWidth(y()); + availableWidth = cb->lineWidth(y(), false); // Subtract out any fixed margins from our available width for auto width tables. int marginTotal = 0; @@ -278,14 +277,7 @@ void RenderTable::layout() recalcSectionsIfNeeded(); - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = checkForRepaintDuringLayout(); - if (checkForRepaint) { - oldBounds = absoluteClippedOverflowRect(); - oldOutlineBox = absoluteOutlineBounds(); - } - + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y())); setHeight(0); @@ -308,17 +300,29 @@ void RenderTable::layout() else if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { // if the width of a table is wider than its container width, or it has a nested table, // we will render it with single column. - int cw = containingBlockWidth(); - if (width() > cw || hasChildTable()) { + int cw = containingBlockWidthForContent(); + bool shouldRenderAsSingleColumn = (width() > cw); + if (!shouldRenderAsSingleColumn) { + RenderObject* child = firstChild(); + while (child) { + if (child->isTable()) { + shouldRenderAsSingleColumn = true; + break; + } + child = child->nextInPreOrder(); + } + } + + if (shouldRenderAsSingleColumn) { m_singleColumn = true; if (width() > cw) - setWidth(cw); + setWidth(cw); if (m_minPrefWidth > cw) - m_minPrefWidth = cw; - if (m_maxPrefWidth > cw) - m_maxPrefWidth = cw; + m_minPrefWidth = cw; + if (m_maxPrefWidth > cw) + m_maxPrefWidth = cw; } - } + } #endif if (m_caption && width() != oldWidth) m_caption->setNeedsLayout(true, false); @@ -339,18 +343,18 @@ void RenderTable::layout() bool collapsing = collapseBorders(); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - // FIXME: What about a form that has a display value that makes it a table section? #ifdef ANDROID_LAYOUT - if ((relayoutChildren || child->needsLayout()) && - !(child->element() && child->element()->hasTagName(formTag))) { - child->setNeedsLayout(true); - child->layout(); + if (relayoutChildren) { + child->setNeedsLayout(true, false); + if (!child->isTableSection()) { + child->layoutIfNeeded(); + continue; + } + // fall through } -#else - if (child->needsLayout() && !(child->element() && child->element()->hasTagName(formTag) && !child->isTableSection())) - child->layout(); #endif if (child->isTableSection()) { + child->layoutIfNeeded(); RenderTableSection* section = static_cast<RenderTableSection*>(child); calculatedHeight += section->calcRowHeight(); if (collapsing) @@ -359,6 +363,10 @@ void RenderTable::layout() } } + // Only lay out one caption, since it's the only one we're going to end up painting. + if (m_caption) + m_caption->layoutIfNeeded(); + m_overflowWidth = width() + (collapsing ? outerBorderRight() - borderRight() : 0); m_overflowLeft = collapsing ? borderLeft() - outerBorderLeft() : 0; @@ -480,10 +488,8 @@ void RenderTable::layout() statePusher.pop(); - bool didFullRepaint = true; + bool didFullRepaint = repainter.repaintAfterLayout(); // Repaint with our new bounds if they are different from our old bounds. - if (checkForRepaint) - didFullRepaint = repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); if (!didFullRepaint && sectionMoved) repaintRectangle(IntRect(m_overflowLeft, movedSectionTop, m_overflowWidth - m_overflowLeft, m_overflowHeight - movedSectionTop)); @@ -511,6 +517,15 @@ void RenderTable::paint(PaintInfo& paintInfo, int tx, int ty) if (tx + overflowLeft(false) >= paintInfo.rect.right() + os || tx + overflowWidth(false) <= paintInfo.rect.x() - os) return; + bool pushedClip = pushContentsClip(paintInfo, tx, ty); + paintObject(paintInfo, tx, ty); + if (pushedClip) + popContentsClip(paintInfo, paintPhase, tx, ty); +} + +void RenderTable::paintObject(PaintInfo& paintInfo, int tx, int ty) +{ + PaintPhase paintPhase = paintInfo.phase; if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE) paintBoxDecorations(paintInfo, tx, ty); @@ -522,19 +537,20 @@ void RenderTable::paint(PaintInfo& paintInfo, int tx, int ty) // We're done. We don't bother painting any children. if (paintPhase == PaintPhaseBlockBackground) return; - + // We don't paint our own background, but we do let the kids paint their backgrounds. if (paintPhase == PaintPhaseChildBlockBackgrounds) paintPhase = PaintPhaseChildBlockBackground; + PaintInfo info(paintInfo); info.phase = paintPhase; info.paintingRoot = paintingRootForChildren(paintInfo); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (!child->hasLayer() && (child->isTableSection() || child == m_caption)) + if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption)) child->paint(info, tx, ty); } - + if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) { // Collect all the unique border styles that we want to paint in a sorted list. Once we // have all the styles sorted, we then do individual passes, painting each style of border @@ -717,7 +733,7 @@ void RenderTable::recalcSections() const switch (child->style()->display()) { case TABLE_CAPTION: if (!m_caption && child->isRenderBlock()) { - m_caption = static_cast<RenderBlock*>(child); + m_caption = toRenderBlock(child); m_caption->setNeedsLayout(true); } break; @@ -777,12 +793,6 @@ void RenderTable::recalcSections() const m_needsSectionRecalc = false; } -RenderObject* RenderTable::removeChildNode(RenderObject* child, bool fullRemove) -{ - setNeedsSectionRecalc(); - return RenderContainer::removeChildNode(child, fullRemove); -} - int RenderTable::calcBorderLeft() const { if (collapseBorders()) { @@ -1172,7 +1182,7 @@ void RenderTable::updateFirstLetter() { } -int RenderTable::getBaselineOfFirstLineBox() const +int RenderTable::firstLineBoxBaseline() const { RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); if (firstNonEmptySection && !firstNonEmptySection->numRows()) @@ -1181,12 +1191,12 @@ int RenderTable::getBaselineOfFirstLineBox() const if (!firstNonEmptySection) return -1; - return firstNonEmptySection->y() + firstNonEmptySection->getBaselineOfFirstLineBox(); + return firstNonEmptySection->y() + firstNonEmptySection->firstLineBoxBaseline(); } -IntRect RenderTable::getOverflowClipRect(int tx, int ty) +IntRect RenderTable::overflowClipRect(int tx, int ty) { - IntRect rect = RenderBlock::getOverflowClipRect(tx, ty); + IntRect rect = RenderBlock::overflowClipRect(tx, ty); // If we have a caption, expand the clip to include the caption. // FIXME: Technically this is wrong, but it's virtually impossible to fix this @@ -1202,4 +1212,29 @@ IntRect RenderTable::getOverflowClipRect(int tx, int ty) return rect; } +bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action) +{ + tx += x(); + ty += y(); + + // Check kids first. + if (!hasOverflowClip() || overflowClipRect(tx, ty).contains(xPos, yPos)) { + for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { + if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption) && + child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) { + updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); + return true; + } + } + } + + // Check our bounds next. + if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && IntRect(tx, ty, width(), height()).contains(xPos, yPos)) { + updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); + return true; + } + + return false; +} + } diff --git a/WebCore/rendering/RenderTable.h b/WebCore/rendering/RenderTable.h index e3168cb..2fb7a14 100644 --- a/WebCore/rendering/RenderTable.h +++ b/WebCore/rendering/RenderTable.h @@ -39,26 +39,6 @@ class TableLayout; class RenderTable : public RenderBlock { public: - enum Rules { - None = 0x00, - RGroups = 0x01, - CGroups = 0x02, - Groups = 0x03, - Rows = 0x05, - Cols = 0x0a, - All = 0x0f - }; - enum Frame { - Void = 0x00, - Above = 0x01, - Below = 0x02, - Lhs = 0x04, - Rhs = 0x08, - Hsides = 0x03, - Vsides = 0x0c, - Box = 0x0f - }; - RenderTable(Node*); ~RenderTable(); @@ -79,8 +59,6 @@ public: int borderTop() const; int borderBottom() const; - Rules getRules() const { return static_cast<Rules>(m_rules); } - const Color& bgColor() const { return style()->backgroundColor(); } int outerBorderTop() const; @@ -94,13 +72,17 @@ public: // overrides virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject* oldChild); + virtual void paint(PaintInfo&, int tx, int ty); + virtual void paintObject(PaintInfo&, int tx, int ty); virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); virtual void paintMask(PaintInfo& paintInfo, int tx, int ty); virtual void layout(); virtual void calcPrefWidths(); - - virtual int getBaselineOfFirstLineBox() const; + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int xPos, int yPos, int tx, int ty, HitTestAction); + + virtual int firstLineBoxBaseline() const; virtual RenderBlock* firstLineBlock() const; virtual void updateFirstLetter(); @@ -169,8 +151,6 @@ public: setNeedsLayout(true); } - virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); - RenderTableSection* sectionAbove(const RenderTableSection*, bool skipEmptySections = false) const; RenderTableSection* sectionBelow(const RenderTableSection*, bool skipEmptySections = false) const; @@ -183,7 +163,7 @@ public: bool hasSections() const { return m_head || m_foot || m_firstBody; } - virtual IntRect getOverflowClipRect(int tx, int ty); + virtual IntRect overflowClipRect(int tx, int ty); void recalcSectionsIfNeeded() const { @@ -197,7 +177,7 @@ public: #endif protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); private: void recalcSections() const; @@ -214,9 +194,6 @@ private: const CollapsedBorderValue* m_currentBorder; - unsigned m_frame : 4; // Frame - unsigned m_rules : 4; // Rules - mutable bool m_hasColElements : 1; mutable bool m_needsSectionRecalc : 1; diff --git a/WebCore/rendering/RenderTableCell.cpp b/WebCore/rendering/RenderTableCell.cpp index f5bf358..f5e6ae2 100644 --- a/WebCore/rendering/RenderTableCell.cpp +++ b/WebCore/rendering/RenderTableCell.cpp @@ -31,6 +31,7 @@ #include "HTMLTableCellElement.h" #include "RenderTableCol.h" #include "RenderView.h" +#include "TransformState.h" #ifdef ANDROID_LAYOUT #include "Document.h" @@ -68,9 +69,9 @@ void RenderTableCell::destroy() void RenderTableCell::updateFromElement() { - Node* node = element(); - if (node && (node->hasTagName(tdTag) || node->hasTagName(thTag))) { - HTMLTableCellElement* tc = static_cast<HTMLTableCellElement*>(node); + Node* n = node(); + if (n && (n->hasTagName(tdTag) || n->hasTagName(thTag))) { + HTMLTableCellElement* tc = static_cast<HTMLTableCellElement*>(n); int oldRSpan = m_rowSpan; int oldCSpan = m_columnSpan; @@ -110,10 +111,10 @@ void RenderTableCell::calcPrefWidths() table()->recalcSectionsIfNeeded(); RenderBlock::calcPrefWidths(); - if (element() && style()->autoWrap()) { + if (node() && style()->autoWrap()) { // See if nowrap was set. Length w = styleOrColWidth(); - String nowrap = static_cast<Element*>(element())->getAttribute(nowrapAttr); + String nowrap = static_cast<Element*>(node())->getAttribute(nowrapAttr); if (!nowrap.isNull() && w.isFixed()) // Nowrap is set, but we didn't actually use it because of the // fixed width set on the cell. Even so, it is a WinIE/Moz trait @@ -141,14 +142,14 @@ void RenderTableCell::updateWidth(int w) { if (w != width()) { setWidth(w); - m_cellWidthChanged = true; + setCellWidthChanged(true); } } void RenderTableCell::layout() { - layoutBlock(m_cellWidthChanged); - m_cellWidthChanged = false; + layoutBlock(cellWidthChanged()); + setCellWidthChanged(false); } int RenderTableCell::paddingTop(bool includeIntrinsicPadding) const @@ -167,7 +168,7 @@ void RenderTableCell::setOverrideSize(int size) RenderBlock::setOverrideSize(size); } -IntRect RenderTableCell::clippedOverflowRectForRepaint(RenderBox* repaintContainer) +IntRect RenderTableCell::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { // If the table grid is dirty, we cannot get reliable information about adjoining cells, // so we ignore outside borders. This should not be a problem because it means that @@ -182,13 +183,13 @@ IntRect RenderTableCell::clippedOverflowRectForRepaint(RenderBox* repaintContain int right = max(borderHalfRight(true), outlineSize); int top = max(borderHalfTop(true), outlineSize); int bottom = max(borderHalfBottom(true), outlineSize); - if (left && !rtl || right && rtl) { + if ((left && !rtl) || (right && rtl)) { if (RenderTableCell* before = table()->cellBefore(this)) { top = max(top, before->borderHalfTop(true)); bottom = max(bottom, before->borderHalfBottom(true)); } } - if (left && rtl || right && !rtl) { + if ((left && rtl) || (right && !rtl)) { if (RenderTableCell* after = table()->cellAfter(this)) { top = max(top, after->borderHalfTop(true)); bottom = max(bottom, after->borderHalfBottom(true)); @@ -215,11 +216,11 @@ IntRect RenderTableCell::clippedOverflowRectForRepaint(RenderBox* repaintContain // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 r.move(v->layoutDelta()); } - computeRectForRepaint(r, repaintContainer); + computeRectForRepaint(repaintContainer, r); return r; } -void RenderTableCell::computeRectForRepaint(IntRect& r, RenderBox* repaintContainer, bool fixed) +void RenderTableCell::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& r, bool fixed) { if (repaintContainer == this) return; @@ -227,54 +228,48 @@ void RenderTableCell::computeRectForRepaint(IntRect& r, RenderBox* repaintContai RenderView* v = view(); if ((!v || !v->layoutStateEnabled()) && parent()) r.move(-parentBox()->x(), -parentBox()->y()); // Rows are in the same coordinate space, so don't add their offset in. - RenderBlock::computeRectForRepaint(r, repaintContainer, fixed); + RenderBlock::computeRectForRepaint(repaintContainer, r, fixed); } -FloatPoint RenderTableCell::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const +void RenderTableCell::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const { + if (repaintContainer == this) + return; + RenderView* v = view(); if ((!v || !v->layoutStateEnabled()) && parent()) { // Rows are in the same coordinate space, so don't add their offset in. - localPoint.move(-parentBox()->x(), -parentBox()->y()); + // FIXME: this is wrong with transforms + transformState.move(-parentBox()->x(), -parentBox()->y()); } - return RenderBlock::localToAbsolute(localPoint, fixed, useTransforms); + RenderBlock::mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); } -FloatPoint RenderTableCell::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const +void RenderTableCell::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const { - FloatPoint localPoint = RenderBlock::absoluteToLocal(containerPoint, fixed, useTransforms); + RenderBlock::mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); if (parent()) { // Rows are in the same coordinate space, so add their offset back in. - localPoint.move(parentBox()->x(), parentBox()->y()); + // FIXME: this is wrong with transforms + transformState.move(parentBox()->x(), parentBox()->y()); } - return localPoint; } -FloatQuad RenderTableCell::localToContainerQuad(const FloatQuad& localQuad, RenderBox* repaintContainer, bool fixed) const +int RenderTableCell::baselinePosition(bool firstLine, bool isRootLineBox) const { - if (repaintContainer == this) - return localQuad; - - FloatQuad quad = localQuad; - if (parent()) { - // Rows are in the same coordinate space, so don't add their offset in. - quad.move(-parentBox()->x(), -parentBox()->y()); - } - return RenderBlock::localToContainerQuad(quad, repaintContainer, fixed); -} + if (isRootLineBox) + return RenderBox::baselinePosition(firstLine, isRootLineBox); -int RenderTableCell::baselinePosition(bool /*firstLine*/, bool /*isRootLineBox*/) const -{ // <http://www.w3.org/TR/2007/CR-CSS21-20070719/tables.html#height-layout>: The baseline of a cell is the baseline of // the first in-flow line box in the cell, or the first in-flow table-row in the cell, whichever comes first. If there // is no such line box or table-row, the baseline is the bottom of content edge of the cell box. - int firstLineBaseline = getBaselineOfFirstLineBox(); + int firstLineBaseline = firstLineBoxBaseline(); if (firstLineBaseline != -1) return firstLineBaseline; return paddingTop() + borderTop() + contentHeight(); } -void RenderTableCell::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +void RenderTableCell::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { if (parent() && section() && style() && style()->height() != newStyle->height()) section()->setNeedsCellRecalc(); @@ -284,7 +279,7 @@ void RenderTableCell::styleWillChange(RenderStyle::Diff diff, const RenderStyle* RenderBlock::styleWillChange(diff, newStyle); } -void RenderTableCell::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderTableCell::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); setHasBoxDecorations(true); @@ -660,22 +655,17 @@ int RenderTableCell::borderHalfBottom(bool outer) const void RenderTableCell::paint(PaintInfo& paintInfo, int tx, int ty) { - tx += x(); - ty += y(); - - // check if we need to do anything at all... - int os = 2 * maximalOutlineSize(paintInfo.phase); - if (paintInfo.phase == PaintPhaseCollapsedTableBorders && style()->visibility() == VISIBLE) { - if (ty - table()->outerBorderTop() >= paintInfo.rect.bottom() + os || - ty + height() + table()->outerBorderBottom() <= paintInfo.rect.y() - os) - return; - paintCollapsedBorder(paintInfo.context, tx, ty, width(), height()); - } else { - if (ty + overflowTop(false) >= paintInfo.rect.bottom() + os || ty + overflowHeight(false) <= paintInfo.rect.y() - os) - return; - RenderBlock::paintObject(paintInfo, tx, ty); - } + tx += x(); + ty += y(); + int os = 2 * maximalOutlineSize(paintInfo.phase); + if (ty - table()->outerBorderTop() < paintInfo.rect.bottom() + os && + ty + height() + table()->outerBorderBottom() > paintInfo.rect.y() - os) + paintCollapsedBorder(paintInfo.context, tx, ty, width(), height()); + return; + } + + RenderBlock::paint(paintInfo, tx, ty); } static EBorderStyle collapsedBorderStyle(EBorderStyle style) @@ -689,7 +679,7 @@ static EBorderStyle collapsedBorderStyle(EBorderStyle style) struct CollapsedBorder { CollapsedBorderValue borderValue; - RenderObject::BorderSide side; + BoxSide side; bool shouldPaint; int x1; int y1; @@ -705,7 +695,7 @@ public: { } - void addBorder(const CollapsedBorderValue& borderValue, RenderObject::BorderSide borderSide, bool shouldPaint, + void addBorder(const CollapsedBorderValue& borderValue, BoxSide borderSide, bool shouldPaint, int x1, int y1, int x2, int y2, EBorderStyle borderStyle) { if (borderValue.exists() && shouldPaint) { @@ -830,8 +820,8 @@ void RenderTableCell::paintCollapsedBorder(GraphicsContext* graphicsContext, int for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { if (border->borderValue == *table()->currentBorderStyle()) - drawBorder(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, - border->borderValue.color(), style()->color(), border->style, 0, 0); + drawLineForBoxSide(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, + border->borderValue.color(), style()->color(), border->style, 0, 0); } } diff --git a/WebCore/rendering/RenderTableCell.h b/WebCore/rendering/RenderTableCell.h index 97e2dda..4b09032 100644 --- a/WebCore/rendering/RenderTableCell.h +++ b/WebCore/rendering/RenderTableCell.h @@ -99,10 +99,8 @@ public: void paintCollapsedBorder(GraphicsContext*, int x, int y, int w, int h); void paintBackgroundsBehindCell(PaintInfo&, int tx, int ty, RenderObject* backgroundObject); - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); - virtual void computeRectForRepaint(IntRect&, RenderBox* repaintContainer, bool fixed = false); - virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; - virtual FloatPoint absoluteToLocal(FloatPoint containerPoint, bool fixed = false, bool useTransforms = false) const; + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); + virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); virtual int baselinePosition(bool firstLine = false, bool isRootLineBox = false) const; @@ -120,10 +118,11 @@ public: virtual void setOverrideSize(int); protected: - virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); - virtual FloatQuad localToContainerQuad(const FloatQuad&, RenderBox* repaintContainer, bool fixed = false) const; + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; + virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const; private: int m_row; diff --git a/WebCore/rendering/RenderTableCol.cpp b/WebCore/rendering/RenderTableCol.cpp index 19538fa..f17963c 100644 --- a/WebCore/rendering/RenderTableCol.cpp +++ b/WebCore/rendering/RenderTableCol.cpp @@ -37,7 +37,7 @@ namespace WebCore { using namespace HTMLNames; RenderTableCol::RenderTableCol(Node* node) - : RenderContainer(node), m_span(1) + : RenderBox(node), m_span(1) { // init RenderObject attributes setInline(true); // our object is not Inline @@ -47,9 +47,9 @@ RenderTableCol::RenderTableCol(Node* node) void RenderTableCol::updateFromElement() { int oldSpan = m_span; - Node* node = element(); - if (node && (node->hasTagName(colTag) || node->hasTagName(colgroupTag))) { - HTMLTableColElement* tc = static_cast<HTMLTableColElement*>(node); + Node* n = node(); + if (n && (n->hasTagName(colTag) || n->hasTagName(colgroupTag))) { + HTMLTableColElement* tc = static_cast<HTMLTableColElement*>(n); m_span = tc->span(); } else m_span = !(style() && style()->display() == TABLE_COLUMN_GROUP); @@ -69,7 +69,7 @@ bool RenderTableCol::canHaveChildren() const return style()->display() == TABLE_COLUMN_GROUP; } -IntRect RenderTableCol::clippedOverflowRectForRepaint(RenderBox* /*repaintContainer*/) +IntRect RenderTableCol::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { // For now, just repaint the whole table. // FIXME: Find a better way to do this, e.g., need to repaint all the cells that we @@ -79,7 +79,7 @@ IntRect RenderTableCol::clippedOverflowRectForRepaint(RenderBox* /*repaintContai if (table && !table->isTable()) table = table->parent(); if (table && table->isTable()) - return table->absoluteClippedOverflowRect(); + return table->clippedOverflowRectForRepaint(repaintContainer); return IntRect(); } diff --git a/WebCore/rendering/RenderTableCol.h b/WebCore/rendering/RenderTableCol.h index 3472965..8c494b2 100644 --- a/WebCore/rendering/RenderTableCol.h +++ b/WebCore/rendering/RenderTableCol.h @@ -28,15 +28,20 @@ #ifndef RenderTableCol_h #define RenderTableCol_h -#include "RenderContainer.h" +#include "RenderBox.h" namespace WebCore { -class RenderTableCol : public RenderContainer +class RenderTableCol : public RenderBox { public: RenderTableCol(Node*); + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + virtual const char* renderName() const { return "RenderTableCol"; } virtual bool isTableCol() const { return true; } virtual int lineHeight(bool) const { return 0; } @@ -46,13 +51,14 @@ public: virtual bool canHaveChildren() const; virtual bool requiresLayer() const { return false; } - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); int span() const { return m_span; } void setSpan(int s) { m_span = s; } private: + RenderObjectChildList m_children; int m_span; }; diff --git a/WebCore/rendering/RenderTableRow.cpp b/WebCore/rendering/RenderTableRow.cpp index 6b83769..c0603a2 100644 --- a/WebCore/rendering/RenderTableRow.cpp +++ b/WebCore/rendering/RenderTableRow.cpp @@ -42,7 +42,7 @@ namespace WebCore { using namespace HTMLNames; RenderTableRow::RenderTableRow(Node* node) - : RenderContainer(node) + : RenderBox(node) { // init RenderObject attributes setInline(false); // our object is not Inline @@ -52,20 +52,20 @@ void RenderTableRow::destroy() { RenderTableSection* recalcSection = section(); - RenderContainer::destroy(); + RenderBox::destroy(); if (recalcSection) recalcSection->setNeedsCellRecalc(); } -void RenderTableRow::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +void RenderTableRow::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { if (section() && style() && style()->height() != newStyle->height()) section()->setNeedsCellRecalc(); ASSERT(newStyle->display() == TABLE_ROW); - RenderContainer::styleWillChange(diff, newStyle); + RenderBox::styleWillChange(diff, newStyle); } void RenderTableRow::addChild(RenderObject* child, RenderObject* beforeChild) @@ -74,16 +74,16 @@ void RenderTableRow::addChild(RenderObject* child, RenderObject* beforeChild) if (!beforeChild && isAfterContent(lastChild())) beforeChild = lastChild(); - bool isTableRow = element() && element()->hasTagName(trTag); + bool isTableRow = node() && node()->hasTagName(trTag); #if ENABLE(WML) - if (!isTableRow && element() && element()->isWMLElement()) - isTableRow = element()->hasTagName(WMLNames::trTag); + if (!isTableRow && node() && node()->isWMLElement()) + isTableRow = node()->hasTagName(WMLNames::trTag); #endif if (!child->isTableCell()) { - if (isTableRow && child->element() && child->element()->hasTagName(formTag) && document()->isHTMLDocument()) { - RenderContainer::addChild(child, beforeChild); + if (isTableRow && child->node() && child->node()->hasTagName(formTag) && document()->isHTMLDocument()) { + RenderBox::addChild(child, beforeChild); return; } @@ -121,8 +121,8 @@ void RenderTableRow::addChild(RenderObject* child, RenderObject* beforeChild) if (parent()) section()->addCell(cell, this); - ASSERT(!beforeChild || beforeChild->isTableCell() || isTableRow && beforeChild->element() && beforeChild->element()->hasTagName(formTag) && document()->isHTMLDocument()); - RenderContainer::addChild(cell, beforeChild); + ASSERT(!beforeChild || beforeChild->isTableCell() || isTableRow && beforeChild->node() && beforeChild->node()->hasTagName(formTag) && document()->isHTMLDocument()); + RenderBox::addChild(cell, beforeChild); if (beforeChild || nextSibling()) section()->setNeedsCellRecalc(); @@ -148,7 +148,7 @@ void RenderTableRow::layout() // We only ever need to repaint if our cells didn't, which menas that they didn't need // layout, so we know that our bounds didn't change. This code is just making up for // the fact that we did not repaint in setStyle() because we had a layout hint. - // We cannot call repaint() because our absoluteClippedOverflowRect() is taken from the + // We cannot call repaint() because our clippedOverflowRectForRepaint() is taken from the // parent table, and being mid-layout, that is invalid. Instead, we repaint our cells. if (selfNeedsLayout() && checkForRepaintDuringLayout()) { for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { @@ -161,7 +161,7 @@ void RenderTableRow::layout() setNeedsLayout(false); } -IntRect RenderTableRow::clippedOverflowRectForRepaint(RenderBox* repaintContainer) +IntRect RenderTableRow::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { // For now, just repaint the whole table. // FIXME: Find a better way to do this, e.g., need to repaint all the cells that we @@ -183,7 +183,7 @@ bool RenderTableRow::nodeAtPoint(const HitTestRequest& request, HitTestResult& r // at the moment (a demoted inline <form> for example). If we ever implement a // table-specific hit-test method (which we should do for performance reasons anyway), // then we can remove this check. - if (!child->hasLayer() && !child->isRenderInline() && child->nodeAtPoint(request, result, x, y, tx, ty, action)) { + if (child->isTableCell() && !toRenderBox(child)->hasSelfPaintingLayer() && child->nodeAtPoint(request, result, x, y, tx, ty, action)) { updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } @@ -194,10 +194,9 @@ bool RenderTableRow::nodeAtPoint(const HitTestRequest& request, HitTestResult& r void RenderTableRow::paint(PaintInfo& paintInfo, int tx, int ty) { - ASSERT(m_layer); - if (!m_layer) + ASSERT(hasSelfPaintingLayer()); + if (!layer()) return; - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (child->isTableCell()) { // Paint the row background behind the cell. @@ -205,7 +204,7 @@ void RenderTableRow::paint(PaintInfo& paintInfo, int tx, int ty) RenderTableCell* cell = static_cast<RenderTableCell*>(child); cell->paintBackgroundsBehindCell(paintInfo, tx, ty, this); } - if (!child->hasLayer()) + if (!toRenderBox(child)->hasSelfPaintingLayer()) child->paint(paintInfo, tx, ty); } } diff --git a/WebCore/rendering/RenderTableRow.h b/WebCore/rendering/RenderTableRow.h index 79d32d8..9622480 100644 --- a/WebCore/rendering/RenderTableRow.h +++ b/WebCore/rendering/RenderTableRow.h @@ -31,10 +31,15 @@ namespace WebCore { -class RenderTableRow : public RenderContainer { +class RenderTableRow : public RenderBox { public: RenderTableRow(Node*); + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + RenderTableSection* section() const { return static_cast<RenderTableSection*>(parent()); } RenderTable* table() const { return static_cast<RenderTable*>(parent()->parent()); } @@ -47,19 +52,21 @@ private: virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); virtual int lineHeight(bool, bool) const { return 0; } - virtual void position(InlineBox*) { } virtual void layout(); - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); // The only time rows get a layer is when they have transparency. - virtual bool requiresLayer() const { return isTransparent() || hasOverflowClip(); } + virtual bool requiresLayer() const { return isTransparent() || hasOverflowClip() || hasTransform() || hasMask(); } virtual void paint(PaintInfo&, int tx, int ty); + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); - virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); +private: + RenderObjectChildList m_children; }; } // namespace WebCore diff --git a/WebCore/rendering/RenderTableSection.cpp b/WebCore/rendering/RenderTableSection.cpp index ad2bfaf..59fb324 100644 --- a/WebCore/rendering/RenderTableSection.cpp +++ b/WebCore/rendering/RenderTableSection.cpp @@ -47,7 +47,7 @@ namespace WebCore { using namespace HTMLNames; RenderTableSection::RenderTableSection(Node* node) - : RenderContainer(node) + : RenderBox(node) , m_gridRows(0) , m_cCol(0) , m_cRow(-1) @@ -75,7 +75,7 @@ void RenderTableSection::destroy() { RenderTable* recalcTable = table(); - RenderContainer::destroy(); + RenderBox::destroy(); // recalc cell info because RenderTable has unguarded pointers // stored that point to this RenderTableSection. @@ -89,11 +89,11 @@ void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild if (!beforeChild && isAfterContent(lastChild())) beforeChild = lastChild(); - bool isTableSection = element() && (element()->hasTagName(theadTag) || element()->hasTagName(tbodyTag) || element()->hasTagName(tfootTag)); + bool isTableSection = node() && (node()->hasTagName(theadTag) || node()->hasTagName(tbodyTag) || node()->hasTagName(tfootTag)); if (!child->isTableRow()) { - if (isTableSection && child->element() && child->element()->hasTagName(formTag) && document()->isHTMLDocument()) { - RenderContainer::addChild(child, beforeChild); + if (isTableSection && child->node() && child->node()->hasTagName(formTag) && document()->isHTMLDocument()) { + RenderBox::addChild(child, beforeChild); return; } @@ -147,8 +147,14 @@ void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild while (beforeChild && beforeChild->parent() != this) beforeChild = beforeChild->parent(); - ASSERT(!beforeChild || beforeChild->isTableRow() || isTableSection && beforeChild->element() && beforeChild->element()->hasTagName(formTag) && document()->isHTMLDocument()); - RenderContainer::addChild(child, beforeChild); + ASSERT(!beforeChild || beforeChild->isTableRow() || isTableSection && beforeChild->node() && beforeChild->node()->hasTagName(formTag) && document()->isHTMLDocument()); + RenderBox::addChild(child, beforeChild); +} + +void RenderTableSection::removeChild(RenderObject* oldChild) +{ + setNeedsCellRecalc(); + RenderBox::removeChild(oldChild); } bool RenderTableSection::ensureRows(int numRows) @@ -242,7 +248,7 @@ void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row) for (int r = 0; r < rSpan; r++) { CellStruct& c = cellAt(m_cRow + r, m_cCol); - if (currentCell.cell && !c.cell) + if (!c.cell) c.cell = currentCell.cell; if (currentCell.inColSpan) c.inColSpan = true; @@ -252,10 +258,8 @@ void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row) currentCell.cell = 0; currentCell.inColSpan = true; } - if (cell) { - cell->setRow(m_cRow); - cell->setCol(table()->effColToCol(col)); - } + cell->setRow(m_cRow); + cell->setCol(table()->effColToCol(col)); } void RenderTableSection::setCellWidths() @@ -425,6 +429,21 @@ int RenderTableSection::calcRowHeight() return m_rowPos[m_gridRows]; } +void RenderTableSection::layout() +{ + ASSERT(needsLayout()); + + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y())); + for (RenderObject* child = children()->firstChild(); child; child = child->nextSibling()) { + if (child->isTableRow()) { + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + } + } + statePusher.pop(); + setNeedsLayout(false); +} + int RenderTableSection::layoutRows(int toAdd) { #ifndef NDEBUG @@ -598,17 +617,37 @@ int RenderTableSection::layoutRows(int toAdd) (!table()->style()->height().isAuto() && rHeight != cell->height()); for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) { - if (!o->isText() && o->style()->height().isPercent() && (o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow()) || flexAllChildren)) { + if (!o->isText() && o->style()->height().isPercent() && (flexAllChildren || o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow()))) { // Tables with no sections do not flex. if (!o->isTable() || static_cast<RenderTable*>(o)->hasSections()) { o->setNeedsLayout(true, false); - cell->setChildNeedsLayout(true, false); cellChildrenFlex = true; } } } - + + if (HashSet<RenderBox*>* percentHeightDescendants = cell->percentHeightDescendants()) { + HashSet<RenderBox*>::iterator end = percentHeightDescendants->end(); + for (HashSet<RenderBox*>::iterator it = percentHeightDescendants->begin(); it != end; ++it) { + RenderBox* box = *it; + if (!box->isReplaced() && !box->scrollsOverflow() && !flexAllChildren) + continue; + + while (box != cell) { + if (box->normalChildNeedsLayout()) + break; + box->setChildNeedsLayout(true, false); + box = box->containingBlock(); + ASSERT(box); + if (!box) + break; + } + cellChildrenFlex = true; + } + } + if (cellChildrenFlex) { + cell->setChildNeedsLayout(true, false); // Alignment within a cell is based off the calculated // height, which becomes irrelevant once the cell has // been resized based off its percentage. @@ -702,14 +741,16 @@ int RenderTableSection::layoutRows(int toAdd) int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf) const { - int bottom = RenderContainer::lowestPosition(includeOverflowInterior, includeSelf); + int bottom = RenderBox::lowestPosition(includeOverflowInterior, includeSelf); if (!includeOverflowInterior && hasOverflowClip()) return bottom; for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { - for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { - if (cell->isTableCell()) - bottom = max(bottom, static_cast<RenderTableCell*>(cell)->y() + cell->lowestPosition(false)); + for (RenderObject* curr = row->firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isTableCell()) { + RenderTableCell* cell = static_cast<RenderTableCell*>(curr); + bottom = max(bottom, cell->y() + cell->lowestPosition(false)); + } } } @@ -718,14 +759,16 @@ int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includ int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const { - int right = RenderContainer::rightmostPosition(includeOverflowInterior, includeSelf); + int right = RenderBox::rightmostPosition(includeOverflowInterior, includeSelf); if (!includeOverflowInterior && hasOverflowClip()) return right; for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { - for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { - if (cell->isTableCell()) - right = max(right, static_cast<RenderTableCell*>(cell)->x() + cell->rightmostPosition(false)); + for (RenderObject* curr = row->firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isTableCell()) { + RenderTableCell* cell = static_cast<RenderTableCell*>(curr); + right = max(right, cell->x() + cell->rightmostPosition(false)); + } } } @@ -734,14 +777,16 @@ int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool inc int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const { - int left = RenderContainer::leftmostPosition(includeOverflowInterior, includeSelf); + int left = RenderBox::leftmostPosition(includeOverflowInterior, includeSelf); if (!includeOverflowInterior && hasOverflowClip()) return left; for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { - for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { - if (cell->isTableCell()) - left = min(left, static_cast<RenderTableCell*>(cell)->x() + cell->leftmostPosition(false)); + for (RenderObject* curr = row->firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isTableCell()) { + RenderTableCell* cell = static_cast<RenderTableCell*>(curr); + left = min(left, cell->x() + cell->leftmostPosition(false)); + } } } @@ -957,7 +1002,7 @@ void RenderTableSection::recalcOuterBorder() m_outerBorderRight = calcOuterBorderRight(rtl); } -int RenderTableSection::getBaselineOfFirstLineBox() const +int RenderTableSection::firstLineBoxBaseline() const { if (!m_gridRows) return -1; @@ -994,8 +1039,20 @@ void RenderTableSection::paint(PaintInfo& paintInfo, int tx, int ty) tx += x(); ty += y(); + PaintPhase phase = paintInfo.phase; + bool pushedClip = pushContentsClip(paintInfo, tx, ty); + paintObject(paintInfo, tx, ty); + if (pushedClip) + popContentsClip(paintInfo, phase, tx, ty); +} + +void RenderTableSection::paintObject(PaintInfo& paintInfo, int tx, int ty) +{ // Check which rows and cols are visible and only paint these. // FIXME: Could use a binary search here. + unsigned totalRows = m_gridRows; + unsigned totalCols = table()->columns().size(); + PaintPhase paintPhase = paintInfo.phase; int x = paintInfo.rect.x(); int y = paintInfo.rect.y(); @@ -1102,10 +1159,10 @@ void RenderTableSection::paint(PaintInfo& paintInfo, int tx, int ty) // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for // painting the row background for the cell. - if (!row->hasLayer()) + if (!row->hasSelfPaintingLayer()) cell->paintBackgroundsBehindCell(paintInfo, tx, ty, row); } - if ((!cell->hasLayer() && !row->hasLayer()) || paintInfo.phase == PaintPhaseCollapsedTableBorders) + if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()) || paintInfo.phase == PaintPhaseCollapsedTableBorders) cell->paint(paintInfo, tx, ty); } } @@ -1190,12 +1247,6 @@ void RenderTableSection::splitColumn(int pos, int newSize) } } -RenderObject* RenderTableSection::removeChildNode(RenderObject* child, bool fullRemove) -{ - setNeedsCellRecalc(); - return RenderContainer::removeChildNode(child, fullRemove); -} - // Hit Testing bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action) { @@ -1204,12 +1255,15 @@ bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResul tx += x(); ty += y(); + if (hasOverflowClip() && !overflowClipRect(tx, ty).contains(xPos, yPos)) + return false; + for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { // FIXME: We have to skip over inline flows, since they can show up inside table rows // at the moment (a demoted inline <form> for example). If we ever implement a // table-specific hit-test method (which we should do for performance reasons anyway), // then we can remove this check. - if (!child->hasLayer() && !child->isRenderInline() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) { + if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) { updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); return true; } diff --git a/WebCore/rendering/RenderTableSection.h b/WebCore/rendering/RenderTableSection.h index 71839d1..5c57714 100644 --- a/WebCore/rendering/RenderTableSection.h +++ b/WebCore/rendering/RenderTableSection.h @@ -35,20 +35,28 @@ namespace WebCore { class RenderTableCell; class RenderTableRow; -class RenderTableSection : public RenderContainer { +class RenderTableSection : public RenderBox { public: RenderTableSection(Node*); ~RenderTableSection(); + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + virtual const char* renderName() const { return isAnonymous() ? "RenderTableSection (anonymous)" : "RenderTableSection"; } virtual bool isTableSection() const { return true; } virtual void destroy(); + virtual void layout(); + virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject* oldChild); - virtual int getBaselineOfFirstLineBox() const; + virtual int firstLineBoxBaseline() const; void addCell(RenderTableCell*, RenderTableRow* row); @@ -99,6 +107,8 @@ public: int outerBorderRight() const { return m_outerBorderRight; } virtual void paint(PaintInfo&, int tx, int ty); + virtual void paintObject(PaintInfo&, int tx, int ty); + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); int numRows() const { return m_gridRows; } @@ -119,17 +129,16 @@ public: int getBaseline(int row) { return m_grid[row].baseline; } - virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); private: virtual int lineHeight(bool, bool) const { return 0; } - virtual void position(InlineBox*) { } bool ensureRows(int); void clearGrid(); + RenderObjectChildList m_children; + Vector<RowStruct> m_grid; int m_gridRows; Vector<int> m_rowPos; diff --git a/WebCore/rendering/RenderText.cpp b/WebCore/rendering/RenderText.cpp index 084be71..2dfdb27 100644 --- a/WebCore/rendering/RenderText.cpp +++ b/WebCore/rendering/RenderText.cpp @@ -36,6 +36,7 @@ #include "RenderView.h" #include "Text.h" #include "TextBreakIterator.h" +#include "VisiblePosition.h" #include "break_lines.h" #include <wtf/AlwaysInline.h> @@ -60,7 +61,6 @@ RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str) , m_maxWidth(-1) , m_beginMinWidth(0) , m_endMinWidth(0) - , m_selectionState(SelectionNone) , m_hasTab(false) , m_linesDirty(false) , m_containsReversedText(false) @@ -98,13 +98,13 @@ bool RenderText::isWordBreak() const return false; } -void RenderText::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { // There is no need to ever schedule repaints from a style change of a text run, since // we already did this for the parent of the text run. // We do have to schedule layouts, though, since a style change can force us to // need to relayout. - if (diff == RenderStyle::Layout) + if (diff == StyleDifferenceLayout) setNeedsLayoutAndPrefWidthsRecalc(); ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE; @@ -200,17 +200,17 @@ void RenderText::deleteTextBoxes() PassRefPtr<StringImpl> RenderText::originalText() const { - Node* e = element(); + Node* e = node(); return e ? static_cast<Text*>(e)->string() : 0; } void RenderText::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool) { for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - rects.append(IntRect(tx + box->xPos(), ty + box->yPos(), box->width(), box->height())); + rects.append(IntRect(tx + box->x(), ty + box->y(), box->width(), box->height())); } -void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight) +void RenderText::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight) { // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this @@ -227,7 +227,7 @@ void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigne for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { // Note: box->end() returns the index of the last character, not the index past it if (start <= box->start() && box->end() < end) { - IntRect r = IntRect(absPos.x() + box->xPos(), absPos.y() + box->yPos(), box->width(), box->height()); + IntRect r = IntRect(absPos.x() + box->x(), absPos.y() + box->y(), box->width(), box->height()); if (useSelectionHeight) { IntRect selectionRect = box->selectionRect(absPos.x(), absPos.y(), start, end); r.setHeight(selectionRect.height()); @@ -241,7 +241,7 @@ void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigne if (!useSelectionHeight) { // change the height and y position because selectionRect uses selection-specific values r.setHeight(box->height()); - r.setY(absPos.y() + box->yPos()); + r.setY(absPos.y() + box->y()); } rects.append(r); } @@ -252,10 +252,10 @@ void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigne void RenderText::absoluteQuads(Vector<FloatQuad>& quads, bool) { for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - quads.append(localToAbsoluteQuad(FloatRect(box->xPos(), box->yPos(), box->width(), box->height()))); + quads.append(localToAbsoluteQuad(FloatRect(box->x(), box->y(), box->width(), box->height()))); } -void RenderText::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight) +void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight) { // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this @@ -270,7 +270,7 @@ void RenderText::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { // Note: box->end() returns the index of the last character, not the index past it if (start <= box->start() && box->end() < end) { - IntRect r = IntRect(box->xPos(), box->yPos(), box->width(), box->height()); + IntRect r = IntRect(box->x(), box->y(), box->width(), box->height()); if (useSelectionHeight) { IntRect selectionRect = box->selectionRect(0, 0, start, end); r.setHeight(selectionRect.height()); @@ -284,7 +284,7 @@ void RenderText::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned if (!useSelectionHeight) { // change the height and y position because selectionRect uses selection-specific values r.setHeight(box->height()); - r.setY(box->yPos()); + r.setY(box->y()); } quads.append(localToAbsoluteQuad(FloatRect(r))); } @@ -313,61 +313,61 @@ InlineTextBox* RenderText::findNextInlineTextBox(int offset, int& pos) const return s; } -VisiblePosition RenderText::positionForCoordinates(int x, int y) +VisiblePosition RenderText::positionForPoint(const IntPoint& point) { if (!firstTextBox() || textLength() == 0) - return VisiblePosition(element(), 0, DOWNSTREAM); + return createVisiblePosition(0, DOWNSTREAM); // Get the offset for the position, since this will take rtl text into account. int offset; // FIXME: We should be able to roll these special cases into the general cases in the loop below. - if (firstTextBox() && y < firstTextBox()->root()->bottomOverflow() && x < firstTextBox()->m_x) { + if (firstTextBox() && point.y() < firstTextBox()->root()->bottomOverflow() && point.x() < firstTextBox()->m_x) { // at the y coordinate of the first line or above // and the x coordinate is to the left of the first text box left edge - offset = firstTextBox()->offsetForPosition(x); - return VisiblePosition(element(), offset + firstTextBox()->start(), DOWNSTREAM); + offset = firstTextBox()->offsetForPosition(point.x()); + return createVisiblePosition(offset + firstTextBox()->start(), DOWNSTREAM); } - if (lastTextBox() && y >= lastTextBox()->root()->topOverflow() && x >= lastTextBox()->m_x + lastTextBox()->m_width) { + if (lastTextBox() && point.y() >= lastTextBox()->root()->topOverflow() && point.x() >= lastTextBox()->m_x + lastTextBox()->m_width) { // 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(x); - return VisiblePosition(element(), offset + lastTextBox()->start(), DOWNSTREAM); + offset = lastTextBox()->offsetForPosition(point.x()); + return createVisiblePosition(offset + lastTextBox()->start(), DOWNSTREAM); } InlineTextBox* lastBoxAbove = 0; for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { - if (y >= box->root()->topOverflow()) { + if (point.y() >= box->root()->topOverflow()) { int bottom = box->root()->nextRootBox() ? box->root()->nextRootBox()->topOverflow() : box->root()->bottomOverflow(); - if (y < bottom) { - offset = box->offsetForPosition(x); + if (point.y() < bottom) { + offset = box->offsetForPosition(point.x()); - if (x == box->m_x) + if (point.x() == box->m_x) // the x coordinate is equal to the left edge of this box // the affinity must be downstream so the position doesn't jump back to the previous line - return VisiblePosition(element(), offset + box->start(), DOWNSTREAM); + return createVisiblePosition(offset + box->start(), DOWNSTREAM); - if (x < box->m_x + box->m_width) + if (point.x() < box->m_x + box->m_width) // and the x coordinate is to the left of the right edge of this box // check to see if position goes in this box - return VisiblePosition(element(), offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); + return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); - if (!box->prevOnLine() && x < box->m_x) + if (!box->prevOnLine() && point.x() < box->m_x) // box is first on line // and the x coordinate is to the left of the first text box left edge - return VisiblePosition(element(), offset + box->start(), DOWNSTREAM); + return createVisiblePosition(offset + box->start(), DOWNSTREAM); if (!box->nextOnLine()) // box is last on line // and the x coordinate is to the right of the last text box right edge // generate VisiblePosition, use UPSTREAM affinity if possible - return VisiblePosition(element(), offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); + return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); } lastBoxAbove = box; } } - return VisiblePosition(element(), lastBoxAbove ? lastBoxAbove->start() + lastBoxAbove->len() : 0, DOWNSTREAM); + return createVisiblePosition(lastBoxAbove ? lastBoxAbove->start() + lastBoxAbove->len() : 0, DOWNSTREAM); } IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine) @@ -386,7 +386,7 @@ IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* e int left = box->positionForOffset(caretOffset); - int rootLeft = box->root()->xPos(); + int rootLeft = box->root()->x(); // FIXME: should we use the width of the root inline box or the // width of the containing block for this? if (extraWidthToEndOfLine) @@ -394,14 +394,13 @@ IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* e RenderBlock* cb = containingBlock(); if (style()->autoWrap()) { - int availableWidth = cb->lineWidth(top); + int availableWidth = cb->lineWidth(top, false); if (box->direction() == LTR) left = min(left, rootLeft + availableWidth - 1); else left = max(left, rootLeft); } - const int caretWidth = 1; return IntRect(left, top, caretWidth, height); } @@ -709,7 +708,7 @@ void RenderText::calcPrefWidths(int leadWidth) } } - if (needsWordSpacing && len > 1 || ignoringSpaces && !firstWord) + if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord)) currMaxWidth += wordSpacing; m_minWidth = max(currMinWidth, m_minWidth); @@ -750,7 +749,7 @@ void RenderText::setSelectionState(SelectionState state) { InlineTextBox* box; - m_selectionState = state; + RenderObject::setSelectionState(state); if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) { int startPos, endPos; selectionStartEnd(startPos, endPos); @@ -968,7 +967,7 @@ int RenderText::lineHeight(bool firstLine, bool) const return parent()->lineHeight(firstLine, true); } -void RenderText::dirtyLineBoxes(bool fullLayout, bool) +void RenderText::dirtyLineBoxes(bool fullLayout) { if (fullLayout) deleteTextBoxes(); @@ -979,16 +978,14 @@ void RenderText::dirtyLineBoxes(bool fullLayout, bool) m_linesDirty = false; } -InlineTextBox* RenderText::createInlineTextBox() +InlineTextBox* RenderText::createTextBox() { return new (renderArena()) InlineTextBox(this); } -InlineBox* RenderText::createInlineBox(bool, bool unusedIsRootLineBox, bool) +InlineTextBox* RenderText::createInlineTextBox() { - ASSERT_UNUSED(unusedIsRootLineBox, !unusedIsRootLineBox); - - InlineTextBox* textBox = createInlineTextBox(); + InlineTextBox* textBox = createTextBox(); if (!m_firstTextBox) m_firstTextBox = m_lastTextBox = textBox; else { @@ -999,7 +996,7 @@ InlineBox* RenderText::createInlineBox(bool, bool unusedIsRootLineBox, bool) return textBox; } -void RenderText::position(InlineBox* box) +void RenderText::positionLineBox(InlineBox* box) { InlineTextBox* s = static_cast<InlineTextBox*>(box); @@ -1015,7 +1012,7 @@ void RenderText::position(InlineBox* box) m_containsReversedText |= s->direction() == RTL; } -unsigned int RenderText::width(unsigned int from, unsigned int len, int xPos, bool firstLine) const +unsigned RenderText::width(unsigned from, unsigned len, int xPos, bool firstLine) const { if (from >= textLength()) return 0; @@ -1026,14 +1023,12 @@ unsigned int RenderText::width(unsigned int from, unsigned int len, int xPos, bo return width(from, len, style(firstLine)->font(), xPos); } -unsigned int RenderText::width(unsigned int from, unsigned int len, const Font& f, int xPos) const +unsigned RenderText::width(unsigned from, unsigned len, const Font& f, int xPos) const { - if (!characters() || from > textLength()) + ASSERT(from + len <= textLength()); + if (!characters()) return 0; - if (from + len > textLength()) - len = textLength() - from; - int w; if (&f == &style()->font()) { if (!style()->preserveNewline() && !from && len == textLength()) @@ -1056,36 +1051,35 @@ IntRect RenderText::linesBoundingBox() const int leftSide = 0; int rightSide = 0; for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { - if (curr == firstTextBox() || curr->xPos() < leftSide) - leftSide = curr->xPos(); - if (curr == firstTextBox() || curr->xPos() + curr->width() > rightSide) - rightSide = curr->xPos() + curr->width(); + if (curr == firstTextBox() || curr->x() < leftSide) + leftSide = curr->x(); + if (curr == firstTextBox() || curr->x() + curr->width() > rightSide) + rightSide = curr->x() + curr->width(); } result.setWidth(rightSide - leftSide); result.setX(leftSide); - result.setHeight(lastTextBox()->yPos() + lastTextBox()->height() - firstTextBox()->yPos()); - result.setY(firstTextBox()->yPos()); + result.setHeight(lastTextBox()->y() + lastTextBox()->height() - firstTextBox()->y()); + result.setY(firstTextBox()->y()); } return result; } -IntRect RenderText::clippedOverflowRectForRepaint(RenderBox* repaintContainer) +IntRect RenderText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { RenderObject* cb = containingBlock(); return cb->clippedOverflowRectForRepaint(repaintContainer); } -IntRect RenderText::selectionRect(bool clipToVisibleContent) +IntRect RenderText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent) { ASSERT(!needsLayout()); - IntRect rect; if (selectionState() == SelectionNone) - return rect; + return IntRect(); RenderBlock* cb = containingBlock(); if (!cb) - return rect; + return IntRect(); // Now calculate startPos and endPos for painting selection. // We include a selection while endPos > 0 @@ -1103,31 +1097,24 @@ IntRect RenderText::selectionRect(bool clipToVisibleContent) } if (startPos == endPos) - return rect; + return IntRect(); + IntRect rect; for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) rect.unite(box->selectionRect(0, 0, startPos, endPos)); if (clipToVisibleContent) - computeAbsoluteRepaintRect(rect); + computeRectForRepaint(repaintContainer, rect); else { if (cb->hasColumns()) cb->adjustRectForColumns(rect); - // FIXME: This doesn't work correctly with transforms. - FloatPoint absPos = localToAbsolute(); - rect.move(absPos.x(), absPos.y()); + + rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox(); } return rect; } -int RenderText::verticalPositionHint(bool firstLine) const -{ - if (parent()->isReplaced()) - return 0; // Treat inline blocks just like blocks. There can't be any vertical position hint. - return parent()->verticalPositionHint(firstLine); -} - int RenderText::caretMinOffset() const { InlineTextBox* box = firstTextBox(); @@ -1161,7 +1148,7 @@ unsigned RenderText::caretMaxRenderedOffset() const int RenderText::previousOffset(int current) const { StringImpl* si = m_text.get(); - TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length()); + TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length()); if (!iterator) return current - 1; @@ -1169,13 +1156,122 @@ int RenderText::previousOffset(int current) const if (result == TextBreakDone) result = current - 1; +#ifdef BUILDING_ON_TIGER + // ICU 3.2 allows character breaks before a half-width Katakana voiced mark. + if (static_cast<unsigned>(result) < si->length()) { + UChar character = (*si)[result]; + if (character == 0xFF9E || character == 0xFF9F) + --result; + } +#endif + return result; } +#define HANGUL_CHOSEONG_START (0x1100) +#define HANGUL_CHOSEONG_END (0x115F) +#define HANGUL_JUNGSEONG_START (0x1160) +#define HANGUL_JUNGSEONG_END (0x11A2) +#define HANGUL_JONGSEONG_START (0x11A8) +#define HANGUL_JONGSEONG_END (0x11F9) +#define HANGUL_SYLLABLE_START (0xAC00) +#define HANGUL_SYLLABLE_END (0xD7AF) +#define HANGUL_JONGSEONG_COUNT (28) + +enum HangulState { + HangulStateL, + HangulStateV, + HangulStateT, + HangulStateLV, + HangulStateLVT, + HangulStateBreak +}; + +inline bool isHangulLVT(UChar32 character) +{ + return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT; +} + +int RenderText::previousOffsetForBackwardDeletion(int current) const +{ +#if PLATFORM(MAC) + UChar32 character; + while (current > 0) { + if (U16_IS_TRAIL((*m_text)[--current])) + --current; + if (current < 0) + break; + + UChar32 character = m_text->characterStartingAt(current); + + // We don't combine characters in Armenian ... Limbu range for backward deletion. + if ((character >= 0x0530) && (character < 0x1950)) + break; + + if (u_isbase(character) && (character != 0xFF9E) && (character != 0xFF9F)) + break; + } + + if (current <= 0) + return current; + + // Hangul + character = m_text->characterStartingAt(current); + if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) { + HangulState state; + HangulState initialState; + + if (character < HANGUL_JUNGSEONG_START) + state = HangulStateL; + else if (character < HANGUL_JONGSEONG_START) + state = HangulStateV; + else if (character < HANGUL_SYLLABLE_START) + state = HangulStateT; + else + state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV; + + initialState = state; + + while (current > 0 && ((character = m_text->characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) { + switch (state) { + case HangulStateV: + if (character <= HANGUL_CHOSEONG_END) + state = HangulStateL; + else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character)) + state = HangulStateLV; + else if (character > HANGUL_JUNGSEONG_END) + state = HangulStateBreak; + break; + case HangulStateT: + if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END)) + state = HangulStateV; + else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END)) + state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV); + else if (character < HANGUL_JUNGSEONG_START) + state = HangulStateBreak; + break; + default: + state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak; + break; + } + if (state == HangulStateBreak) + break; + + --current; + } + } + + return current; +#else + // Platforms other than Mac delete by one code point. + return current - 1; +#endif +} + int RenderText::nextOffset(int current) const { StringImpl* si = m_text.get(); - TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length()); + TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length()); if (!iterator) return current + 1; @@ -1183,6 +1279,15 @@ int RenderText::nextOffset(int current) const if (result == TextBreakDone) result = current + 1; +#ifdef BUILDING_ON_TIGER + // ICU 3.2 allows character breaks before a half-width Katakana voiced mark. + if (static_cast<unsigned>(result) < si->length()) { + UChar character = (*si)[result]; + if (character == 0xFF9E || character == 0xFF9F) + ++result; + } +#endif + return result; } diff --git a/WebCore/rendering/RenderText.h b/WebCore/rendering/RenderText.h index 6a09605..649f155 100644 --- a/WebCore/rendering/RenderText.h +++ b/WebCore/rendering/RenderText.h @@ -52,21 +52,20 @@ public: StringImpl* text() const { return m_text.get(); } - virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun = false); - virtual InlineTextBox* createInlineTextBox(); - virtual void dirtyLineBoxes(bool fullLayout, bool isRootInlineBox = false); + InlineTextBox* createInlineTextBox(); + void dirtyLineBoxes(bool fullLayout); virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); - virtual void addLineBoxRects(Vector<IntRect>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); + virtual void absoluteRectsForRange(Vector<IntRect>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); - virtual void collectAbsoluteLineBoxQuads(Vector<FloatQuad>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); + virtual void absoluteQuadsForRange(Vector<FloatQuad>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); - virtual VisiblePosition positionForCoordinates(int x, int y); + virtual VisiblePosition positionForPoint(const IntPoint&); const UChar* characters() const { return m_text->characters(); } unsigned textLength() const { return m_text->length(); } // non virtual implementation of length() - virtual void position(InlineBox*); + void positionLineBox(InlineBox*); virtual unsigned width(unsigned from, unsigned len, const Font&, int xPos) const; virtual unsigned width(unsigned from, unsigned len, int xPos, bool firstLine = false) const; @@ -88,21 +87,18 @@ public: int firstRunX() const; int firstRunY() const; - virtual int verticalPositionHint(bool firstLine) const; - void setText(PassRefPtr<StringImpl>, bool force = false); void setTextWithOffset(PassRefPtr<StringImpl>, unsigned offset, unsigned len, bool force = false); virtual bool canBeSelectionLeaf() const { return true; } - virtual SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState); } virtual void setSelectionState(SelectionState s); - virtual IntRect selectionRect(bool clipToVisibleContent = true); + virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent = true); virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); virtual int marginLeft() const { return style()->marginLeft().calcMinValue(0); } virtual int marginRight() const { return style()->marginRight().calcMinValue(0); } - virtual IntRect clippedOverflowRectForRepaint(RenderBox* repaintContainer); + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); InlineTextBox* firstTextBox() const { return m_firstTextBox; } InlineTextBox* lastTextBox() const { return m_lastTextBox; } @@ -112,6 +108,7 @@ public: virtual unsigned caretMaxRenderedOffset() const; virtual int previousOffset(int current) const; + virtual int previousOffsetForBackwardDeletion(int current) const; virtual int nextOffset(int current) const; bool containsReversedText() const { return m_containsReversedText; } @@ -122,13 +119,16 @@ public: void checkConsistency() const; + virtual void calcPrefWidths(int leadWidth); + protected: - virtual void styleWillChange(RenderStyle::Diff, const RenderStyle*) { } - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleWillChange(StyleDifference, const RenderStyle*) { } + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); virtual void setTextInternal(PassRefPtr<StringImpl>); - virtual void calcPrefWidths(int leadWidth); virtual UChar previousCharacter(); + + virtual InlineTextBox* createTextBox(); // Subclassed by SVG. private: // Make length() private so that callers that have a RenderText* @@ -155,7 +155,6 @@ private: int m_beginMinWidth; int m_endMinWidth; - unsigned m_selectionState : 3; // enums on Windows are signed, so this needs to be unsigned to prevent it turning negative. bool m_hasBreakableChar : 1; // Whether or not we can be broken into multiple lines. bool m_hasBreak : 1; // Whether or not we have a hard break (e.g., <pre> with '\n'). bool m_hasTab : 1; // Whether or not we have a variable width tab character (e.g., <pre> with '\t'). @@ -181,6 +180,9 @@ inline const RenderText* toRenderText(const RenderObject* o) return static_cast<const RenderText*>(o); } +// This will catch anyone doing an unnecessary cast. +void toRenderText(const RenderText*); + #ifdef NDEBUG inline void RenderText::checkConsistency() const { diff --git a/WebCore/rendering/RenderTextControl.cpp b/WebCore/rendering/RenderTextControl.cpp index 642ef1f..38d689b 100644 --- a/WebCore/rendering/RenderTextControl.cpp +++ b/WebCore/rendering/RenderTextControl.cpp @@ -31,17 +31,14 @@ #include "HTMLFormControlElement.h" #include "HTMLNames.h" #include "HitTestResult.h" +#include "RenderLayer.h" #include "RenderText.h" #include "ScrollbarTheme.h" #include "SelectionController.h" -#include "TextControlInnerElements.h" #include "Text.h" +#include "TextControlInnerElements.h" #include "TextIterator.h" -#ifdef ANDROID_LAYOUT -#include "FrameView.h" -#endif - using namespace std; namespace WebCore { @@ -84,12 +81,12 @@ RenderTextControl::~RenderTextControl() m_innerText->detach(); } -void RenderTextControl::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); if (m_innerText) { - RenderBlock* textBlockRenderer = static_cast<RenderBlock*>(m_innerText->renderer()); + RenderBlock* textBlockRenderer = toRenderBlock(m_innerText->renderer()); RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(style()); // We may have set the width and the height in the old style in layout(). // Reset them now to avoid getting a spurious layout hint. @@ -102,18 +99,34 @@ void RenderTextControl::styleDidChange(RenderStyle::Diff diff, const RenderStyle } } - setHasOverflowClip(false); setReplaced(isInline()); } +static inline bool updateUserModifyProperty(Node* node, RenderStyle* style) +{ + bool isEnabled = true; + bool isReadOnlyControl = false; + + if (node->isElementNode()) { + FormControlElement* formControlElement = toFormControlElement(static_cast<Element*>(node)); + ASSERT(formControlElement); + + isEnabled = formControlElement->isEnabled(); + isReadOnlyControl = formControlElement->isReadOnlyControl(); + } + + style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY); + return !isEnabled; +} + void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const { // The inner block, if present, always has its direction set to LTR, // so we need to inherit the direction from the element. textBlockStyle->setDirection(style()->direction()); - textBlockStyle->setUserModify((node()->isReadOnlyControl() || !node()->isEnabled()) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY); - if (!node()->isEnabled()) + bool disabled = updateUserModifyProperty(node(), textBlockStyle); + if (disabled) textBlockStyle->setColor(disabledTextColor(textBlockStyle->color(), startStyle->backgroundColor())); } @@ -142,7 +155,7 @@ int RenderTextControl::textBlockWidth() const void RenderTextControl::updateFromElement() { - m_innerText->renderer()->style()->setUserModify((node()->isReadOnlyControl() || !node()->isEnabled()) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY); + updateUserModifyProperty(node(), m_innerText->renderer()->style()); } void RenderTextControl::setInnerTextValue(const String& innerTextValue) @@ -236,7 +249,7 @@ void RenderTextControl::setSelectionRange(int start, int end) ASSERT(startPosition.isNotNull() && endPosition.isNotNull()); ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node() && endPosition.deepEquivalent().node()->shadowAncestorNode() == node()); - Selection newSelection = Selection(startPosition, endPosition); + VisibleSelection newSelection = VisibleSelection(startPosition, endPosition); if (Frame* frame = document()->frame()) frame->selection()->setSelection(newSelection); @@ -247,10 +260,10 @@ void RenderTextControl::setSelectionRange(int start, int end) frame->setSelectionGranularity(CharacterGranularity); } -Selection RenderTextControl::selection(int start, int end) const +VisibleSelection RenderTextControl::selection(int start, int end) const { - return Selection(VisiblePosition(m_innerText.get(), start, VP_DEFAULT_AFFINITY), - VisiblePosition(m_innerText.get(), end, VP_DEFAULT_AFFINITY)); + return VisibleSelection(VisiblePosition(m_innerText.get(), start, VP_DEFAULT_AFFINITY), + VisiblePosition(m_innerText.get(), end, VP_DEFAULT_AFFINITY)); } VisiblePosition RenderTextControl::visiblePositionForIndex(int index) @@ -279,7 +292,7 @@ int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos) RefPtr<Range> range = Range::create(document()); range->setStart(m_innerText.get(), 0, ec); ASSERT(!ec); - range->setEnd(indexPosition.node(), indexPosition.offset(), ec); + range->setEnd(indexPosition.node(), indexPosition.m_offset, ec); ASSERT(!ec); return TextIterator::rangeLength(range.get()); } @@ -367,7 +380,7 @@ String RenderTextControl::textWithHardLineBreaks() if (!renderer) return ""; - InlineBox* box = renderer->isText() ? toRenderText(renderer)->firstTextBox() : renderer->inlineBoxWrapper(); + InlineBox* box = renderer->isText() ? toRenderText(renderer)->firstTextBox() : toRenderBox(renderer)->inlineBoxWrapper(); if (!box) return ""; @@ -434,15 +447,16 @@ void RenderTextControl::calcHeight() setHeight(height() + paddingTop() + paddingBottom() + borderTop() + borderBottom()); // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap. - if (m_innerText->renderer()->style()->overflowX() == OSCROLL || (m_innerText->renderer()->style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap)) + if (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap)) setHeight(height() + scrollbarThickness()); RenderBlock::calcHeight(); } -void RenderTextControl::hitInnerTextBlock(HitTestResult& result, int xPos, int yPos, int tx, int ty) +void RenderTextControl::hitInnerTextElement(HitTestResult& result, int xPos, int yPos, int tx, int ty) { result.setInnerNode(m_innerText.get()); + result.setInnerNonSharedNode(m_innerText.get()); result.setLocalPoint(IntPoint(xPos - tx - x() - m_innerText->renderBox()->x(), yPos - ty - y() - m_innerText->renderBox()->y())); } @@ -517,7 +531,7 @@ void RenderTextControl::selectionChanged(bool userTriggered) if (Frame* frame = document()->frame()) { if (frame->selection()->isRange() && userTriggered) - static_cast<EventTargetNode*>(node())->dispatchEventForType(eventNames().selectEvent, true, false); + node()->dispatchEventForType(eventNames().selectEvent, true, false); } } @@ -526,61 +540,6 @@ void RenderTextControl::addFocusRingRects(GraphicsContext* graphicsContext, int graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); } -void RenderTextControl::autoscroll() -{ - RenderLayer* layer = m_innerText->renderBox()->layer(); - if (layer) - layer->autoscroll(); -} - -int RenderTextControl::scrollWidth() const -{ - if (m_innerText) - return m_innerText->scrollWidth(); - return RenderBlock::scrollWidth(); -} - -int RenderTextControl::scrollHeight() const -{ - if (m_innerText) - return m_innerText->scrollHeight(); - return RenderBlock::scrollHeight(); -} - -int RenderTextControl::scrollLeft() const -{ - if (m_innerText) - return m_innerText->scrollLeft(); - return RenderBlock::scrollLeft(); -} - -int RenderTextControl::scrollTop() const -{ - if (m_innerText) - return m_innerText->scrollTop(); - return RenderBlock::scrollTop(); -} - -void RenderTextControl::setScrollLeft(int newLeft) -{ - if (m_innerText) - m_innerText->setScrollLeft(newLeft); -} - -void RenderTextControl::setScrollTop(int newTop) -{ - if (m_innerText) - m_innerText->setScrollTop(newTop); -} - -bool RenderTextControl::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) -{ - RenderLayer* layer = m_innerText->renderBox()->layer(); - if (layer && layer->scroll(direction, granularity, multiplier)) - return true; - return RenderBlock::scroll(direction, granularity, multiplier); -} - HTMLElement* RenderTextControl::innerTextElement() const { return m_innerText.get(); diff --git a/WebCore/rendering/RenderTextControl.h b/WebCore/rendering/RenderTextControl.h index 86d3f8a..e4f7747 100644 --- a/WebCore/rendering/RenderTextControl.h +++ b/WebCore/rendering/RenderTextControl.h @@ -27,7 +27,7 @@ namespace WebCore { class FormControlElement; -class Selection; +class VisibleSelection; class TextControlInnerElement; class TextControlInnerTextElement; @@ -36,6 +36,7 @@ public: virtual ~RenderTextControl(); virtual const char* renderName() const { return "RenderTextControl"; } + virtual bool isTextControl() const { return true; } virtual bool hasControlClip() const { return false; } virtual IntRect controlClipRect(int tx, int ty) const; virtual void calcHeight(); @@ -45,8 +46,8 @@ public: virtual bool canHaveChildren() const { return false; } virtual bool avoidsFloats() const { return true; } - virtual bool isEdited() const { return m_edited; } - virtual void setEdited(bool isEdited) { m_edited = isEdited; } + bool isEdited() const { return m_edited; } + void setEdited(bool isEdited) { m_edited = isEdited; } bool isUserEdited() const { return m_userEdited; } void setUserEdited(bool isUserEdited); @@ -57,7 +58,7 @@ public: void setSelectionEnd(int); void select(); void setSelectionRange(int start, int end); - Selection selection(int start, int end) const; + VisibleSelection selection(int start, int end) const; virtual void subtreeHasChanged(); String text(); @@ -67,16 +68,6 @@ public: virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); virtual bool canBeProgramaticallyScrolled(bool) const { return true; } - virtual void autoscroll(); - - // Subclassed to forward to our inner div. - virtual int scrollLeft() const; - virtual int scrollTop() const; - virtual int scrollWidth() const; - virtual int scrollHeight() const; - virtual void setScrollLeft(int); - virtual void setScrollTop(int); - virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); VisiblePosition visiblePositionForIndex(int index); int indexForVisiblePosition(const VisiblePosition&); @@ -88,10 +79,10 @@ protected: void adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const; void setInnerTextValue(const String&); - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); void createSubtreeIfNeeded(TextControlInnerElement* innerBlock); - void hitInnerTextBlock(HitTestResult&, int x, int y, int tx, int ty); + void hitInnerTextElement(HitTestResult&, int x, int y, int tx, int ty); void forwardEvent(Event*); int textBlockWidth() const; @@ -115,6 +106,21 @@ private: RefPtr<TextControlInnerTextElement> m_innerText; }; +inline RenderTextControl* toRenderTextControl(RenderObject* o) +{ + ASSERT(!o || o->isTextControl()); + return static_cast<RenderTextControl*>(o); +} + +inline const RenderTextControl* toRenderTextControl(const RenderObject* o) +{ + ASSERT(!o || o->isTextControl()); + return static_cast<const RenderTextControl*>(o); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderTextControl(const RenderTextControl*); + } // namespace WebCore #endif // RenderTextControl_h diff --git a/WebCore/rendering/RenderTextControlMultiLine.cpp b/WebCore/rendering/RenderTextControlMultiLine.cpp index 253f53f..271fb12 100644 --- a/WebCore/rendering/RenderTextControlMultiLine.cpp +++ b/WebCore/rendering/RenderTextControlMultiLine.cpp @@ -55,54 +55,15 @@ void RenderTextControlMultiLine::subtreeHasChanged() frame->textDidChangeInTextArea(static_cast<Element*>(node())); } -void RenderTextControlMultiLine::layout() -{ - int oldHeight = height(); - calcHeight(); - -#ifdef ANDROID_LAYOUT - int oldVisibleWidth = m_visibleWidth; -#endif - - int oldWidth = width(); - calcWidth(); - - bool relayoutChildren = oldHeight != height() || oldWidth != width(); -#ifdef ANDROID_LAYOUT - if (oldVisibleWidth != m_visibleWidth - && document()->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) { - relayoutChildren = true; - } -#endif - - RenderBox* innerTextRenderer = innerTextElement()->renderBox(); - - // Set the text block height - int desiredHeight = textBlockHeight(); - if (desiredHeight != innerTextRenderer->height()) - relayoutChildren = true; - innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed)); - - // Set the text block width - int desiredWidth = textBlockWidth(); - if (desiredWidth != innerTextRenderer->width()) - relayoutChildren = true; - innerTextRenderer->style()->setWidth(Length(desiredWidth, Fixed)); - - RenderBlock::layoutBlock(relayoutChildren); -} - bool RenderTextControlMultiLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) { if (!RenderTextControl::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction)) return false; - if (result.innerNode() == element()) { - hitInnerTextBlock(result, x, y, tx, ty); - return true; - } + if (result.innerNode() == node() || result.innerNode() == innerTextElement()) + hitInnerTextElement(result, x, y, tx, ty); - return false; + return true; } void RenderTextControlMultiLine::forwardEvent(Event* event) @@ -146,10 +107,9 @@ PassRefPtr<RenderStyle> RenderTextControlMultiLine::createInnerTextStyle(const R adjustInnerTextStyle(startStyle, textBlockStyle.get()); - // Forward overflow properties. - textBlockStyle->setOverflowX(startStyle->overflowX() == OVISIBLE ? OAUTO : startStyle->overflowX()); - textBlockStyle->setOverflowY(startStyle->overflowY() == OVISIBLE ? OAUTO : startStyle->overflowY()); - + // FIXME: This code should just map wrap into CSS in the DOM code. + // Then here we should set the textBlockStyle appropriately based off this + // object's style()->whiteSpace() and style->wordWrap(). // Set word wrap property based on wrap attribute. if (static_cast<HTMLTextAreaElement*>(node())->shouldWrapText()) { textBlockStyle->setWhiteSpace(PRE_WRAP); diff --git a/WebCore/rendering/RenderTextControlMultiLine.h b/WebCore/rendering/RenderTextControlMultiLine.h index 591a65d..579047d 100644 --- a/WebCore/rendering/RenderTextControlMultiLine.h +++ b/WebCore/rendering/RenderTextControlMultiLine.h @@ -33,7 +33,6 @@ public: virtual bool isTextArea() const { return true; } virtual void subtreeHasChanged(); - virtual void layout(); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); void forwardEvent(Event*); diff --git a/WebCore/rendering/RenderTextControlSingleLine.cpp b/WebCore/rendering/RenderTextControlSingleLine.cpp index fccea00..dd06501 100644 --- a/WebCore/rendering/RenderTextControlSingleLine.cpp +++ b/WebCore/rendering/RenderTextControlSingleLine.cpp @@ -174,8 +174,8 @@ void RenderTextControlSingleLine::subtreeHasChanged() InputElement* input = inputElement(); input->setValueFromRenderer(input->constrainValue(text())); - if (RenderObject* cancelButtonRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) - updateCancelButtonVisibility(cancelButtonRenderer->style()); + if (m_cancelButton) + updateCancelButtonVisibility(); // If the incremental attribute is set, then dispatch the search event if (input->searchEventsShouldBeDispatched()) @@ -281,12 +281,14 @@ bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, Hit if (!RenderTextControl::nodeAtPoint(request, result, xPos, yPos, tx, ty, hitTestAction)) return false; - if (result.innerNode() != element() && result.innerNode() != m_innerBlock.get()) - return false; - - hitInnerTextBlock(result, xPos, yPos, tx, ty); + // If we hit a node inside the inner text element, say that we hit that element, + // and if we hit our node (e.g. we're over the border or padding), also say that we hit the + // inner text element so that it gains focus. + if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node()) + hitInnerTextElement(result, xPos, yPos, tx, ty); - if (!m_innerBlock) + // If we're not a search field, or we already found the results or cancel buttons, we're done. + if (!m_innerBlock || result.innerNode() == m_resultsButton || result.innerNode() == m_cancelButton) return true; Node* innerNode = 0; @@ -334,7 +336,7 @@ void RenderTextControlSingleLine::forwardEvent(Event* event) return; } - FloatPoint localPoint = innerTextRenderer->absoluteToLocal(FloatPoint(static_cast<MouseEvent*>(event)->pageX(), static_cast<MouseEvent*>(event)->pageY()), false, true); + FloatPoint localPoint = innerTextRenderer->absoluteToLocal(static_cast<MouseEvent*>(event)->absoluteLocation(), false, true); if (m_resultsButton && localPoint.x() < innerTextRenderer->borderBoxRect().x()) m_resultsButton->defaultEventHandler(event); else if (m_cancelButton && localPoint.x() > innerTextRenderer->borderBoxRect().right()) @@ -343,7 +345,7 @@ void RenderTextControlSingleLine::forwardEvent(Event* event) RenderTextControl::forwardEvent(event); } -void RenderTextControlSingleLine::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderTextControl::styleDidChange(diff, oldStyle); @@ -360,6 +362,8 @@ void RenderTextControlSingleLine::styleDidChange(RenderStyle::Diff diff, const R if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) cancelRenderer->setStyle(createCancelButtonStyle(style())); + + setHasOverflowClip(false); } void RenderTextControlSingleLine::capsLockStateMayHaveChanged() @@ -425,7 +429,7 @@ int RenderTextControlSingleLine::preferredContentWidth(float charWidth) const void RenderTextControlSingleLine::adjustControlHeightBasedOnLineHeight(int lineHeight) { if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) { - static_cast<RenderBlock*>(resultsRenderer)->calcHeight(); + toRenderBlock(resultsRenderer)->calcHeight(); setHeight(max(height(), resultsRenderer->borderTop() + resultsRenderer->borderBottom() + resultsRenderer->paddingTop() + resultsRenderer->paddingBottom() + @@ -434,7 +438,7 @@ void RenderTextControlSingleLine::adjustControlHeightBasedOnLineHeight(int lineH } if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) { - static_cast<RenderBlock*>(cancelRenderer)->calcHeight(); + toRenderBlock(cancelRenderer)->calcHeight(); setHeight(max(height(), cancelRenderer->borderTop() + cancelRenderer->borderBottom() + cancelRenderer->paddingTop() + cancelRenderer->paddingBottom() + @@ -482,8 +486,8 @@ void RenderTextControlSingleLine::updateFromElement() bool placeholderVisibilityShouldChange = m_placeholderVisible != placeholderShouldBeVisible(); m_placeholderVisible = placeholderShouldBeVisible(); - if (RenderObject* cancelButtonRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) - updateCancelButtonVisibility(cancelButtonRenderer->style()); + if (m_cancelButton) + updateCancelButtonVisibility(); if (m_placeholderVisible) { ExceptionCode ec = 0; @@ -505,7 +509,7 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const { RefPtr<RenderStyle> textBlockStyle; if (placeholderShouldBeVisible()) { - RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::INPUT_PLACEHOLDER); + RenderStyle* pseudoStyle = getCachedPseudoStyle(INPUT_PLACEHOLDER); textBlockStyle = RenderStyle::clone(pseudoStyle); } else { textBlockStyle = RenderStyle::create(); @@ -562,11 +566,11 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createResultsButtonStyle(co RefPtr<RenderStyle> resultsBlockStyle; if (input->maxResults() < 0) - resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_DECORATION); + resultsBlockStyle = getCachedPseudoStyle(SEARCH_DECORATION); else if (!input->maxResults()) - resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_RESULTS_DECORATION); + resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_DECORATION); else - resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_RESULTS_BUTTON); + resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_BUTTON); if (!resultsBlockStyle) resultsBlockStyle = RenderStyle::create(); @@ -582,7 +586,7 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createCancelButtonStyle(con ASSERT(node()->isHTMLElement()); RefPtr<RenderStyle> cancelBlockStyle; - if (RefPtr<RenderStyle> pseudoStyle = getCachedPseudoStyle(RenderStyle::SEARCH_CANCEL_BUTTON)) + if (RefPtr<RenderStyle> pseudoStyle = getCachedPseudoStyle(SEARCH_CANCEL_BUTTON)) // We may be sharing style with another search field, but we must not share the cancel button style. cancelBlockStyle = RenderStyle::clone(pseudoStyle.get()); else @@ -591,15 +595,30 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createCancelButtonStyle(con if (startStyle) cancelBlockStyle->inheritFrom(startStyle); - updateCancelButtonVisibility(cancelBlockStyle.get()); + cancelBlockStyle->setVisibility(visibilityForCancelButton()); return cancelBlockStyle.release(); } -void RenderTextControlSingleLine::updateCancelButtonVisibility(RenderStyle* style) const +void RenderTextControlSingleLine::updateCancelButtonVisibility() const +{ + if (!m_cancelButton->renderer()) + return; + + const RenderStyle* curStyle = m_cancelButton->renderer()->style(); + EVisibility buttonVisibility = visibilityForCancelButton(); + if (curStyle->visibility() == buttonVisibility) + return; + + RefPtr<RenderStyle> cancelButtonStyle = RenderStyle::clone(curStyle); + cancelButtonStyle->setVisibility(buttonVisibility); + m_cancelButton->renderer()->setStyle(cancelButtonStyle); +} + +EVisibility RenderTextControlSingleLine::visibilityForCancelButton() const { ASSERT(node()->isHTMLElement()); HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); - style->setVisibility(input->value().isEmpty() ? HIDDEN : VISIBLE); + return input->value().isEmpty() ? HIDDEN : VISIBLE; } const AtomicString& RenderTextControlSingleLine::autosaveName() const @@ -684,7 +703,7 @@ PopupMenuStyle RenderTextControlSingleLine::itemStyle(unsigned) const PopupMenuStyle RenderTextControlSingleLine::menuStyle() const { - return PopupMenuStyle(style()->color(), style()->backgroundColor(), style()->font(), style()->visibility() == VISIBLE); + return PopupMenuStyle(style()->color(), style()->backgroundColor(), style()->font(), style()->visibility() == VISIBLE, style()->textIndent(), style()->direction()); } int RenderTextControlSingleLine::clientInsetLeft() const @@ -768,10 +787,65 @@ HostWindow* RenderTextControlSingleLine::hostWindow() const return document()->view()->hostWindow(); } +void RenderTextControlSingleLine::autoscroll() +{ + RenderLayer* layer = innerTextElement()->renderBox()->layer(); + if (layer) + layer->autoscroll(); +} + +int RenderTextControlSingleLine::scrollWidth() const +{ + if (innerTextElement()) + return innerTextElement()->scrollWidth(); + return RenderBlock::scrollWidth(); +} + +int RenderTextControlSingleLine::scrollHeight() const +{ + if (innerTextElement()) + return innerTextElement()->scrollHeight(); + return RenderBlock::scrollHeight(); +} + +int RenderTextControlSingleLine::scrollLeft() const +{ + if (innerTextElement()) + return innerTextElement()->scrollLeft(); + return RenderBlock::scrollLeft(); +} + +int RenderTextControlSingleLine::scrollTop() const +{ + if (innerTextElement()) + return innerTextElement()->scrollTop(); + return RenderBlock::scrollTop(); +} + +void RenderTextControlSingleLine::setScrollLeft(int newLeft) +{ + if (innerTextElement()) + innerTextElement()->setScrollLeft(newLeft); +} + +void RenderTextControlSingleLine::setScrollTop(int newTop) +{ + if (innerTextElement()) + innerTextElement()->setScrollTop(newTop); +} + +bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) +{ + RenderLayer* layer = innerTextElement()->renderBox()->layer(); + if (layer && layer->scroll(direction, granularity, multiplier)) + return true; + return RenderBlock::scroll(direction, granularity, multiplier); +} + PassRefPtr<Scrollbar> RenderTextControlSingleLine::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) { RefPtr<Scrollbar> widget; - bool hasCustomScrollbarStyle = style()->hasPseudoStyle(RenderStyle::SCROLLBAR); + bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR); if (hasCustomScrollbarStyle) widget = RenderScrollbar::createCustomScrollbar(client, orientation, this); else diff --git a/WebCore/rendering/RenderTextControlSingleLine.h b/WebCore/rendering/RenderTextControlSingleLine.h index a7b58e1..23352b4 100644 --- a/WebCore/rendering/RenderTextControlSingleLine.h +++ b/WebCore/rendering/RenderTextControlSingleLine.h @@ -59,9 +59,20 @@ public: virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); void forwardEvent(Event*); -private: - virtual void capsLockStateMayHaveChanged(); + void capsLockStateMayHaveChanged(); + + virtual void autoscroll(); + // Subclassed to forward to our inner div. + virtual int scrollLeft() const; + virtual int scrollTop() const; + virtual int scrollWidth() const; + virtual int scrollHeight() const; + virtual void setScrollLeft(int); + virtual void setScrollTop(int); + virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); + +private: int textBlockWidth() const; virtual int preferredContentWidth(float charWidth) const; virtual void adjustControlHeightBasedOnLineHeight(int lineHeight); @@ -69,14 +80,15 @@ private: void createSubtreeIfNeeded(); virtual void updateFromElement(); virtual void cacheSelection(int start, int end); - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const; PassRefPtr<RenderStyle> createInnerBlockStyle(const RenderStyle* startStyle) const; PassRefPtr<RenderStyle> createResultsButtonStyle(const RenderStyle* startStyle) const; PassRefPtr<RenderStyle> createCancelButtonStyle(const RenderStyle* startStyle) const; - void updateCancelButtonVisibility(RenderStyle*) const; + void updateCancelButtonVisibility() const; + EVisibility visibilityForCancelButton() const; const AtomicString& autosaveName() const; void startSearchEventTimer(); diff --git a/WebCore/rendering/RenderTextFragment.cpp b/WebCore/rendering/RenderTextFragment.cpp index c8beba0..7da9e5a 100644 --- a/WebCore/rendering/RenderTextFragment.cpp +++ b/WebCore/rendering/RenderTextFragment.cpp @@ -46,7 +46,7 @@ RenderTextFragment::RenderTextFragment(Node* node, StringImpl* str) PassRefPtr<StringImpl> RenderTextFragment::originalText() const { - Node* e = element(); + Node* e = node(); RefPtr<StringImpl> result = (e ? static_cast<Text*>(e)->string() : contentString()); if (result && (start() > 0 || start() < result->length())) result = result->substring(start(), end()); @@ -75,7 +75,7 @@ void RenderTextFragment::setTextInternal(PassRefPtr<StringImpl> text) UChar RenderTextFragment::previousCharacter() { if (start()) { - Node* e = element(); + Node* e = node(); StringImpl* original = (e ? static_cast<Text*>(e)->string() : contentString()); if (original) return (*original)[start() - 1]; diff --git a/WebCore/rendering/RenderTheme.cpp b/WebCore/rendering/RenderTheme.cpp index e7fa5de..f82f48c 100644 --- a/WebCore/rendering/RenderTheme.cpp +++ b/WebCore/rendering/RenderTheme.cpp @@ -581,7 +581,7 @@ ControlStates RenderTheme::controlStatesForRenderer(const RenderObject* o) const bool RenderTheme::isActive(const RenderObject* o) const { - Node* node = o->element(); + Node* node = o->node(); if (!node) return false; @@ -598,28 +598,43 @@ bool RenderTheme::isActive(const RenderObject* o) const bool RenderTheme::isChecked(const RenderObject* o) const { - if (!o->element()) + if (!o->node() || !o->node()->isElementNode()) return false; - return o->element()->isChecked(); + + InputElement* inputElement = toInputElement(static_cast<Element*>(o->node())); + if (!inputElement) + return false; + + return inputElement->isChecked(); } bool RenderTheme::isIndeterminate(const RenderObject* o) const { - if (!o->element()) + if (!o->node() || !o->node()->isElementNode()) + return false; + + InputElement* inputElement = toInputElement(static_cast<Element*>(o->node())); + if (!inputElement) return false; - return o->element()->isIndeterminate(); + + return inputElement->isIndeterminate(); } bool RenderTheme::isEnabled(const RenderObject* o) const { - if (!o->element()) + if (!o->node() || !o->node()->isElementNode()) + return true; + + FormControlElement* formControlElement = toFormControlElement(static_cast<Element*>(o->node())); + if (!formControlElement) return true; - return o->element()->isEnabled(); + + return formControlElement->isEnabled(); } bool RenderTheme::isFocused(const RenderObject* o) const { - Node* node = o->element(); + Node* node = o->node(); if (!node) return false; Document* document = node->document(); @@ -629,23 +644,28 @@ bool RenderTheme::isFocused(const RenderObject* o) const bool RenderTheme::isPressed(const RenderObject* o) const { - if (!o->element()) + if (!o->node()) return false; - return o->element()->active(); + return o->node()->active(); } bool RenderTheme::isReadOnlyControl(const RenderObject* o) const { - if (!o->element()) + if (!o->node() || !o->node()->isElementNode()) + return false; + + FormControlElement* formControlElement = toFormControlElement(static_cast<Element*>(o->node())); + if (!formControlElement) return false; - return o->element()->isReadOnlyControl(); + + return formControlElement->isReadOnlyControl(); } bool RenderTheme::isHovered(const RenderObject* o) const { - if (!o->element()) + if (!o->node()) return false; - return o->element()->hovered(); + return o->node()->hovered(); } bool RenderTheme::isDefault(const RenderObject* o) const @@ -721,10 +741,6 @@ void RenderTheme::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Ele { } -void RenderTheme::adjustButtonInnerStyle(RenderStyle*) const -{ -} - void RenderTheme::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const { } diff --git a/WebCore/rendering/RenderTheme.h b/WebCore/rendering/RenderTheme.h index 828e789..15a81fa 100644 --- a/WebCore/rendering/RenderTheme.h +++ b/WebCore/rendering/RenderTheme.h @@ -29,6 +29,7 @@ #else #include "ThemeTypes.h" #endif +#include "ScrollTypes.h" namespace WebCore { @@ -128,14 +129,21 @@ public: virtual int minimumMenuListSize(RenderStyle*) const { return 0; } - virtual void adjustButtonInnerStyle(RenderStyle*) const; virtual void adjustSliderThumbSize(RenderObject*) const; virtual int popupInternalPaddingLeft(RenderStyle*) const { return 0; } virtual int popupInternalPaddingRight(RenderStyle*) const { return 0; } virtual int popupInternalPaddingTop(RenderStyle*) const { return 0; } virtual int popupInternalPaddingBottom(RenderStyle*) const { return 0; } - + virtual bool popupOptionSupportsTextIndent() const { return false; } + + virtual int buttonInternalPaddingLeft() const { return 0; } + virtual int buttonInternalPaddingRight() const { return 0; } + virtual int buttonInternalPaddingTop() const { return 0; } + virtual int buttonInternalPaddingBottom() const { return 0; } + + virtual ScrollbarControlSize scrollbarControlSizeForPart(ControlPart) { return RegularScrollbar; } + // Method for painting the caps lock indicator virtual bool paintCapsLockIndicator(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return 0; }; diff --git a/WebCore/rendering/RenderThemeChromiumGtk.cpp b/WebCore/rendering/RenderThemeChromiumGtk.cpp deleted file mode 100644 index 220ce07..0000000 --- a/WebCore/rendering/RenderThemeChromiumGtk.cpp +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2008 Collabora Ltd. - * Copyright (C) 2008, 2009 Google Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "RenderThemeChromiumGtk.h" - -#include "ChromiumBridge.h" -#include "CSSValueKeywords.h" -#include "GraphicsContext.h" -#include "NotImplemented.h" -#include "PlatformContextSkia.h" -#include "RenderObject.h" -#include "ScrollbarTheme.h" -#include "gtkdrawing.h" -#include "GdkSkia.h" -#include "TransformationMatrix.h" -#include "UserAgentStyleSheets.h" - -#include <gdk/gdk.h> - -namespace WebCore { - -enum PaddingType { - TopPadding, - RightPadding, - BottomPadding, - LeftPadding -}; - -static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 }; - -// The default variable-width font size. We use this as the default font -// size for the "system font", and as a base size (which we then shrink) for -// form control fonts. -static float DefaultFontSize = 16.0; - -static Color makeColor(const GdkColor& c) -{ - return Color(makeRGB(c.red >> 8, c.green >> 8, c.blue >> 8)); -} - -// We aim to match IE here. -// -IE uses a font based on the encoding as the default font for form controls. -// -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT), -// which returns MS Shell Dlg) -// -Safari uses Lucida Grande. -// -// FIXME: The only case where we know we don't match IE is for ANSI encodings. -// IE uses MS Shell Dlg there, which we render incorrectly at certain pixel -// sizes (e.g. 15px). So, for now we just use Arial. -static const char* defaultGUIFont(Document* document) -{ - return "Arial"; -} - -// Converts points to pixels. One point is 1/72 of an inch. -static float pointsToPixels(float points) -{ - static float pixelsPerInch = 0.0f; - if (!pixelsPerInch) { - GdkScreen* screen = gdk_screen_get_default(); - // FIXME: I'm getting floating point values of ~75 and ~100, - // and it's making my fonts look all wrong. Figure this out. -#if 0 - if (screen) - pixelsPerInch = gdk_screen_get_resolution(screen); - else -#endif - pixelsPerInch = 96.0f; // Match the default we set on Windows. - } - - static const float pointsPerInch = 72.0f; - return points / pointsPerInch * pixelsPerInch; -} - -static void setSizeIfAuto(RenderStyle* style, const IntSize& size) -{ - if (style->width().isIntrinsicOrAuto()) - style->setWidth(Length(size.width(), Fixed)); - if (style->height().isAuto()) - style->setHeight(Length(size.height(), Fixed)); -} - -static bool supportsFocus(ControlPart appearance) -{ - switch (appearance) { - case PushButtonPart: - case ButtonPart: - case TextFieldPart: - case TextAreaPart: - case SearchFieldPart: - case MenulistPart: - case RadioPart: - case CheckboxPart: - return true; - default: - return false; - } -} - -static GtkTextDirection gtkTextDirection(TextDirection direction) -{ - switch (direction) { - case RTL: - return GTK_TEXT_DIR_RTL; - case LTR: - return GTK_TEXT_DIR_LTR; - default: - return GTK_TEXT_DIR_NONE; - } -} - -static void setMozState(RenderTheme* theme, GtkWidgetState* state, RenderObject* o) -{ - state->active = theme->isPressed(o); - state->focused = theme->isFocused(o); - state->inHover = theme->isHovered(o); - // FIXME: Disabled does not always give the correct appearance for ReadOnly - state->disabled = !theme->isEnabled(o) || theme->isReadOnlyControl(o); - state->isDefault = false; - state->canDefault = false; - state->depressed = false; -} - -static bool paintMozWidget(RenderTheme* theme, GtkThemeWidgetType type, RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - // Painting is disabled so just claim to have succeeded - if (i.context->paintingDisabled()) - return false; - - GtkWidgetState mozState; - setMozState(theme, &mozState, o); - - int flags; - - // We might want to make setting flags the caller's job at some point rather than doing it here. - switch (type) { - case MOZ_GTK_BUTTON: - flags = GTK_RELIEF_NORMAL; - break; - case MOZ_GTK_CHECKBUTTON: - case MOZ_GTK_RADIOBUTTON: - flags = theme->isChecked(o); - break; - default: - flags = 0; - break; - } - - PlatformContextSkia* pcs = i.context->platformContext(); - SkCanvas* canvas = pcs->canvas(); - if (!canvas) - return false; - - GdkRectangle gdkRect; - gdkRect.x = rect.x(); - gdkRect.y = rect.y(); - gdkRect.width = rect.width(); - gdkRect.height = rect.height(); - - // getTotalClip returns the currently set clip region in device coordinates, - // so we have to apply the current transform (actually we only support translations) - // to get the page coordinates that our gtk widget rendering expects. - // We invert it because we want to map from device coordinates to page coordinates. - const SkIRect clipRegion = canvas->getTotalClip().getBounds(); - TransformationMatrix ctm = i.context->getCTM().inverse(); - IntPoint pos = ctm.mapPoint(IntPoint(SkScalarRound(clipRegion.fLeft), SkScalarRound(clipRegion.fTop))); - GdkRectangle gdkClipRect; - gdkClipRect.x = pos.x(); - gdkClipRect.y = pos.y(); - gdkClipRect.width = clipRegion.width(); - gdkClipRect.height = clipRegion.height(); - - // moz_gtk_widget_paint will paint outside the bounds of gdkRect unless we further restrict |gdkClipRect|. - gdk_rectangle_intersect(&gdkRect, &gdkClipRect, &gdkClipRect); - - GtkTextDirection direction = gtkTextDirection(o->style()->direction()); - - return moz_gtk_widget_paint(type, pcs->gdk_skia(), &gdkRect, &gdkClipRect, &mozState, flags, direction) != MOZ_GTK_SUCCESS; -} - -static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderTheme* renderTheme) -{ - // FIXME: Make sure this function doesn't get called many times for a single GTK+ style change signal. - renderTheme->platformColorsDidChange(); -} - -static double querySystemBlinkInterval(double defaultInterval) -{ - GtkSettings* settings = gtk_settings_get_default(); - - gboolean shouldBlink; - gint time; - - g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL); - - if (!shouldBlink) - return 0; - - return time / 1000.0; -} - -// Implement WebCore::theme() for getting the global RenderTheme. -RenderTheme* theme() -{ - static RenderThemeChromiumGtk gtkTheme; - return >kTheme; -} - -RenderThemeChromiumGtk::RenderThemeChromiumGtk() - : m_gtkWindow(0) - , m_gtkContainer(0) - , m_gtkEntry(0) - , m_gtkTreeView(0) -{ -} - -// Use the Windows style sheets to match their metrics. -String RenderThemeChromiumGtk::extraDefaultStyleSheet() -{ - return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)); -} - -String RenderThemeChromiumGtk::extraQuirksStyleSheet() -{ - return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); -} - -bool RenderThemeChromiumGtk::supportsFocusRing(const RenderStyle* style) const -{ - return supportsFocus(style->appearance()); -} - -Color RenderThemeChromiumGtk::platformActiveSelectionBackgroundColor() const -{ - GtkWidget* widget = gtkEntry(); - return makeColor(widget->style->base[GTK_STATE_SELECTED]); -} - -Color RenderThemeChromiumGtk::platformInactiveSelectionBackgroundColor() const -{ - GtkWidget* widget = gtkEntry(); - return makeColor(widget->style->base[GTK_STATE_ACTIVE]); -} - -Color RenderThemeChromiumGtk::platformActiveSelectionForegroundColor() const -{ - GtkWidget* widget = gtkEntry(); - return makeColor(widget->style->text[GTK_STATE_SELECTED]); -} - -Color RenderThemeChromiumGtk::platformInactiveSelectionForegroundColor() const -{ - GtkWidget* widget = gtkEntry(); - return makeColor(widget->style->text[GTK_STATE_ACTIVE]); -} - -Color RenderThemeChromiumGtk::platformTextSearchHighlightColor() const -{ - return Color(255, 255, 150); -} - -double RenderThemeChromiumGtk::caretBlinkInterval() const -{ - // Disable the blinking caret in layout test mode, as it introduces - // a race condition for the pixel tests. http://b/1198440 - if (ChromiumBridge::layoutTestMode()) - return 0; - - // We cache the interval so we don't have to repeatedly request it from gtk. - static double blinkInterval = querySystemBlinkInterval(RenderTheme::caretBlinkInterval()); - return blinkInterval; -} - -void RenderThemeChromiumGtk::systemFont(int propId, Document* document, FontDescription& fontDescription) const -{ - const char* faceName = 0; - float fontSize = 0; - // FIXME: see also RenderThemeChromiumWin.cpp - switch (propId) { - case CSSValueMenu: - case CSSValueStatusBar: - case CSSValueSmallCaption: - // triggered by LayoutTests/fast/css/css2-system-fonts.html - notImplemented(); - break; - case CSSValueWebkitMiniControl: - case CSSValueWebkitSmallControl: - case CSSValueWebkitControl: - faceName = defaultGUIFont(document); - // Why 2 points smaller? Because that's what Gecko does. - fontSize = DefaultFontSize - pointsToPixels(2); - break; - default: - faceName = defaultGUIFont(document); - fontSize = DefaultFontSize; - } - - // Only update if the size makes sense. - if (fontSize > 0) { - fontDescription.firstFamily().setFamily(faceName); - fontDescription.setSpecifiedSize(fontSize); - fontDescription.setIsAbsoluteSize(true); - fontDescription.setGenericFamily(FontDescription::NoFamily); - fontDescription.setWeight(FontWeightNormal); - fontDescription.setItalic(false); - } -} - -int RenderThemeChromiumGtk::minimumMenuListSize(RenderStyle* style) const -{ - return 0; -} - -bool RenderThemeChromiumGtk::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - return paintMozWidget(this, MOZ_GTK_CHECKBUTTON, o, i, rect); -} - -void RenderThemeChromiumGtk::setCheckboxSize(RenderStyle* style) const -{ - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox. - // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for - // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's - // metrics. - const IntSize size(13, 13); - setSizeIfAuto(style, size); -} - -bool RenderThemeChromiumGtk::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - return paintMozWidget(this, MOZ_GTK_RADIOBUTTON, o, i, rect); -} - -void RenderThemeChromiumGtk::setRadioSize(RenderStyle* style) const -{ - // Use same sizing for radio box as checkbox. - setCheckboxSize(style); -} - -bool RenderThemeChromiumGtk::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - return paintMozWidget(this, MOZ_GTK_BUTTON, o, i, rect); -} - -bool RenderThemeChromiumGtk::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - return paintMozWidget(this, MOZ_GTK_ENTRY, o, i, rect); -} - -bool RenderThemeChromiumGtk::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - return paintTextField(o, i, rect); -} - -bool RenderThemeChromiumGtk::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - return paintMozWidget(this, MOZ_GTK_CHECKMENUITEM, o, i, rect); -} - -bool RenderThemeChromiumGtk::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - return paintMozWidget(this, MOZ_GTK_DROPDOWN_ARROW, o, i, rect); -} - -bool RenderThemeChromiumGtk::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - return paintMozWidget(this, MOZ_GTK_CHECKMENUITEM, o, i, rect); -} - -void RenderThemeChromiumGtk::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const -{ - // Height is locked to auto on all browsers. - style->setLineHeight(RenderStyle::initialLineHeight()); -} - -bool RenderThemeChromiumGtk::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) -{ - return paintMozWidget(this, MOZ_GTK_DROPDOWN, o, i, rect); -} - -void RenderThemeChromiumGtk::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const -{ - adjustMenuListStyle(selector, style, e); -} - -// Used to paint styled menulists (i.e. with a non-default border) -bool RenderThemeChromiumGtk::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) -{ - return paintMenuList(o, i, r); -} - -int RenderThemeChromiumGtk::popupInternalPaddingLeft(RenderStyle* style) const -{ - return menuListInternalPadding(style, LeftPadding); -} - -int RenderThemeChromiumGtk::popupInternalPaddingRight(RenderStyle* style) const -{ - return menuListInternalPadding(style, RightPadding); -} - -int RenderThemeChromiumGtk::popupInternalPaddingTop(RenderStyle* style) const -{ - return menuListInternalPadding(style, TopPadding); -} - -int RenderThemeChromiumGtk::popupInternalPaddingBottom(RenderStyle* style) const -{ - return menuListInternalPadding(style, BottomPadding); -} - -void RenderThemeChromiumGtk::adjustButtonInnerStyle(RenderStyle* style) const -{ - // This inner padding matches Firefox. - style->setPaddingTop(Length(1, Fixed)); - style->setPaddingRight(Length(3, Fixed)); - style->setPaddingBottom(Length(1, Fixed)); - style->setPaddingLeft(Length(3, Fixed)); -} - -bool RenderThemeChromiumGtk::controlSupportsTints(const RenderObject* o) const -{ - return isEnabled(o); -} - -Color RenderThemeChromiumGtk::activeListBoxSelectionBackgroundColor() const -{ - GtkWidget* widget = gtkTreeView(); - return makeColor(widget->style->base[GTK_STATE_SELECTED]); -} - -Color RenderThemeChromiumGtk::activeListBoxSelectionForegroundColor() const -{ - GtkWidget* widget = gtkTreeView(); - return makeColor(widget->style->text[GTK_STATE_SELECTED]); -} - -Color RenderThemeChromiumGtk::inactiveListBoxSelectionBackgroundColor() const -{ - GtkWidget* widget = gtkTreeView(); - return makeColor(widget->style->base[GTK_STATE_ACTIVE]); -} - -Color RenderThemeChromiumGtk::inactiveListBoxSelectionForegroundColor() const -{ - GtkWidget* widget = gtkTreeView(); - return makeColor(widget->style->text[GTK_STATE_ACTIVE]); -} - -GtkWidget* RenderThemeChromiumGtk::gtkEntry() const -{ - if (m_gtkEntry) - return m_gtkEntry; - - m_gtkEntry = gtk_entry_new(); - g_signal_connect(m_gtkEntry, "style-set", G_CALLBACK(gtkStyleSetCallback), theme()); - gtk_container_add(gtkContainer(), m_gtkEntry); - gtk_widget_realize(m_gtkEntry); - - return m_gtkEntry; -} - -GtkWidget* RenderThemeChromiumGtk::gtkTreeView() const -{ - if (m_gtkTreeView) - return m_gtkTreeView; - - m_gtkTreeView = gtk_tree_view_new(); - g_signal_connect(m_gtkTreeView, "style-set", G_CALLBACK(gtkStyleSetCallback), theme()); - gtk_container_add(gtkContainer(), m_gtkTreeView); - gtk_widget_realize(m_gtkTreeView); - - return m_gtkTreeView; -} - -GtkContainer* RenderThemeChromiumGtk::gtkContainer() const -{ - if (m_gtkContainer) - return m_gtkContainer; - - m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP); - m_gtkContainer = GTK_CONTAINER(gtk_fixed_new()); - gtk_container_add(GTK_CONTAINER(m_gtkWindow), GTK_WIDGET(m_gtkContainer)); - gtk_widget_realize(m_gtkWindow); - - return m_gtkContainer; -} - -int RenderThemeChromiumGtk::menuListInternalPadding(RenderStyle* style, int paddingType) const -{ - // This internal padding is in addition to the user-supplied padding. - // Matches the FF behavior. - int padding = styledMenuListInternalPadding[paddingType]; - - // Reserve the space for right arrow here. The rest of the padding is - // set by adjustMenuListStyle, since PopMenuWin.cpp uses the padding from - // RenderMenuList to lay out the individual items in the popup. - // If the MenuList actually has appearance "NoAppearance", then that means - // we don't draw a button, so don't reserve space for it. - const int bar_type = style->direction() == LTR ? RightPadding : LeftPadding; - if (paddingType == bar_type && style->appearance() != NoControlPart) - padding += ScrollbarTheme::nativeTheme()->scrollbarThickness(); - - return padding; -} - -} // namespace WebCore diff --git a/WebCore/rendering/RenderThemeChromiumLinux.cpp b/WebCore/rendering/RenderThemeChromiumLinux.cpp new file mode 100644 index 0000000..8c6874d --- /dev/null +++ b/WebCore/rendering/RenderThemeChromiumLinux.cpp @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2007 Apple Inc. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2008, 2009 Google Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderThemeChromiumLinux.h" + +#include "ChromiumBridge.h" +#include "CSSValueKeywords.h" +#include "GraphicsContext.h" +#include "Image.h" +#include "NotImplemented.h" +#include "PlatformContextSkia.h" +#include "RenderObject.h" +#include "ScrollbarTheme.h" +#include "TransformationMatrix.h" +#include "UserAgentStyleSheets.h" + +#include "SkShader.h" +#include "SkGradientShader.h" + +namespace WebCore { + +enum PaddingType { + TopPadding, + RightPadding, + BottomPadding, + LeftPadding +}; + +static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 }; + +// The default variable-width font size. We use this as the default font +// size for the "system font", and as a base size (which we then shrink) for +// form control fonts. +static const float DefaultFontSize = 16.0; + +static bool supportsFocus(ControlPart appearance) +{ + // This causes WebKit to draw the focus rings for us. + return false; +} + +static void setSizeIfAuto(RenderStyle* style, const IntSize& size) +{ + if (style->width().isIntrinsicOrAuto()) + style->setWidth(Length(size.width(), Fixed)); + if (style->height().isAuto()) + style->setHeight(Length(size.height(), Fixed)); +} + +// We aim to match IE here. +// -IE uses a font based on the encoding as the default font for form controls. +// -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT), +// which returns MS Shell Dlg) +// -Safari uses Lucida Grande. +// +// FIXME: The only case where we know we don't match IE is for ANSI encodings. +// IE uses MS Shell Dlg there, which we render incorrectly at certain pixel +// sizes (e.g. 15px). So, for now we just use Arial. +static const char* defaultGUIFont(Document* document) +{ + return "Arial"; +} + +RenderTheme* theme() +{ + static RenderThemeChromiumLinux theme; + return &theme; +} + +RenderThemeChromiumLinux::RenderThemeChromiumLinux() +{ +} + +// Use the Windows style sheets to match their metrics. +String RenderThemeChromiumLinux::extraDefaultStyleSheet() +{ + return String(themeChromiumWinUserAgentStyleSheet, sizeof(themeChromiumWinUserAgentStyleSheet)); +} + +String RenderThemeChromiumLinux::extraQuirksStyleSheet() +{ + return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); +} + +bool RenderThemeChromiumLinux::supportsFocusRing(const RenderStyle* style) const +{ + return supportsFocus(style->appearance()); +} + +Color RenderThemeChromiumLinux::platformActiveSelectionBackgroundColor() const +{ + return Color(0x1e, 0x90, 0xff); +} + +Color RenderThemeChromiumLinux::platformInactiveSelectionBackgroundColor() const +{ + return Color(0xc8, 0xc8, 0xc8); +} + +Color RenderThemeChromiumLinux::platformActiveSelectionForegroundColor() const +{ + return Color(0, 0, 0); +} + +Color RenderThemeChromiumLinux::platformInactiveSelectionForegroundColor() const +{ + return Color(0x32, 0x32, 0x32); +} + +Color RenderThemeChromiumLinux::platformTextSearchHighlightColor() const +{ + return Color(0xff, 0xff, 0x96); +} + +double RenderThemeChromiumLinux::caretBlinkInterval() const +{ + // Disable the blinking caret in layout test mode, as it introduces + // a race condition for the pixel tests. http://b/1198440 + if (ChromiumBridge::layoutTestMode()) + return 0; + + // We cache the interval so we don't have to repeatedly request it from gtk. + return 0.5; +} + +void RenderThemeChromiumLinux::systemFont(int propId, Document* document, FontDescription& fontDescription) const +{ + float fontSize = DefaultFontSize; + + switch (propId) { + case CSSValueWebkitMiniControl: + case CSSValueWebkitSmallControl: + case CSSValueWebkitControl: + // Why 2 points smaller? Because that's what Gecko does. Note that we + // are assuming a 96dpi screen, which is the default that we use on + // Windows. + static const float pointsPerInch = 72.0f; + static const float pixelsPerInch = 96.0f; + fontSize -= (2.0f / pointsPerInch) * pixelsPerInch; + break; + } + + fontDescription.firstFamily().setFamily(defaultGUIFont(NULL)); + fontDescription.setSpecifiedSize(fontSize); + fontDescription.setIsAbsoluteSize(true); + fontDescription.setGenericFamily(FontDescription::NoFamily); + fontDescription.setWeight(FontWeightNormal); + fontDescription.setItalic(false); +} + +int RenderThemeChromiumLinux::minimumMenuListSize(RenderStyle* style) const +{ + return 0; +} + +bool RenderThemeChromiumLinux::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + static Image* const checkedImage = Image::loadPlatformResource("linuxCheckboxOn").releaseRef(); + static Image* const uncheckedImage = Image::loadPlatformResource("linuxCheckboxOff").releaseRef(); + + Image* image = this->isChecked(o) ? checkedImage : uncheckedImage; + i.context->drawImage(image, rect); + return false; +} + +void RenderThemeChromiumLinux::setCheckboxSize(RenderStyle* style) const +{ + // If the width and height are both specified, then we have nothing to do. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // FIXME: A hard-coded size of 13 is used. This is wrong but necessary + // for now. It matches Firefox. At different DPI settings on Windows, + // querying the theme gives you a larger size that accounts for the higher + // DPI. Until our entire engine honors a DPI setting other than 96, we + // can't rely on the theme's metrics. + const IntSize size(13, 13); + setSizeIfAuto(style, size); +} + +bool RenderThemeChromiumLinux::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + static Image* const checkedImage = Image::loadPlatformResource("linuxRadioOn").releaseRef(); + static Image* const uncheckedImage = Image::loadPlatformResource("linuxRadioOff").releaseRef(); + + Image* image = this->isChecked(o) ? checkedImage : uncheckedImage; + i.context->drawImage(image, rect); + return false; +} + +void RenderThemeChromiumLinux::setRadioSize(RenderStyle* style) const +{ + // Use same sizing for radio box as checkbox. + setCheckboxSize(style); +} + +static void paintButtonLike(RenderTheme* theme, RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) { + SkCanvas* const canvas = i.context->platformContext()->canvas(); + SkPaint paint; + SkRect skrect; + const int right = rect.x() + rect.width(); + const int bottom = rect.y() + rect.height(); + + // If the button is too small, fallback to drawing a single, solid color + if (rect.width() < 5 || rect.height() < 5) { + paint.setARGB(0xff, 0xe9, 0xe9, 0xe9); + skrect.set(rect.x(), rect.y(), right, bottom); + canvas->drawRect(skrect, paint); + return; + } + + const int borderAlpha = theme->isHovered(o) ? 0x80 : 0x55; + paint.setARGB(borderAlpha, 0, 0, 0); + canvas->drawLine(rect.x() + 1, rect.y(), right - 1, rect.y(), paint); + canvas->drawLine(right - 1, rect.y() + 1, right - 1, bottom - 1, paint); + canvas->drawLine(rect.x() + 1, bottom - 1, right - 1, bottom - 1, paint); + canvas->drawLine(rect.x(), rect.y() + 1, rect.x(), bottom - 1, paint); + + paint.setARGB(0xff, 0, 0, 0); + SkPoint p[2]; + const int lightEnd = theme->isPressed(o) ? 1 : 0; + const int darkEnd = !lightEnd; + p[lightEnd].set(SkIntToScalar(rect.x()), SkIntToScalar(rect.y())); + p[darkEnd].set(SkIntToScalar(rect.x()), SkIntToScalar(bottom - 1)); + SkColor colors[2]; + colors[0] = SkColorSetARGB(0xff, 0xf8, 0xf8, 0xf8); + colors[1] = SkColorSetARGB(0xff, 0xdd, 0xdd, 0xdd); + + SkShader* s = SkGradientShader::CreateLinear( + p, colors, NULL, 2, SkShader::kClamp_TileMode, NULL); + paint.setStyle(SkPaint::kFill_Style); + paint.setShader(s); + s->unref(); + + skrect.set(rect.x() + 1, rect.y() + 1, right - 1, bottom - 1); + canvas->drawRect(skrect, paint); + + paint.setShader(NULL); + paint.setARGB(0xff, 0xce, 0xce, 0xce); + canvas->drawPoint(rect.x() + 1, rect.y() + 1, paint); + canvas->drawPoint(right - 2, rect.y() + 1, paint); + canvas->drawPoint(rect.x() + 1, bottom - 2, paint); + canvas->drawPoint(right - 2, bottom - 2, paint); +} + +bool RenderThemeChromiumLinux::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + paintButtonLike(this, o, i, rect); + return false; +} + +bool RenderThemeChromiumLinux::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + return true; +} + +bool RenderThemeChromiumLinux::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + return true; +} + +bool RenderThemeChromiumLinux::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + return true; +} + +bool RenderThemeChromiumLinux::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + return true; +} + +bool RenderThemeChromiumLinux::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + return true; +} + +void RenderThemeChromiumLinux::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const +{ + // Height is locked to auto on all browsers. + style->setLineHeight(RenderStyle::initialLineHeight()); +} + +bool RenderThemeChromiumLinux::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + SkCanvas* const canvas = i.context->platformContext()->canvas(); + const int right = rect.x() + rect.width(); + const int middle = rect.y() + rect.height() / 2; + + paintButtonLike(this, o, i, rect); + + SkPaint paint; + paint.setARGB(0xff, 0, 0, 0); + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kFill_Style); + + SkPath path; + path.moveTo(right - 13, middle - 3); + path.rLineTo(6, 0); + path.rLineTo(-3, 6); + path.close(); + canvas->drawPath(path, paint); + + return false; +} + +void RenderThemeChromiumLinux::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + adjustMenuListStyle(selector, style, e); +} + +// Used to paint styled menulists (i.e. with a non-default border) +bool RenderThemeChromiumLinux::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + return paintMenuList(o, i, rect); +} + +int RenderThemeChromiumLinux::popupInternalPaddingLeft(RenderStyle* style) const +{ + return menuListInternalPadding(style, LeftPadding); +} + +int RenderThemeChromiumLinux::popupInternalPaddingRight(RenderStyle* style) const +{ + return menuListInternalPadding(style, RightPadding); +} + +int RenderThemeChromiumLinux::popupInternalPaddingTop(RenderStyle* style) const +{ + return menuListInternalPadding(style, TopPadding); +} + +int RenderThemeChromiumLinux::popupInternalPaddingBottom(RenderStyle* style) const +{ + return menuListInternalPadding(style, BottomPadding); +} + +int RenderThemeChromiumLinux::buttonInternalPaddingLeft() const +{ + return 3; +} + +int RenderThemeChromiumLinux::buttonInternalPaddingRight() const +{ + return 3; +} + +int RenderThemeChromiumLinux::buttonInternalPaddingTop() const +{ + return 1; +} + +int RenderThemeChromiumLinux::buttonInternalPaddingBottom() const +{ + return 1; +} + +bool RenderThemeChromiumLinux::controlSupportsTints(const RenderObject* o) const +{ + return isEnabled(o); +} + +Color RenderThemeChromiumLinux::activeListBoxSelectionBackgroundColor() const +{ + return Color(0x28, 0x28, 0x28); +} + +Color RenderThemeChromiumLinux::activeListBoxSelectionForegroundColor() const +{ + return Color(0, 0, 0); +} + +Color RenderThemeChromiumLinux::inactiveListBoxSelectionBackgroundColor() const +{ + return Color(0xc8, 0xc8, 0xc8); +} + +Color RenderThemeChromiumLinux::inactiveListBoxSelectionForegroundColor() const +{ + return Color(0x32, 0x32, 0x32); +} + +int RenderThemeChromiumLinux::menuListInternalPadding(RenderStyle* style, int paddingType) const +{ + // This internal padding is in addition to the user-supplied padding. + // Matches the FF behavior. + int padding = styledMenuListInternalPadding[paddingType]; + + // Reserve the space for right arrow here. The rest of the padding is + // set by adjustMenuListStyle, since PopMenuWin.cpp uses the padding from + // RenderMenuList to lay out the individual items in the popup. + // If the MenuList actually has appearance "NoAppearance", then that means + // we don't draw a button, so don't reserve space for it. + const int bar_type = style->direction() == LTR ? RightPadding : LeftPadding; + if (paddingType == bar_type && style->appearance() != NoControlPart) + padding += ScrollbarTheme::nativeTheme()->scrollbarThickness(); + + return padding; +} + +} // namespace WebCore diff --git a/WebCore/rendering/RenderThemeChromiumGtk.h b/WebCore/rendering/RenderThemeChromiumLinux.h index 77d927f..dbe8dcc 100644 --- a/WebCore/rendering/RenderThemeChromiumGtk.h +++ b/WebCore/rendering/RenderThemeChromiumLinux.h @@ -25,19 +25,17 @@ * */ -#ifndef RenderThemeChromiumGtk_h -#define RenderThemeChromiumGtk_h +#ifndef RenderThemeChromiumLinux_h +#define RenderThemeChromiumLinux_h #include "RenderTheme.h" -#include <gtk/gtk.h> - namespace WebCore { - class RenderThemeChromiumGtk : public RenderTheme { + class RenderThemeChromiumLinux : public RenderTheme { public: - RenderThemeChromiumGtk(); - ~RenderThemeChromiumGtk() { } + RenderThemeChromiumLinux(); + ~RenderThemeChromiumLinux() { } virtual String extraDefaultStyleSheet(); virtual String extraQuirksStyleSheet(); @@ -100,7 +98,10 @@ namespace WebCore { virtual int popupInternalPaddingTop(RenderStyle*) const; virtual int popupInternalPaddingBottom(RenderStyle*) const; - virtual void adjustButtonInnerStyle(RenderStyle* style) const; + virtual int buttonInternalPaddingLeft() const; + virtual int buttonInternalPaddingRight() const; + virtual int buttonInternalPaddingTop() const; + virtual int buttonInternalPaddingBottom() const; // A method asking if the control changes its tint when the window has focus or not. virtual bool controlSupportsTints(const RenderObject*) const; @@ -115,20 +116,7 @@ namespace WebCore { virtual Color inactiveListBoxSelectionForegroundColor() const; private: - // Hold the state - GtkWidget* gtkEntry() const; - GtkWidget* gtkTreeView() const; - - // Unmapped GdkWindow having a container. This is holding all our fake widgets - GtkContainer* gtkContainer() const; - - private: int menuListInternalPadding(RenderStyle*, int paddingType) const; - - mutable GtkWidget* m_gtkWindow; - mutable GtkContainer* m_gtkContainer; - mutable GtkWidget* m_gtkEntry; - mutable GtkWidget* m_gtkTreeView; }; } // namespace WebCore diff --git a/WebCore/rendering/RenderThemeChromiumMac.h b/WebCore/rendering/RenderThemeChromiumMac.h index b750213..29286d8 100644 --- a/WebCore/rendering/RenderThemeChromiumMac.h +++ b/WebCore/rendering/RenderThemeChromiumMac.h @@ -25,6 +25,7 @@ #define RenderThemeChromiumMac_h #import "RenderTheme.h" +#import <AppKit/AppKit.h> #import <wtf/HashMap.h> #import <wtf/RetainPtr.h> @@ -77,6 +78,8 @@ namespace WebCore { virtual int popupInternalPaddingTop(RenderStyle*) const; virtual int popupInternalPaddingBottom(RenderStyle*) const; + virtual ScrollbarControlSize scrollbarControlSizeForPart(ControlPart) { return SmallScrollbar; } + virtual bool paintCapsLockIndicator(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual Color systemColor(int cssValueId) const; diff --git a/WebCore/rendering/RenderThemeChromiumMac.mm b/WebCore/rendering/RenderThemeChromiumMac.mm index 6318fd9..1eef0cb 100644 --- a/WebCore/rendering/RenderThemeChromiumMac.mm +++ b/WebCore/rendering/RenderThemeChromiumMac.mm @@ -565,7 +565,7 @@ void RenderThemeChromiumMac::updateFocusedState(NSCell* cell, const RenderObject void RenderThemeChromiumMac::updatePressedState(NSCell* cell, const RenderObject* o) { bool oldPressed = [cell isHighlighted]; - bool pressed = (o->element() && o->element()->active()); + bool pressed = (o->node() && o->node()->active()); if (pressed != oldPressed) [cell setHighlighted:pressed]; } @@ -573,8 +573,13 @@ void RenderThemeChromiumMac::updatePressedState(NSCell* cell, const RenderObject // FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. int RenderThemeChromiumMac::baselinePosition(const RenderObject* o) const { - if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart) - return o->marginTop() + o->height() - 2 * o->style()->effectiveZoom(); // The baseline is 2px up from the bottom of the checkbox/radio in AppKit. + if (!o->isBox()) + return 0; + + if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart) { + const RenderBox* box = toRenderBox(o); + return box->marginTop() + box->height() - 2 * o->style()->effectiveZoom(); // The baseline is 2px up from the bottom of the checkbox/radio in AppKit. + } return RenderTheme::baselinePosition(o); } @@ -1311,7 +1316,11 @@ void RenderThemeChromiumMac::adjustMenuListStyle(CSSStyleSelector* selector, Ren // Set the foreground color to black or gray when we have the aqua look. // Cast to RGB32 is to work around a compiler bug. - style->setColor(e->isEnabled() ? static_cast<RGBA32>(Color::black) : Color::darkGray); + bool isEnabled = true; + if (FormControlElement* formControlElement = toFormControlElement(e)) + isEnabled = formControlElement->isEnabled(); + + style->setColor(isEnabled ? static_cast<RGBA32>(Color::black) : Color::darkGray); // Set the button's vertical size. setSizeFromFont(style, menuListButtonSizes()); diff --git a/WebCore/rendering/RenderThemeChromiumWin.cpp b/WebCore/rendering/RenderThemeChromiumWin.cpp index c304385..30f421b 100644 --- a/WebCore/rendering/RenderThemeChromiumWin.cpp +++ b/WebCore/rendering/RenderThemeChromiumWin.cpp @@ -35,9 +35,11 @@ #include "FontSelector.h" #include "FontUtilsChromiumWin.h" #include "GraphicsContext.h" +#include "RenderBox.h" +#include "RenderSlider.h" #include "ScrollbarTheme.h" #include "SkiaUtils.h" -#include "ThemeHelperChromiumWin.h" +#include "TransparencyWin.h" #include "UserAgentStyleSheets.h" #include "WindowsVersion.h" @@ -52,6 +54,52 @@ namespace WebCore { +namespace { + +bool canvasHasMultipleLayers(const SkCanvas* canvas) +{ + SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false); + iter.next(); // There is always at least one layer. + return !iter.done(); // There is > 1 layer if the the iterator can stil advance. +} + +class ThemePainter : public TransparencyWin { +public: + ThemePainter(GraphicsContext* context, const IntRect& r) + { + TransformMode transformMode = getTransformMode(context->getCTM()); + init(context, getLayerMode(context, transformMode), transformMode, r); + } + + ~ThemePainter() + { + composite(); + } + +private: + static LayerMode getLayerMode(GraphicsContext* context, TransformMode transformMode) + { + if (context->platformContext()->isDrawingToImageBuffer()) // Might have transparent background. + return WhiteLayer; + else if (canvasHasMultipleLayers(context->platformContext()->canvas())) // Needs antialiasing help. + return OpaqueCompositeLayer; + else // Nothing interesting. + return transformMode == KeepTransform ? NoLayer : OpaqueCompositeLayer; + } + + static TransformMode getTransformMode(const TransformationMatrix& matrix) + { + if (matrix.b() != 0 || matrix.c() != 0) // Skew. + return Untransform; + else if (matrix.a() != 1.0 || matrix.d() != 1.0) // Scale. + return ScaleTransform; + else // Nothing interesting. + return KeepTransform; + } +}; + +} // namespace + static void getNonClientMetrics(NONCLIENTMETRICS* metrics) { static UINT size = WebCore::isVistaOrNewer() ? sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA; @@ -89,6 +137,7 @@ static bool supportsFocus(ControlPart appearance) case PushButtonPart: case ButtonPart: case DefaultButtonPart: + case SearchFieldPart: case TextFieldPart: case TextAreaPart: return true; @@ -212,7 +261,7 @@ RenderTheme* theme() String RenderThemeChromiumWin::extraDefaultStyleSheet() { - return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)); + return String(themeChromiumWinUserAgentStyleSheet, sizeof(themeChromiumWinUserAgentStyleSheet)); } String RenderThemeChromiumWin::extraQuirksStyleSheet() @@ -347,6 +396,20 @@ int RenderThemeChromiumWin::minimumMenuListSize(RenderStyle* style) const return 0; } +void RenderThemeChromiumWin::adjustSliderThumbSize(RenderObject* o) const +{ + // These sizes match what WinXP draws for various menus. + const int sliderThumbAlongAxis = 11; + const int sliderThumbAcrossAxis = 21; + if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == MediaSliderThumbPart) { + o->style()->setWidth(Length(sliderThumbAlongAxis, Fixed)); + o->style()->setHeight(Length(sliderThumbAcrossAxis, Fixed)); + } else if (o->style()->appearance() == SliderThumbVerticalPart) { + o->style()->setWidth(Length(sliderThumbAcrossAxis, Fixed)); + o->style()->setHeight(Length(sliderThumbAlongAxis, Fixed)); + } +} + void RenderThemeChromiumWin::setCheckboxSize(RenderStyle* style) const { // If the width and height are both specified, then we have nothing to do. @@ -372,12 +435,12 @@ bool RenderThemeChromiumWin::paintButton(RenderObject* o, const RenderObject::Pa { const ThemeData& themeData = getThemeData(o); - WebCore::ThemeHelperWin helper(i.context, r); - ChromiumBridge::paintButton(helper.context(), + WebCore::ThemePainter painter(i.context, r); + ChromiumBridge::paintButton(painter.context(), themeData.m_part, themeData.m_state, themeData.m_classicState, - helper.rect()); + painter.drawRect()); return false; } @@ -386,6 +449,19 @@ bool RenderThemeChromiumWin::paintTextField(RenderObject* o, const RenderObject: return paintTextFieldInternal(o, i, r, true); } +bool RenderThemeChromiumWin::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + const ThemeData& themeData = getThemeData(o); + + WebCore::ThemePainter painter(i.context, r); + ChromiumBridge::paintTrackbar(painter.context(), + themeData.m_part, + themeData.m_state, + themeData.m_classicState, + painter.drawRect()); + return false; +} + bool RenderThemeChromiumWin::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { return paintTextField(o, i, r); @@ -400,10 +476,14 @@ void RenderThemeChromiumWin::adjustMenuListStyle(CSSStyleSelector* selector, Ren // Used to paint unstyled menulists (i.e. with the default border) bool RenderThemeChromiumWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { - int borderRight = o->borderRight(); - int borderLeft = o->borderLeft(); - int borderTop = o->borderTop(); - int borderBottom = o->borderBottom(); + if (!o->isBox()) + return false; + + const RenderBox* box = toRenderBox(o); + int borderRight = box->borderRight(); + int borderLeft = box->borderLeft(); + int borderTop = box->borderTop(); + int borderBottom = box->borderBottom(); // If all the borders are 0, then tell skia not to paint the border on the // textfield. FIXME: http://b/1210017 Figure out how to get Windows to not @@ -418,10 +498,10 @@ bool RenderThemeChromiumWin::paintMenuList(RenderObject* o, const RenderObject:: // the size of a button, make sure to shrink it appropriately and not put // its x position to the left of the menulist. const int buttonWidth = GetSystemMetrics(SM_CXVSCROLL); - int spacingLeft = borderLeft + o->paddingLeft(); - int spacingRight = borderRight + o->paddingRight(); - int spacingTop = borderTop + o->paddingTop(); - int spacingBottom = borderBottom + o->paddingBottom(); + int spacingLeft = borderLeft + box->paddingLeft(); + int spacingRight = borderRight + box->paddingRight(); + int spacingTop = borderTop + box->paddingTop(); + int spacingBottom = borderBottom + box->paddingBottom(); int buttonX; if (r.right() - r.x() < buttonWidth) @@ -436,12 +516,12 @@ bool RenderThemeChromiumWin::paintMenuList(RenderObject* o, const RenderObject:: r.height() - (spacingTop + spacingBottom)); // Get the correct theme data for a textfield and paint the menu. - WebCore::ThemeHelperWin helper(i.context, rect); - ChromiumBridge::paintMenuList(helper.context(), + WebCore::ThemePainter painter(i.context, rect); + ChromiumBridge::paintMenuList(painter.context(), CP_DROPDOWNBUTTON, determineState(o), determineClassicState(o), - helper.rect()); + painter.drawRect()); return false; } @@ -476,13 +556,24 @@ int RenderThemeChromiumWin::popupInternalPaddingBottom(RenderStyle* style) const return menuListInternalPadding(style, BottomPadding); } -void RenderThemeChromiumWin::adjustButtonInnerStyle(RenderStyle* style) const +int RenderThemeChromiumWin::buttonInternalPaddingLeft() const +{ + return 3; +} + +int RenderThemeChromiumWin::buttonInternalPaddingRight() const +{ + return 3; +} + +int RenderThemeChromiumWin::buttonInternalPaddingTop() const +{ + return 1; +} + +int RenderThemeChromiumWin::buttonInternalPaddingBottom() const { - // This inner padding matches Firefox. - style->setPaddingTop(Length(1, Fixed)); - style->setPaddingRight(Length(3, Fixed)); - style->setPaddingBottom(Length(1, Fixed)); - style->setPaddingLeft(Length(3, Fixed)); + return 1; } // static @@ -499,7 +590,7 @@ unsigned RenderThemeChromiumWin::determineState(RenderObject* o) ControlPart appearance = o->style()->appearance(); if (!isEnabled(o)) result = TS_DISABLED; - else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance)) + else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance)) result = ETS_READONLY; // Readonly is supported on textfields. else if (isPressed(o)) // Active overrides hover and focused. result = TS_PRESSED; @@ -512,6 +603,20 @@ unsigned RenderThemeChromiumWin::determineState(RenderObject* o) return result; } +unsigned RenderThemeChromiumWin::determineSliderThumbState(RenderObject* o) +{ + unsigned result = TUS_NORMAL; + if (!isEnabled(o->parent())) + result = TUS_DISABLED; + else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent())) + result = TUS_FOCUSED; + else if (static_cast<RenderSlider*>(o->parent())->inDragMode()) + result = TUS_PRESSED; + else if (isHovered(o)) + result = TUS_HOT; + return result; +} + unsigned RenderThemeChromiumWin::determineClassicState(RenderObject* o) { unsigned result = 0; @@ -530,37 +635,57 @@ ThemeData RenderThemeChromiumWin::getThemeData(RenderObject* o) { ThemeData result; switch (o->style()->appearance()) { - case PushButtonPart: - case ButtonPart: - result.m_part = BP_PUSHBUTTON; - result.m_classicState = DFCS_BUTTONPUSH; - break; case CheckboxPart: result.m_part = BP_CHECKBOX; + result.m_state = determineState(o); result.m_classicState = DFCS_BUTTONCHECK; break; case RadioPart: result.m_part = BP_RADIOBUTTON; + result.m_state = determineState(o); result.m_classicState = DFCS_BUTTONRADIO; break; + case PushButtonPart: + case ButtonPart: + result.m_part = BP_PUSHBUTTON; + result.m_state = determineState(o); + result.m_classicState = DFCS_BUTTONPUSH; + break; + case SliderHorizontalPart: + result.m_part = TKP_TRACK; + result.m_state = TRS_NORMAL; + break; + case SliderVerticalPart: + result.m_part = TKP_TRACKVERT; + result.m_state = TRVS_NORMAL; + break; + case SliderThumbHorizontalPart: + result.m_part = TKP_THUMBBOTTOM; + result.m_state = determineSliderThumbState(o); + break; + case SliderThumbVerticalPart: + result.m_part = TKP_THUMBVERT; + result.m_state = determineSliderThumbState(o); + break; case ListboxPart: case MenulistPart: + case SearchFieldPart: case TextFieldPart: case TextAreaPart: - result.m_part = ETS_NORMAL; + result.m_part = EP_EDITTEXT; + result.m_state = determineState(o); break; } - result.m_state = determineState(o); result.m_classicState |= determineClassicState(o); return result; } bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o, - const RenderObject::PaintInfo& i, - const IntRect& r, - bool drawEdges) + const RenderObject::PaintInfo& i, + const IntRect& r, + bool drawEdges) { // Nasty hack to make us not paint the border on text fields with a // border-radius. Webkit paints elements with border-radius for us. @@ -572,12 +697,12 @@ bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o, const ThemeData& themeData = getThemeData(o); - WebCore::ThemeHelperWin helper(i.context, r); - ChromiumBridge::paintTextField(helper.context(), + WebCore::ThemePainter painter(i.context, r); + ChromiumBridge::paintTextField(painter.context(), themeData.m_part, themeData.m_state, themeData.m_classicState, - helper.rect(), + painter.drawRect(), o->style()->backgroundColor(), true, drawEdges); diff --git a/WebCore/rendering/RenderThemeChromiumWin.h b/WebCore/rendering/RenderThemeChromiumWin.h index 2d335c2..f1ef306 100644 --- a/WebCore/rendering/RenderThemeChromiumWin.h +++ b/WebCore/rendering/RenderThemeChromiumWin.h @@ -70,6 +70,8 @@ namespace WebCore { virtual int minimumMenuListSize(RenderStyle*) const; + virtual void adjustSliderThumbSize(RenderObject*) const; + virtual bool paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { return paintButton(o, i, r); } virtual void setCheckboxSize(RenderStyle*) const; @@ -82,6 +84,10 @@ namespace WebCore { virtual bool paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { return paintTextField(o, i, r); } + virtual bool paintSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual bool paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { return paintSliderTrack(o, i, r); } + virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); // MenuList refers to an unstyled menulist (meaning a menulist without @@ -104,7 +110,10 @@ namespace WebCore { virtual int popupInternalPaddingTop(RenderStyle*) const; virtual int popupInternalPaddingBottom(RenderStyle*) const; - virtual void adjustButtonInnerStyle(RenderStyle*) const; + virtual int buttonInternalPaddingLeft() const; + virtual int buttonInternalPaddingRight() const; + virtual int buttonInternalPaddingTop() const; + virtual int buttonInternalPaddingBottom() const; // Provide a way to pass the default font size from the Settings object // to the render theme. FIXME: http://b/1129186 A cleaner way would be @@ -119,6 +128,7 @@ namespace WebCore { private: unsigned determineState(RenderObject*); + unsigned determineSliderThumbState(RenderObject*); unsigned determineClassicState(RenderObject*); ThemeData getThemeData(RenderObject*); diff --git a/WebCore/rendering/RenderThemeMac.h b/WebCore/rendering/RenderThemeMac.h index 0d31603..63f1d97 100644 --- a/WebCore/rendering/RenderThemeMac.h +++ b/WebCore/rendering/RenderThemeMac.h @@ -60,6 +60,8 @@ public: virtual Color platformInactiveListBoxSelectionBackgroundColor() const; virtual Color platformInactiveListBoxSelectionForegroundColor() const; + virtual ScrollbarControlSize scrollbarControlSizeForPart(ControlPart) { return SmallScrollbar; } + virtual void platformColorsDidChange(); // System fonts. diff --git a/WebCore/rendering/RenderThemeMac.mm b/WebCore/rendering/RenderThemeMac.mm index b2d320a..7d1a60e 100644 --- a/WebCore/rendering/RenderThemeMac.mm +++ b/WebCore/rendering/RenderThemeMac.mm @@ -548,7 +548,7 @@ void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject* o) void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject* o) { bool oldPressed = [cell isHighlighted]; - bool pressed = (o->element() && o->element()->active()); + bool pressed = (o->node() && o->node()->active()); if (pressed != oldPressed) [cell setHighlighted:pressed]; } @@ -951,7 +951,11 @@ void RenderThemeMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle // Set the foreground color to black or gray when we have the aqua look. // Cast to RGB32 is to work around a compiler bug. - style->setColor(e->isEnabled() ? static_cast<RGBA32>(Color::black) : Color::darkGray); + bool isEnabled = true; + if (FormControlElement* formControlElement = toFormControlElement(e)) + isEnabled = formControlElement->isEnabled(); + + style->setColor(isEnabled ? static_cast<RGBA32>(Color::black) : Color::darkGray); // Set the button's vertical size. setSizeFromFont(style, menuListButtonSizes()); @@ -1451,8 +1455,8 @@ void RenderThemeMac::adjustSliderThumbSize(RenderObject* o) const height = size.height; } - o->style()->setWidth(Length(width, Fixed)); - o->style()->setHeight(Length(height, Fixed)); + o->style()->setWidth(Length(static_cast<int>(width * zoomLevel), Fixed)); + o->style()->setHeight(Length(static_cast<int>(height * zoomLevel), Fixed)); } #endif } @@ -1462,7 +1466,7 @@ void RenderThemeMac::adjustSliderThumbSize(RenderObject* o) const bool RenderThemeMac::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); if (!node) return false; @@ -1473,7 +1477,7 @@ bool RenderThemeMac::paintMediaFullscreenButton(RenderObject* o, const RenderObj bool RenderThemeMac::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); Node* mediaNode = node ? node->shadowAncestorNode() : 0; if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) return false; @@ -1489,7 +1493,7 @@ bool RenderThemeMac::paintMediaMuteButton(RenderObject* o, const RenderObject::P bool RenderThemeMac::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); Node* mediaNode = node ? node->shadowAncestorNode() : 0; if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) return false; @@ -1505,7 +1509,7 @@ bool RenderThemeMac::paintMediaPlayButton(RenderObject* o, const RenderObject::P bool RenderThemeMac::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); if (!node) return false; @@ -1516,7 +1520,7 @@ bool RenderThemeMac::paintMediaSeekBackButton(RenderObject* o, const RenderObjec bool RenderThemeMac::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); if (!node) return false; @@ -1527,7 +1531,7 @@ bool RenderThemeMac::paintMediaSeekForwardButton(RenderObject* o, const RenderOb bool RenderThemeMac::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); Node* mediaNode = node ? node->shadowAncestorNode() : 0; if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) return false; @@ -1551,7 +1555,7 @@ bool RenderThemeMac::paintMediaSliderTrack(RenderObject* o, const RenderObject:: bool RenderThemeMac::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); if (!node) return false; @@ -1562,7 +1566,7 @@ bool RenderThemeMac::paintMediaSliderThumb(RenderObject* o, const RenderObject:: bool RenderThemeMac::paintMediaTimelineContainer(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); if (!node) return false; @@ -1573,7 +1577,7 @@ bool RenderThemeMac::paintMediaTimelineContainer(RenderObject* o, const RenderOb bool RenderThemeMac::paintMediaCurrentTime(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); if (!node) return false; @@ -1584,7 +1588,7 @@ bool RenderThemeMac::paintMediaCurrentTime(RenderObject* o, const RenderObject:: bool RenderThemeMac::paintMediaTimeRemaining(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); if (!node) return false; diff --git a/WebCore/rendering/RenderThemeSafari.cpp b/WebCore/rendering/RenderThemeSafari.cpp index ef39a3e..cf73fdb 100644 --- a/WebCore/rendering/RenderThemeSafari.cpp +++ b/WebCore/rendering/RenderThemeSafari.cpp @@ -31,6 +31,7 @@ #include "Frame.h" #include "FrameView.h" #include "GraphicsContext.h" +#include "FormControlElement.h" #include "HTMLInputElement.h" #include "HTMLMediaElement.h" #include "HTMLNames.h" @@ -834,7 +835,7 @@ void RenderThemeSafari::adjustMenuListStyle(CSSStyleSelector* selector, RenderSt // Set the foreground color to black or gray when we have the aqua look. // Cast to RGB32 is to work around a compiler bug. - style->setColor(e->isEnabled() ? static_cast<RGBA32>(Color::black) : Color::darkGray); + style->setColor(e->isFormControlElement() && toFormControlElement(e)->isEnabled() ? static_cast<RGBA32>(Color::black) : Color::darkGray); // Set the button's vertical size. setButtonSize(style); @@ -1145,7 +1146,7 @@ bool RenderThemeSafari::paintMediaFullscreenButton(RenderObject* o, const Render bool RenderThemeSafari::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); Node* mediaNode = node ? node->shadowAncestorNode() : 0; if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) return false; @@ -1164,7 +1165,7 @@ bool RenderThemeSafari::paintMediaMuteButton(RenderObject* o, const RenderObject bool RenderThemeSafari::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); Node* mediaNode = node ? node->shadowAncestorNode() : 0; if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) return false; @@ -1203,7 +1204,7 @@ bool RenderThemeSafari::paintMediaSeekForwardButton(RenderObject* o, const Rende bool RenderThemeSafari::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - Node* node = o->element(); + Node* node = o->node(); Node* mediaNode = node ? node->shadowAncestorNode() : 0; if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) return false; diff --git a/WebCore/rendering/RenderThemeWin.cpp b/WebCore/rendering/RenderThemeWin.cpp index e4717a1..0518ef0 100644 --- a/WebCore/rendering/RenderThemeWin.cpp +++ b/WebCore/rendering/RenderThemeWin.cpp @@ -712,28 +712,29 @@ void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const } } -void RenderThemeWin::adjustButtonInnerStyle(RenderStyle* style) const +int RenderThemeWin::buttonInternalPaddingLeft() const { - // This inner padding matches Firefox. - style->setPaddingTop(Length(1, Fixed)); - style->setPaddingRight(Length(3, Fixed)); - style->setPaddingBottom(Length(1, Fixed)); - style->setPaddingLeft(Length(3, Fixed)); + return 3; } -bool RenderThemeWin::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +int RenderThemeWin::buttonInternalPaddingRight() const { - return paintTextField(o, i, r); + return 3; } -void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const -{ - // Override padding size to match AppKit text positioning. - const int padding = 1; - style->setPaddingLeft(Length(padding, Fixed)); - style->setPaddingRight(Length(padding, Fixed)); - style->setPaddingTop(Length(padding, Fixed)); - style->setPaddingBottom(Length(padding, Fixed)); +int RenderThemeWin::buttonInternalPaddingTop() const +{ + return 1; +} + +int RenderThemeWin::buttonInternalPaddingBottom() const +{ + return 1; +} + +bool RenderThemeWin::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + return paintTextField(o, i, r); } bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) diff --git a/WebCore/rendering/RenderThemeWin.h b/WebCore/rendering/RenderThemeWin.h index 5d5bd4b..664094f 100644 --- a/WebCore/rendering/RenderThemeWin.h +++ b/WebCore/rendering/RenderThemeWin.h @@ -92,9 +92,13 @@ public: virtual bool paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); virtual void adjustSliderThumbSize(RenderObject*) const; - virtual void adjustButtonInnerStyle(RenderStyle*) const; + virtual bool popupOptionSupportsTextIndent() const { return true; } + + virtual int buttonInternalPaddingLeft() const; + virtual int buttonInternalPaddingRight() const; + virtual int buttonInternalPaddingTop() const; + virtual int buttonInternalPaddingBottom() const; - virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; diff --git a/WebCore/rendering/RenderTreeAsText.cpp b/WebCore/rendering/RenderTreeAsText.cpp index cf7e027..2350491 100644 --- a/WebCore/rendering/RenderTreeAsText.cpp +++ b/WebCore/rendering/RenderTreeAsText.cpp @@ -170,13 +170,13 @@ static TextStream &operator<<(TextStream& ts, const RenderObject& o) if (o.style() && o.style()->zIndex()) ts << " zI: " << o.style()->zIndex(); - if (o.element()) { - String tagName = getTagName(o.element()); + if (o.node()) { + String tagName = getTagName(o.node()); if (!tagName.isEmpty()) { ts << " {" << tagName << "}"; // flag empty or unstyled AppleStyleSpan because we never // want to leave them in the DOM - if (isEmptyOrUnstyledAppleStyleSpan(o.element())) + if (isEmptyOrUnstyledAppleStyleSpan(o.node())) ts << " *empty or unstyled AppleStyleSpan*"; } } @@ -192,21 +192,19 @@ static TextStream &operator<<(TextStream& ts, const RenderObject& o) r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height()); if (adjustForTableCells && !text.firstTextBox()) adjustForTableCells = false; - } else if (o.isBox()) { - if (o.isRenderInline()) { - // FIXME: Would be better not to just dump 0, 0 as the x and y here. - const RenderInline& inlineFlow = static_cast<const RenderInline&>(o); - r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height()); - adjustForTableCells = false; - } else if (o.isTableCell()) { - // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like - // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are - // captured by the results. - const RenderTableCell& cell = static_cast<const RenderTableCell&>(o); - r = IntRect(cell.x(), cell.y() + cell.intrinsicPaddingTop(), cell.width(), cell.height() - cell.intrinsicPaddingTop() - cell.intrinsicPaddingBottom()); - } else - r = toRenderBox(&o)->frameRect(); - } + } else if (o.isRenderInline()) { + // FIXME: Would be better not to just dump 0, 0 as the x and y here. + const RenderInline& inlineFlow = *toRenderInline(&o); + r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height()); + adjustForTableCells = false; + } else if (o.isTableCell()) { + // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like + // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are + // captured by the results. + const RenderTableCell& cell = static_cast<const RenderTableCell&>(o); + r = IntRect(cell.x(), cell.y() + cell.intrinsicPaddingTop(), cell.width(), cell.height() - cell.intrinsicPaddingTop() - cell.intrinsicPaddingBottom()); + } else if (o.isBox()) + r = toRenderBox(&o)->frameRect(); // FIXME: Temporary in order to ensure compatibility with existing layout test results. if (adjustForTableCells) @@ -237,10 +235,10 @@ static TextStream &operator<<(TextStream& ts, const RenderObject& o) o.style()->textStrokeWidth() > 0) ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]"; - if (!o.isBox()) + if (!o.isBoxModelObject()) return ts; - const RenderBox& box = *toRenderBox(&o); + const RenderBoxModelObject& box = *toRenderBoxModelObject(&o); if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) { ts << " [border:"; @@ -405,7 +403,7 @@ void write(TextStream& ts, const RenderObject& o, int indent) view->layout(); RenderLayer* l = root->layer(); if (l) - writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()), indent + 1); + writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), indent + 1); } } } @@ -433,9 +431,9 @@ static void write(TextStream& ts, RenderLayer& l, ts << " scrollX " << l.scrollXOffset(); if (l.scrollYOffset()) ts << " scrollY " << l.scrollYOffset(); - if (l.renderer()->clientWidth() != l.scrollWidth()) + if (l.renderBox() && l.renderBox()->clientWidth() != l.scrollWidth()) ts << " scrollWidth " << l.scrollWidth(); - if (l.renderer()->clientHeight() != l.scrollHeight()) + if (l.renderBox() && l.renderBox()->clientHeight() != l.scrollHeight()) ts << " scrollHeight " << l.scrollHeight(); } @@ -459,7 +457,7 @@ static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLaye // Ensure our lists are up-to-date. l->updateZOrderLists(); - l->updateOverflowList(); + l->updateNormalFlowList(); bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect, rootLayer); Vector<RenderLayer*>* negList = l->negZOrderList(); @@ -474,10 +472,10 @@ static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLaye if (shouldPaint) write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, negList && negList->size() > 0, indent); - Vector<RenderLayer*>* overflowList = l->overflowList(); - if (overflowList) { - for (unsigned i = 0; i != overflowList->size(); ++i) - writeLayers(ts, rootLayer, overflowList->at(i), paintDirtyRect, indent); + Vector<RenderLayer*>* normalFlowList = l->normalFlowList(); + if (normalFlowList) { + for (unsigned i = 0; i != normalFlowList->size(); ++i) + writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, indent); } Vector<RenderLayer*>* posList = l->posZOrderList(); @@ -509,7 +507,7 @@ static String nodePosition(Node* node) static void writeSelection(TextStream& ts, const RenderObject* o) { - Node* n = o->element(); + Node* n = o->node(); if (!n || !n->isDocumentNode()) return; @@ -518,15 +516,15 @@ static void writeSelection(TextStream& ts, const RenderObject* o) if (!frame) return; - Selection selection = frame->selection()->selection(); + VisibleSelection selection = frame->selection()->selection(); if (selection.isCaret()) { - ts << "caret: position " << selection.start().offset() << " of " << nodePosition(selection.start().node()); + ts << "caret: position " << selection.start().m_offset << " of " << nodePosition(selection.start().node()); if (selection.affinity() == UPSTREAM) ts << " (upstream affinity)"; ts << "\n"; } else if (selection.isRange()) - ts << "selection start: position " << selection.start().offset() << " of " << nodePosition(selection.start().node()) << "\n" - << "selection end: position " << selection.end().offset() << " of " << nodePosition(selection.end().node()) << "\n"; + ts << "selection start: position " << selection.start().m_offset << " of " << nodePosition(selection.start().node()) << "\n" + << "selection end: position " << selection.end().m_offset << " of " << nodePosition(selection.end().node()) << "\n"; } String externalRepresentation(RenderObject* o) @@ -542,7 +540,7 @@ String externalRepresentation(RenderObject* o) o->view()->frameView()->layout(); if (o->hasLayer()) { RenderLayer* l = toRenderBox(o)->layer(); - writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height())); + writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height())); writeSelection(ts, o); } return ts.release(); diff --git a/WebCore/rendering/RenderVideo.cpp b/WebCore/rendering/RenderVideo.cpp index be75997..d6e98e7 100644 --- a/WebCore/rendering/RenderVideo.cpp +++ b/WebCore/rendering/RenderVideo.cpp @@ -125,12 +125,9 @@ void RenderVideo::updatePlayer() return; } - // FIXME: This doesn't work correctly with transforms. - FloatPoint absPos = localToAbsolute(); IntRect videoBounds = videoBox(); - videoBounds.move(absPos.x(), absPos.y()); mediaPlayer->setFrameView(document()->view()); - mediaPlayer->setRect(videoBounds); + mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height())); mediaPlayer->setVisible(true); } diff --git a/WebCore/rendering/RenderView.cpp b/WebCore/rendering/RenderView.cpp index 7ce4998..ab2f085 100644 --- a/WebCore/rendering/RenderView.cpp +++ b/WebCore/rendering/RenderView.cpp @@ -27,7 +27,15 @@ #include "Frame.h" #include "FrameView.h" #include "GraphicsContext.h" +#include "HitTestResult.h" #include "RenderLayer.h" +#include "RenderSelectionInfo.h" +#include "RenderWidget.h" +#include "TransformState.h" + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerCompositor.h" +#endif #ifdef ANDROID_LAYOUT #include "Settings.h" @@ -139,39 +147,20 @@ void RenderView::layout() setNeedsLayout(false); } -FloatPoint RenderView::localToAbsolute(FloatPoint localPoint, bool fixed, bool) const -{ - // This disables the css position:fixed to the Browser window. Instead - // the fixed element will be always fixed to the top page. -#ifndef ANDROID_DISABLE_POSITION_FIXED - if (fixed && m_frameView) - localPoint += m_frameView->scrollOffset(); -#endif - return localPoint; -} - -FloatPoint RenderView::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool) const -{ - // This disables the css position:fixed to the Browser window. Instead - // the fixed element will be always fixed to the top page. -#ifndef ANDROID_DISABLE_POSITION_FIXED - if (fixed && m_frameView) - containerPoint -= m_frameView->scrollOffset(); -#endif - return containerPoint; -} - -FloatQuad RenderView::localToContainerQuad(const FloatQuad& localQuad, RenderBox* repaintContainer, bool fixed) const +void RenderView::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool /*useTransforms*/, TransformState& transformState) const { // If a container was specified, and was not 0 or the RenderView, // then we should have found it by now. ASSERT_UNUSED(repaintContainer, !repaintContainer || repaintContainer == this); - FloatQuad quad = localQuad; if (fixed && m_frameView) - quad += m_frameView->scrollOffset(); + transformState.move(m_frameView->scrollOffset()); +} - return quad; +void RenderView::mapAbsoluteToLocalPoint(bool fixed, bool /*useTransforms*/, TransformState& transformState) const +{ + if (fixed && m_frameView) + transformState.move(-m_frameView->scrollOffset()); } void RenderView::paint(PaintInfo& paintInfo, int tx, int ty) @@ -230,12 +219,20 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, int, int) } } -void RenderView::repaintViewRectangle(const IntRect& ur, bool immediate) +bool RenderView::shouldRepaint(const IntRect& r) const { - if (printing() || ur.width() == 0 || ur.height() == 0) - return; + if (printing() || r.width() == 0 || r.height() == 0) + return false; if (!m_frameView) + return false; + + return true; +} + +void RenderView::repaintViewRectangle(const IntRect& ur, bool immediate) +{ + if (!shouldRepaint(ur)) return; // We always just invalidate the root view, since we could be an iframe that is clipped out @@ -258,7 +255,25 @@ void RenderView::repaintViewRectangle(const IntRect& ur, bool immediate) } } -void RenderView::computeRectForRepaint(IntRect& rect, RenderBox* repaintContainer, bool fixed) +void RenderView::repaintRectangleInViewAndCompositedLayers(const IntRect& ur, bool immediate) +{ + if (!shouldRepaint(ur)) + return; + + repaintViewRectangle(ur, immediate); + +#if USE(ACCELERATED_COMPOSITING) + // If we're a frame, repaintViewRectangle will have repainted via a RenderObject in the + // parent document. + if (document()->ownerElement()) + return; + + if (compositor()->inCompositingMode()) + compositor()->repaintCompositedLayersAbsoluteRect(ur); +#endif +} + +void RenderView::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) { // If a container was specified, and was not 0 or the RenderView, // then we should have found it by now. @@ -294,21 +309,11 @@ static RenderObject* rendererAfterPosition(RenderObject* object, unsigned offset return child ? child : object->nextInPreOrderAfterChildren(); } -IntRect RenderView::selectionRect(bool clipToVisibleContent) -{ - // The virtual selectionRect() should never be called on the RenderView. - // We assert because there used to be ambiguity between - // RenderView::selectionRect(bool) and - // virtual RenderObject::selectionRect(bool) const - ASSERT_NOT_REACHED(); - return RenderBlock::selectionRect(clipToVisibleContent); -} - IntRect RenderView::selectionBounds(bool clipToVisibleContent) const { document()->updateRendering(); - typedef HashMap<RenderObject*, SelectionInfo*> SelectionMap; + typedef HashMap<RenderObject*, RenderSelectionInfo*> SelectionMap; SelectionMap selectedObjects; RenderObject* os = m_selectionStart; @@ -316,13 +321,13 @@ IntRect RenderView::selectionBounds(bool clipToVisibleContent) const while (os && os != stop) { if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. - selectedObjects.set(os, new SelectionInfo(os, clipToVisibleContent)); + selectedObjects.set(os, new RenderSelectionInfo(os, clipToVisibleContent)); RenderBlock* cb = os->containingBlock(); while (cb && !cb->isRenderView()) { - SelectionInfo* blockInfo = selectedObjects.get(cb); + RenderSelectionInfo* blockInfo = selectedObjects.get(cb); if (blockInfo) break; - selectedObjects.set(cb, new SelectionInfo(cb, clipToVisibleContent)); + selectedObjects.set(cb, new RenderSelectionInfo(cb, clipToVisibleContent)); cb = cb->containingBlock(); } } @@ -334,13 +339,28 @@ IntRect RenderView::selectionBounds(bool clipToVisibleContent) const IntRect selRect; SelectionMap::iterator end = selectedObjects.end(); for (SelectionMap::iterator i = selectedObjects.begin(); i != end; ++i) { - SelectionInfo* info = i->second; + RenderSelectionInfo* info = i->second; selRect.unite(info->rect()); delete info; } return selRect; } +#if USE(ACCELERATED_COMPOSITING) +// Compositing layer dimensions take outline size into account, so we have to recompute layer +// bounds when it changes. +// FIXME: This is ugly; it would be nice to have a better way to do this. +void RenderView::setMaximalOutlineSize(int o) +{ + if (o != m_maximalOutlineSize) { + m_maximalOutlineSize = o; + + if (m_frameView) + m_frameView->updateCompositingLayers(FrameView::ForcedCompositingUpdate); + } +} +#endif + void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos) { // Make sure both our start and end objects are defined. @@ -359,14 +379,14 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e int oldEndPos = m_selectionEndPos; // Objects each have a single selection rect to examine. - typedef HashMap<RenderObject*, SelectionInfo*> SelectedObjectMap; + typedef HashMap<RenderObject*, RenderSelectionInfo*> SelectedObjectMap; SelectedObjectMap oldSelectedObjects; SelectedObjectMap newSelectedObjects; // Blocks contain selected objects and fill gaps between them, either on the left, right, or in between lines and blocks. // In order to get the repaint rect right, we have to examine left, middle, and right rects individually, since otherwise // the union of those rects might remain the same even when changes have occurred. - typedef HashMap<RenderBlock*, BlockSelectionInfo*> SelectedBlockMap; + typedef HashMap<RenderBlock*, RenderBlockSelectionInfo*> SelectedBlockMap; SelectedBlockMap oldSelectedBlocks; SelectedBlockMap newSelectedBlocks; @@ -375,13 +395,13 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e while (os && os != stop) { if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. - oldSelectedObjects.set(os, new SelectionInfo(os, true)); + oldSelectedObjects.set(os, new RenderSelectionInfo(os, true)); RenderBlock* cb = os->containingBlock(); while (cb && !cb->isRenderView()) { - BlockSelectionInfo* blockInfo = oldSelectedBlocks.get(cb); + RenderBlockSelectionInfo* blockInfo = oldSelectedBlocks.get(cb); if (blockInfo) break; - oldSelectedBlocks.set(cb, new BlockSelectionInfo(cb)); + oldSelectedBlocks.set(cb, new RenderBlockSelectionInfo(cb)); cb = cb->containingBlock(); } } @@ -424,13 +444,13 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e o = start; while (o && o != stop) { if ((o->canBeSelectionLeaf() || o == start || o == end) && o->selectionState() != SelectionNone) { - newSelectedObjects.set(o, new SelectionInfo(o, true)); + newSelectedObjects.set(o, new RenderSelectionInfo(o, true)); RenderBlock* cb = o->containingBlock(); while (cb && !cb->isRenderView()) { - BlockSelectionInfo* blockInfo = newSelectedBlocks.get(cb); + RenderBlockSelectionInfo* blockInfo = newSelectedBlocks.get(cb); if (blockInfo) break; - newSelectedBlocks.set(cb, new BlockSelectionInfo(cb)); + newSelectedBlocks.set(cb, new RenderBlockSelectionInfo(cb)); cb = cb->containingBlock(); } } @@ -451,14 +471,14 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e // Have any of the old selected objects changed compared to the new selection? for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) { RenderObject* obj = i->first; - SelectionInfo* newInfo = newSelectedObjects.get(obj); - SelectionInfo* oldInfo = i->second; + RenderSelectionInfo* newInfo = newSelectedObjects.get(obj); + RenderSelectionInfo* oldInfo = i->second; if (!newInfo || oldInfo->rect() != newInfo->rect() || oldInfo->state() != newInfo->state() || (m_selectionStart == obj && oldStartPos != m_selectionStartPos) || (m_selectionEnd == obj && oldEndPos != m_selectionEndPos)) { - repaintViewRectangle(oldInfo->rect()); + oldInfo->repaint(); if (newInfo) { - repaintViewRectangle(newInfo->rect()); + newInfo->repaint(); newSelectedObjects.remove(obj); delete newInfo; } @@ -469,8 +489,8 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e // Any new objects that remain were not found in the old objects dict, and so they need to be updated. SelectedObjectMap::iterator newObjectsEnd = newSelectedObjects.end(); for (SelectedObjectMap::iterator i = newSelectedObjects.begin(); i != newObjectsEnd; ++i) { - SelectionInfo* newInfo = i->second; - repaintViewRectangle(newInfo->rect()); + RenderSelectionInfo* newInfo = i->second; + newInfo->repaint(); delete newInfo; } @@ -478,12 +498,12 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e SelectedBlockMap::iterator oldBlocksEnd = oldSelectedBlocks.end(); for (SelectedBlockMap::iterator i = oldSelectedBlocks.begin(); i != oldBlocksEnd; ++i) { RenderBlock* block = i->first; - BlockSelectionInfo* newInfo = newSelectedBlocks.get(block); - BlockSelectionInfo* oldInfo = i->second; + RenderBlockSelectionInfo* newInfo = newSelectedBlocks.get(block); + RenderBlockSelectionInfo* oldInfo = i->second; if (!newInfo || oldInfo->rects() != newInfo->rects() || oldInfo->state() != newInfo->state()) { - repaintViewRectangle(oldInfo->rects()); + oldInfo->repaint(); if (newInfo) { - repaintViewRectangle(newInfo->rects()); + newInfo->repaint(); newSelectedBlocks.remove(block); delete newInfo; } @@ -494,8 +514,8 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e // Any new blocks that remain were not found in the old blocks dict, and so they need to be updated. SelectedBlockMap::iterator newBlocksEnd = newSelectedBlocks.end(); for (SelectedBlockMap::iterator i = newSelectedBlocks.begin(); i != newBlocksEnd; ++i) { - BlockSelectionInfo* newInfo = i->second; - repaintViewRectangle(newInfo->rects()); + RenderBlockSelectionInfo* newInfo = i->second; + newInfo->repaint(); delete newInfo; } } @@ -518,17 +538,17 @@ bool RenderView::printing() const void RenderView::updateWidgetPositions() { - RenderObjectSet::iterator end = m_widgets.end(); - for (RenderObjectSet::iterator it = m_widgets.begin(); it != end; ++it) + RenderWidgetSet::iterator end = m_widgets.end(); + for (RenderWidgetSet::iterator it = m_widgets.begin(); it != end; ++it) (*it)->updateWidgetPosition(); } -void RenderView::addWidget(RenderObject* o) +void RenderView::addWidget(RenderWidget* o) { m_widgets.add(o); } -void RenderView::removeWidget(RenderObject* o) +void RenderView::removeWidget(RenderWidget* o) { m_widgets.remove(o); } @@ -598,9 +618,17 @@ int RenderView::viewWidth() const return width; } +float RenderView::zoomFactor() const +{ + if (m_frameView->frame() && m_frameView->frame()->shouldApplyPageZoom()) + return m_frameView->frame()->zoomFactor(); + + return 1.0f; +} + // The idea here is to take into account what object is moving the pagination point, and // thus choose the best place to chop it. -void RenderView::setBestTruncatedAt(int y, RenderBox* forRenderer, bool forcedBreak) +void RenderView::setBestTruncatedAt(int y, RenderBoxModelObject* forRenderer, bool forcedBreak) { // Nobody else can set a page break once we have a forced break. if (m_forcedPageBreak) @@ -613,9 +641,10 @@ void RenderView::setBestTruncatedAt(int y, RenderBox* forRenderer, bool forcedBr return; } - // prefer the widest object who tries to move the pagination point - if (forRenderer->width() > m_truncatorWidth) { - m_truncatorWidth = forRenderer->width(); + // Prefer the widest object that tries to move the pagination point + IntRect boundingBox = forRenderer->borderBoundingBox(); + if (boundingBox.width() > m_truncatorWidth) { + m_truncatorWidth = boundingBox.width(); m_bestTruncatedAt = y; } } @@ -629,4 +658,49 @@ void RenderView::pushLayoutState(RenderObject* root) m_layoutState = new (renderArena()) LayoutState(root); } +void RenderView::updateHitTestResult(HitTestResult& result, const IntPoint& point) +{ + if (result.innerNode()) + return; + + Node* node = document()->documentElement(); + if (node) { + result.setInnerNode(node); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(node); + result.setLocalPoint(point); + } +} + +#if USE(ACCELERATED_COMPOSITING) +bool RenderView::usesCompositing() const +{ + return m_compositor && m_compositor->inCompositingMode(); +} + +RenderLayerCompositor* RenderView::compositor() +{ + if (!m_compositor) + m_compositor.set(new RenderLayerCompositor(this)); + + return m_compositor.get(); +} +#endif + +void RenderView::didMoveOnscreen() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_compositor) + m_compositor->didMoveOnscreen(); +#endif +} + +void RenderView::willMoveOffscreen() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_compositor) + m_compositor->willMoveOffscreen(); +#endif +} + } // namespace WebCore diff --git a/WebCore/rendering/RenderView.h b/WebCore/rendering/RenderView.h index 8e7bf95..1c6925c 100644 --- a/WebCore/rendering/RenderView.h +++ b/WebCore/rendering/RenderView.h @@ -25,12 +25,18 @@ #define RenderView_h #include "FrameView.h" -#include "Frame.h" #include "LayoutState.h" #include "RenderBlock.h" +#include <wtf/OwnPtr.h> namespace WebCore { +class RenderWidget; + +#if USE(ACCELERATED_COMPOSITING) +class RenderLayerCompositor; +#endif + class RenderView : public RenderBlock { public: RenderView(Node*, FrameView*); @@ -44,8 +50,6 @@ public: virtual void calcWidth(); virtual void calcHeight(); virtual void calcPrefWidths(); - virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; - virtual FloatPoint absoluteToLocal(FloatPoint containerPoint, bool fixed = false, bool useTransforms = false) const; int docHeight() const; int docWidth() const; @@ -54,14 +58,15 @@ public: int viewHeight() const; int viewWidth() const; - float zoomFactor() const { return m_frameView->frame() && m_frameView->frame()->shouldApplyPageZoom() ? m_frameView->frame()->zoomFactor() : 1.0f; } + float zoomFactor() const; FrameView* frameView() const { return m_frameView; } - virtual bool hasOverhangingFloats() { return false; } - - virtual void computeRectForRepaint(IntRect&, RenderBox* repaintContainer, bool fixed = false); + virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); virtual void repaintViewRectangle(const IntRect&, bool immediate = false); + // Repaint the view, and all composited layers that intersect the given absolute rectangle. + // FIXME: ideally we'd never have to do this, if all repaints are container-relative. + virtual void repaintRectangleInViewAndCompositedLayers(const IntRect&, bool immediate = false); virtual void paint(PaintInfo&, int tx, int ty); virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); @@ -75,7 +80,7 @@ public: void setPrintImages(bool enable) { m_printImages = enable; } bool printImages() const { return m_printImages; } void setTruncatedAt(int y) { m_truncatedAt = y; m_bestTruncatedAt = m_truncatorWidth = 0; m_forcedPageBreak = false; } - void setBestTruncatedAt(int y, RenderBox* forRenderer, bool forcedBreak = false); + void setBestTruncatedAt(int y, RenderBoxModelObject* forRenderer, bool forcedBreak = false); int bestTruncatedAt() const { return m_bestTruncatedAt; } int truncatedAt() const { return m_truncatedAt; } @@ -85,19 +90,23 @@ public: IntRect selectionBounds(bool clipToVisibleContent = true) const; +#if USE(ACCELERATED_COMPOSITING) + void setMaximalOutlineSize(int o); +#else void setMaximalOutlineSize(int o) { m_maximalOutlineSize = o; } +#endif int maximalOutlineSize() const { return m_maximalOutlineSize; } virtual IntRect viewRect() const; - virtual void selectionStartEnd(int& startPos, int& endPos) const; + void selectionStartEnd(int& startPos, int& endPos) const; IntRect printRect() const { return m_printRect; } void setPrintRect(const IntRect& r) { m_printRect = r; } void updateWidgetPositions(); - void addWidget(RenderObject*); - void removeWidget(RenderObject*); + void addWidget(RenderWidget*); + void removeWidget(RenderWidget*); // layoutDelta is used transiently during layout to store how far an object has moved from its // last layout location, in order to repaint correctly. @@ -144,12 +153,24 @@ public: void disableLayoutState() { m_layoutStateDisableCount++; } void enableLayoutState() { ASSERT(m_layoutStateDisableCount > 0); m_layoutStateDisableCount--; } + virtual void updateHitTestResult(HitTestResult&, const IntPoint&); + + // Notifications that this view became visible in a window, or will be + // removed from the window. + void didMoveOnscreen(); + void willMoveOffscreen(); + +#if USE(ACCELERATED_COMPOSITING) + RenderLayerCompositor* compositor(); + bool usesCompositing() const; +#endif + protected: - virtual FloatQuad localToContainerQuad(const FloatQuad&, RenderBox* repaintContainer, bool fixed = false) const; + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; + virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const; private: - // selectionRect should never be called on a RenderView - virtual IntRect selectionRect(bool); + bool shouldRepaint(const IntRect& r) const; protected: FrameView* m_frameView; @@ -166,9 +187,9 @@ protected: int m_maximalOutlineSize; // Used to apply a fudge factor to dirty-rect checks on blocks/tables. IntRect m_printRect; // Used when printing. - typedef HashSet<RenderObject*> RenderObjectSet; + typedef HashSet<RenderWidget*> RenderWidgetSet; - RenderObjectSet m_widgets; + RenderWidgetSet m_widgets; private: int m_bestTruncatedAt; @@ -176,8 +197,27 @@ private: bool m_forcedPageBreak; LayoutState* m_layoutState; unsigned m_layoutStateDisableCount; +#if USE(ACCELERATED_COMPOSITING) + OwnPtr<RenderLayerCompositor> m_compositor; +#endif }; +inline RenderView* toRenderView(RenderObject* o) +{ + ASSERT(!o || o->isRenderView()); + return static_cast<RenderView*>(o); +} + +inline const RenderView* toRenderView(const RenderObject* o) +{ + ASSERT(!o || o->isRenderView()); + return static_cast<const RenderView*>(o); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderView(const RenderView*); + + // Stack-based class to assist with LayoutState push/pop class LayoutStateMaintainer : Noncopyable { public: diff --git a/WebCore/rendering/RenderWidget.cpp b/WebCore/rendering/RenderWidget.cpp index 9bf8111..2f30c59 100644 --- a/WebCore/rendering/RenderWidget.cpp +++ b/WebCore/rendering/RenderWidget.cpp @@ -91,20 +91,19 @@ void RenderWidget::destroy() if (hasOverrideSize()) setOverrideSize(-1); - RenderLayer* layer = m_layer; - RenderArena* arena = renderArena(); - - if (layer) - layer->clearClipRects(); - if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent())) RenderBlock::removePercentHeightDescendant(this); + if (hasLayer()) { + layer()->clearClipRects(); + destroyLayer(); + } + + // Grab the arena from node()->document()->renderArena() before clearing the node pointer. + // Clear the node before deref-ing, as this may be deleted when deref is called. + RenderArena* arena = renderArena(); setNode(0); deref(arena); - - if (layer) - layer->destroy(arena); } RenderWidget::~RenderWidget() @@ -115,9 +114,9 @@ RenderWidget::~RenderWidget() void RenderWidget::setWidgetGeometry(const IntRect& frame) { - if (element() && m_widget->frameRect() != frame) { + if (node() && m_widget->frameRect() != frame) { RenderArena* arena = ref(); - RefPtr<Node> protectedElement(element()); + RefPtr<Node> protectedElement(node()); m_widget->setFrameRect(frame); deref(arena); } @@ -157,7 +156,7 @@ void RenderWidget::layout() setNeedsLayout(false); } -void RenderWidget::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderReplaced::styleDidChange(diff, oldStyle); if (m_widget) { @@ -192,6 +191,17 @@ void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true); #endif + bool clipToBorderRadius = style()->overflowX() != OVISIBLE && style()->hasBorderRadius(); + if (clipToBorderRadius) { + // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. + paintInfo.context->save(); + paintInfo.context->addRoundedRectClip(IntRect(tx, ty, width(), height()), + style()->borderTopLeftRadius(), + style()->borderTopRightRadius(), + style()->borderBottomLeftRadius(), + style()->borderBottomRightRadius()); + } + 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 @@ -203,9 +213,14 @@ void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) m_widget->paint(paintInfo.context, paintInfo.rect); } + if (clipToBorderRadius) + paintInfo.context->restore(); + // Paint a partially transparent wash over selected widgets. - if (isSelected() && !document()->printing()) + if (isSelected() && !document()->printing()) { + // FIXME: selectionRect() is in absolute, not painting coordinates. paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor()); + } } void RenderWidget::deref(RenderArena *arena) @@ -228,22 +243,22 @@ void RenderWidget::updateWidgetPosition() IntRect newBounds(absPos.x(), absPos.y(), w, h); IntRect oldBounds(m_widget->frameRect()); - if (newBounds != oldBounds) { - // The widget changed positions. Update the frame geometry. - if (checkForRepaintDuringLayout()) { - RenderView* v = view(); - if (!v->printing()) { - v->repaintViewRectangle(oldBounds); - v->repaintViewRectangle(newBounds); - } - } - + bool boundsChanged = newBounds != oldBounds; + if (boundsChanged) { RenderArena* arena = ref(); - element()->ref(); + node()->ref(); m_widget->setFrameRect(newBounds); - element()->deref(); + node()->deref(); deref(arena); } + + // if the frame bounds got changed, or if view needs layout (possibly indicating + // content size is wrong) we have to do a layout to set the right widget size + if (m_widget->isFrameView()) { + FrameView* frameView = static_cast<FrameView*>(m_widget); + if (boundsChanged || frameView->needsLayout()) + frameView->layout(); + } } void RenderWidget::setSelectionState(SelectionState state) @@ -271,7 +286,7 @@ bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& res bool inside = RenderReplaced::nodeAtPoint(request, result, x, y, tx, ty, action); // Check to see if we are really over the widget itself (and not just in the border/padding area). - if (inside && !hadResult && result.innerNode() == element()) + if (inside && !hadResult && result.innerNode() == node()) result.setIsOverWidget(contentBoxRect().contains(result.localPoint())); return inside; } diff --git a/WebCore/rendering/RenderWidget.h b/WebCore/rendering/RenderWidget.h index a64bb54..cca2165 100644 --- a/WebCore/rendering/RenderWidget.h +++ b/WebCore/rendering/RenderWidget.h @@ -50,14 +50,14 @@ public: virtual void setSelectionState(SelectionState); - virtual void updateWidgetPosition(); + void updateWidgetPosition(); virtual void setWidget(Widget*); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); protected: - virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); private: void setWidgetGeometry(const IntRect&); diff --git a/WebCore/rendering/RootInlineBox.cpp b/WebCore/rendering/RootInlineBox.cpp index 6d42aa7..e582e5e 100644 --- a/WebCore/rendering/RootInlineBox.cpp +++ b/WebCore/rendering/RootInlineBox.cpp @@ -74,10 +74,28 @@ void RootInlineBox::detachEllipsisBox(RenderArena* arena) } } +int RootInlineBox::height() const +{ + const Font& font = renderer()->style(m_firstLine)->font(); + int result = font.height(); + bool strictMode = renderer()->document()->inStrictMode(); + if (!strictMode && !hasTextChildren() && !boxModelObject()->hasHorizontalBordersOrPadding()) { + int bottom = bottomOverflow(); + if (y() + result > bottom) + result = bottom - y(); + } + return result; +} + +RenderLineBoxList* RootInlineBox::rendererLineBoxes() const +{ + return block()->lineBoxes(); +} + void RootInlineBox::clearTruncation() { if (m_hasEllipsisBox) { - detachEllipsisBox(m_object->renderArena()); + detachEllipsisBox(renderer()->renderArena()); InlineFlowBox::clearTruncation(); } } @@ -98,9 +116,9 @@ void RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, in InlineBox* markupBox) { // Create an ellipsis box. - EllipsisBox* ellipsisBox = new (m_object->renderArena()) EllipsisBox(m_object, ellipsisStr, this, - ellipsisWidth - (markupBox ? markupBox->width() : 0), - yPos(), height(), baseline(), !prevRootBox(), + EllipsisBox* ellipsisBox = new (renderer()->renderArena()) EllipsisBox(renderer(), ellipsisStr, this, + ellipsisWidth - (markupBox ? markupBox->width() : 0), height(), + y(), !prevRootBox(), markupBox); if (!gEllipsisBoxMap) @@ -108,8 +126,8 @@ void RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, in gEllipsisBoxMap->add(this, ellipsisBox); m_hasEllipsisBox = true; - if (ltr && (xPos() + width() + ellipsisWidth) <= blockEdge) { - ellipsisBox->m_x = xPos() + width(); + if (ltr && (x() + width() + ellipsisWidth) <= blockEdge) { + ellipsisBox->m_x = x() + width(); return; } @@ -130,7 +148,7 @@ int RootInlineBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo& paintInfo, int tx, int ty) const { - if (m_hasEllipsisBox && object()->shouldPaintWithinRoot(paintInfo) && object()->style()->visibility() == VISIBLE && + if (m_hasEllipsisBox && renderer()->shouldPaintWithinRoot(paintInfo) && renderer()->style()->visibility() == VISIBLE && paintInfo.phase == PaintPhaseForeground) ellipsisBox()->paint(paintInfo, tx, ty); } @@ -139,7 +157,7 @@ void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo& paintInfo, int tx, void RootInlineBox::addHighlightOverflow() { - Frame* frame = object()->document()->frame(); + Frame* frame = renderer()->document()->frame(); if (!frame) return; Page* page = frame->page(); @@ -148,17 +166,17 @@ void RootInlineBox::addHighlightOverflow() // Highlight acts as a selection inflation. FloatRect rootRect(0, selectionTop(), width(), selectionHeight()); - IntRect inflatedRect = enclosingIntRect(page->chrome()->client()->customHighlightRect(object()->node(), object()->style()->highlight(), rootRect)); + IntRect inflatedRect = enclosingIntRect(page->chrome()->client()->customHighlightRect(renderer()->node(), renderer()->style()->highlight(), rootRect)); setHorizontalOverflowPositions(min(leftOverflow(), inflatedRect.x()), max(rightOverflow(), inflatedRect.right())); setVerticalOverflowPositions(min(topOverflow(), inflatedRect.y()), max(bottomOverflow(), inflatedRect.bottom())); } void RootInlineBox::paintCustomHighlight(RenderObject::PaintInfo& paintInfo, int tx, int ty, const AtomicString& highlightType) { - if (!object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) + if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) return; - Frame* frame = object()->document()->frame(); + Frame* frame = renderer()->document()->frame(); if (!frame) return; Page* page = frame->page(); @@ -166,10 +184,10 @@ void RootInlineBox::paintCustomHighlight(RenderObject::PaintInfo& paintInfo, int return; // Get the inflated rect so that we can properly hit test. - FloatRect rootRect(tx + xPos(), ty + selectionTop(), width(), selectionHeight()); - FloatRect inflatedRect = page->chrome()->client()->customHighlightRect(object()->node(), highlightType, rootRect); + FloatRect rootRect(tx + x(), ty + selectionTop(), width(), selectionHeight()); + FloatRect inflatedRect = page->chrome()->client()->customHighlightRect(renderer()->node(), highlightType, rootRect); if (inflatedRect.intersects(paintInfo.rect)) - page->chrome()->client()->paintCustomHighlight(object()->node(), highlightType, rootRect, rootRect, false, true); + page->chrome()->client()->paintCustomHighlight(renderer()->node(), highlightType, rootRect, rootRect, false, true); } #endif @@ -179,7 +197,7 @@ void RootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) InlineFlowBox::paint(paintInfo, tx, ty); paintEllipsisBox(paintInfo, tx, ty); #if PLATFORM(MAC) - RenderStyle* styleToUse = object()->style(m_firstLine); + RenderStyle* styleToUse = renderer()->style(m_firstLine); if (styleToUse->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) paintCustomHighlight(paintInfo, tx, ty, styleToUse->highlight()); #endif @@ -189,7 +207,7 @@ bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re { if (m_hasEllipsisBox && visibleToHitTesting()) { if (ellipsisBox()->nodeAtPoint(request, result, x, y, tx, ty)) { - object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } } @@ -210,10 +228,10 @@ void RootInlineBox::adjustPosition(int dx, int dy) void RootInlineBox::childRemoved(InlineBox* box) { - if (box->object() == m_lineBreakObj) + if (box->renderer() == m_lineBreakObj) setLineBreakInfo(0, 0, BidiStatus()); - for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == box->object(); prev = prev->prevRootBox()) { + for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == box->renderer(); prev = prev->prevRootBox()) { prev->setLineBreakInfo(0, 0, BidiStatus()); prev->markDirty(); } @@ -232,12 +250,12 @@ GapRects RootInlineBox::fillLineSelectionGap(int selTop, int selHeight, RenderBl InlineBox* firstBox = firstSelectedBox(); InlineBox* lastBox = lastSelectedBox(); if (leftGap) - result.uniteLeft(block()->fillLeftSelectionGap(firstBox->parent()->object(), - firstBox->xPos(), selTop, selHeight, + result.uniteLeft(block()->fillLeftSelectionGap(firstBox->parent()->renderer(), + firstBox->x(), selTop, selHeight, rootBlock, blockX, blockY, tx, ty, paintInfo)); if (rightGap) - result.uniteRight(block()->fillRightSelectionGap(lastBox->parent()->object(), - lastBox->xPos() + lastBox->width(), selTop, selHeight, + result.uniteRight(block()->fillRightSelectionGap(lastBox->parent()->renderer(), + lastBox->x() + lastBox->width(), selTop, selHeight, rootBlock, blockX, blockY, tx, ty, paintInfo)); // When dealing with bidi text, a non-contiguous selection region is possible. @@ -249,15 +267,15 @@ GapRects RootInlineBox::fillLineSelectionGap(int selTop, int selHeight, RenderBl // We can see that the |bbb| run is not part of the selection while the runs around it are. if (firstBox && firstBox != lastBox) { // Now fill in any gaps on the line that occurred between two selected elements. - int lastX = firstBox->xPos() + firstBox->width(); + int lastX = firstBox->x() + firstBox->width(); bool isPreviousBoxSelected = firstBox->selectionState() != RenderObject::SelectionNone; for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) { if (box->selectionState() != RenderObject::SelectionNone) { - if (isPreviousBoxSelected) // Selection may be non-contiguous, see comment above. - result.uniteCenter(block()->fillHorizontalSelectionGap(box->parent()->object(), + if (isPreviousBoxSelected) // VisibleSelection may be non-contiguous, see comment above. + result.uniteCenter(block()->fillHorizontalSelectionGap(box->parent()->renderer(), lastX + tx, selTop + ty, - box->xPos() - lastX, selHeight, paintInfo)); - lastX = box->xPos() + box->width(); + box->x() - lastX, selHeight, paintInfo)); + lastX = box->x() + box->width(); } if (box == lastBox) break; @@ -326,10 +344,10 @@ int RootInlineBox::selectionTop() // This line has actually been moved further down, probably from a large line-height, but possibly because the // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the previous // line's bottom overflow if the offsets are greater on both sides. - int prevLeft = block()->leftOffset(prevBottom); - int prevRight = block()->rightOffset(prevBottom); - int newLeft = block()->leftOffset(selectionTop); - int newRight = block()->rightOffset(selectionTop); + int prevLeft = block()->leftOffset(prevBottom, !prevRootBox()); + int prevRight = block()->rightOffset(prevBottom, !prevRootBox()); + int newLeft = block()->leftOffset(selectionTop, !prevRootBox()); + int newRight = block()->rightOffset(selectionTop, !prevRootBox()); if (prevLeft > newLeft || prevRight < newRight) return selectionTop; } @@ -339,12 +357,12 @@ int RootInlineBox::selectionTop() RenderBlock* RootInlineBox::block() const { - return static_cast<RenderBlock*>(m_object); + return toRenderBlock(renderer()); } static bool isEditableLeaf(InlineBox* leaf) { - return leaf && leaf->object() && leaf->object()->element() && leaf->object()->element()->isContentEditable(); + return leaf && leaf->renderer() && leaf->renderer()->node() && leaf->renderer()->node()->isContentEditable(); } InlineBox* RootInlineBox::closestLeafChildForXPos(int x, bool onlyEditableLeaves) @@ -355,19 +373,19 @@ InlineBox* RootInlineBox::closestLeafChildForXPos(int x, bool onlyEditableLeaves return firstLeaf; // Avoid returning a list marker when possible. - if (x <= firstLeaf->m_x && !firstLeaf->object()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) + if (x <= firstLeaf->m_x && !firstLeaf->renderer()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) // The x coordinate is less or equal to left edge of the firstLeaf. // Return it. return firstLeaf; - if (x >= lastLeaf->m_x + lastLeaf->m_width && !lastLeaf->object()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf))) + if (x >= lastLeaf->m_x + lastLeaf->m_width && !lastLeaf->renderer()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf))) // The x coordinate is greater or equal to right edge of the lastLeaf. // Return it. return lastLeaf; InlineBox* closestLeaf = 0; for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) { - if (!leaf->object()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(leaf))) { + if (!leaf->renderer()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(leaf))) { closestLeaf = leaf; if (x < leaf->m_x + leaf->m_width) // The x coordinate is less than the right edge of the box. @@ -404,12 +422,28 @@ EllipsisBox* RootInlineBox::ellipsisBox() const void RootInlineBox::setVerticalOverflowPositions(int top, int bottom) { if (!m_overflow) { - if (top == m_y && bottom == m_y + m_height) + const Font& font = renderer()->style(m_firstLine)->font(); + if (top == m_y && bottom == m_y + font.height()) return; - m_overflow = new (m_object->renderArena()) Overflow(this); + m_overflow = new (renderer()->renderArena()) Overflow(this); } m_overflow->m_topOverflow = top; m_overflow->m_bottomOverflow = bottom; } +void RootInlineBox::removeLineBoxFromRenderObject() +{ + block()->lineBoxes()->removeLineBox(this); +} + +void RootInlineBox::extractLineBoxFromRenderObject() +{ + block()->lineBoxes()->extractLineBox(this); +} + +void RootInlineBox::attachLineBoxToRenderObject() +{ + block()->lineBoxes()->attachLineBox(this); +} + } // namespace WebCore diff --git a/WebCore/rendering/RootInlineBox.h b/WebCore/rendering/RootInlineBox.h index a16d1e5..e190a48 100644 --- a/WebCore/rendering/RootInlineBox.h +++ b/WebCore/rendering/RootInlineBox.h @@ -43,7 +43,9 @@ public: { } - virtual bool isRootInlineBox() { return true; } + virtual bool isRootInlineBox() const { return true; } + + virtual int height() const; virtual void destroy(RenderArena*); void detachEllipsisBox(RenderArena*); @@ -53,16 +55,18 @@ public: virtual void adjustPosition(int dx, int dy); - virtual int topOverflow() { return m_overflow ? m_overflow->m_topOverflow : m_y; } - virtual int bottomOverflow() { return m_overflow ? m_overflow->m_bottomOverflow : m_y + m_height; } - virtual int leftOverflow() { return m_overflow ? m_overflow->m_leftOverflow : m_x; } - virtual int rightOverflow() { return m_overflow ? m_overflow->m_rightOverflow : m_x + m_width; } + virtual int topOverflow() const { return m_overflow ? m_overflow->m_topOverflow : m_y; } + virtual int bottomOverflow() const { return m_overflow ? m_overflow->m_bottomOverflow : m_y + m_renderer->style(m_firstLine)->font().height(); } + virtual int leftOverflow() const { return m_overflow ? m_overflow->m_leftOverflow : m_x; } + virtual int rightOverflow() const { return m_overflow ? m_overflow->m_rightOverflow : m_x + m_width; } virtual void setVerticalOverflowPositions(int top, int bottom); void setHorizontalOverflowPositions(int left, int right); virtual void setVerticalSelectionPositions(int top, int bottom); + virtual RenderLineBoxList* rendererLineBoxes() const; + #if ENABLE(SVG) virtual void computePerCharacterLayoutInformation() { } #endif @@ -114,7 +118,7 @@ public: RenderBlock* block() const; int selectionTop(); - int selectionBottom() { return m_overflow ? m_overflow->m_selectionBottom : m_y + m_height; } + int selectionBottom() { return m_overflow ? m_overflow->m_selectionBottom : m_y + height(); } int selectionHeight() { return max(0, selectionBottom() - selectionTop()); } InlineBox* closestLeafChildForXPos(int x, bool onlyEditableLeaves = false); @@ -123,12 +127,16 @@ public: { ASSERT(!isDirty()); if (!m_overflow) - m_overflow = new (m_object->renderArena()) Overflow(this); + m_overflow = new (m_renderer->renderArena()) Overflow(this); return m_overflow->floats; } Vector<RenderBox*>* floatsPtr() { ASSERT(!isDirty()); return m_overflow ? &m_overflow->floats : 0; } + virtual void extractLineBoxFromRenderObject(); + virtual void attachLineBoxToRenderObject(); + virtual void removeLineBoxFromRenderObject(); + protected: // Normally we are only as tall as the style on our block dictates, but we might have content // that spills out above the height of our font (e.g, a tall image), or something that extends further @@ -138,11 +146,11 @@ protected: struct Overflow { Overflow(RootInlineBox* box) : m_topOverflow(box->m_y) - , m_bottomOverflow(box->m_y + box->m_height) + , m_bottomOverflow(box->m_y + box->height()) , m_leftOverflow(box->m_x) , m_rightOverflow(box->m_x + box->m_width) , m_selectionTop(box->m_y) - , m_selectionBottom(box->m_y + box->m_height) + , m_selectionBottom(box->m_y + box->height()) { } @@ -184,7 +192,7 @@ inline void RootInlineBox::setHorizontalOverflowPositions(int left, int right) if (!m_overflow) { if (left == m_x && right == m_x + m_width) return; - m_overflow = new (m_object->renderArena()) Overflow(this); + m_overflow = new (m_renderer->renderArena()) Overflow(this); } m_overflow->m_leftOverflow = left; m_overflow->m_rightOverflow = right; @@ -193,9 +201,10 @@ inline void RootInlineBox::setHorizontalOverflowPositions(int left, int right) inline void RootInlineBox::setVerticalSelectionPositions(int top, int bottom) { if (!m_overflow) { - if (top == m_y && bottom == m_y + m_height) + const Font& font = m_renderer->style(m_firstLine)->font(); + if (top == m_y && bottom == m_y + font.height()) return; - m_overflow = new (m_object->renderArena()) Overflow(this); + m_overflow = new (m_renderer->renderArena()) Overflow(this); } m_overflow->m_selectionTop = top; m_overflow->m_selectionBottom = bottom; diff --git a/WebCore/rendering/SVGCharacterLayoutInfo.cpp b/WebCore/rendering/SVGCharacterLayoutInfo.cpp index 89bab2d..8871a75 100644 --- a/WebCore/rendering/SVGCharacterLayoutInfo.cpp +++ b/WebCore/rendering/SVGCharacterLayoutInfo.cpp @@ -274,7 +274,7 @@ void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox* flowBox, float angleStack.isEmpty() && baselineShiftStack.isEmpty() && curx == 0.0f && cury == 0.0f; - RenderSVGTextPath* textPath = static_cast<RenderSVGTextPath*>(flowBox->object()); + RenderSVGTextPath* textPath = static_cast<RenderSVGTextPath*>(flowBox->renderer()); Path path = textPath->layoutPath(); float baselineShift = calculateBaselineShift(textPath); @@ -521,7 +521,7 @@ TransformationMatrix SVGChar::characterTransform() const ctm.rotate(angle); if (pathData) { - ctm.scale(pathData->xScale, pathData->yScale); + ctm.scaleNonUniform(pathData->xScale, pathData->yScale); ctm.translate(pathData->xShift, pathData->yShift); ctm.rotate(pathData->orientationAngle); } diff --git a/WebCore/rendering/SVGInlineFlowBox.h b/WebCore/rendering/SVGInlineFlowBox.h index bb31807..2742ca0 100644 --- a/WebCore/rendering/SVGInlineFlowBox.h +++ b/WebCore/rendering/SVGInlineFlowBox.h @@ -33,12 +33,19 @@ class SVGInlineFlowBox : public InlineFlowBox { public: SVGInlineFlowBox(RenderObject* obj) : InlineFlowBox(obj) + , m_height(0) { } + virtual int height() const { return m_height; } + void setHeight(int h) { m_height = h; } + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); virtual int placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing); virtual int verticallyAlignBoxes(int heightOfBlock); + +private: + int m_height; }; } // namespace WebCore diff --git a/WebCore/rendering/SVGInlineTextBox.cpp b/WebCore/rendering/SVGInlineTextBox.cpp index 620aea5..f424ef2 100644 --- a/WebCore/rendering/SVGInlineTextBox.cpp +++ b/WebCore/rendering/SVGInlineTextBox.cpp @@ -44,6 +44,7 @@ namespace WebCore { SVGInlineTextBox::SVGInlineTextBox(RenderObject* obj) : InlineTextBox(obj) + , m_height(0) { } @@ -77,7 +78,7 @@ SVGRootInlineBox* SVGInlineTextBox::svgRootInlineBox() const float SVGInlineTextBox::calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed, String& glyphName) const { ASSERT(style); - return style->font().floatWidth(svgTextRunForInlineTextBox(textObject()->text()->characters() + offset, 1, style, this, 0), extraCharsAvailable, charsConsumed, glyphName); + return style->font().floatWidth(svgTextRunForInlineTextBox(textRenderer()->text()->characters() + offset, 1, style, this, 0), extraCharsAvailable, charsConsumed, glyphName); } float SVGInlineTextBox::calculateGlyphHeight(RenderStyle* style, int, int) const @@ -125,14 +126,14 @@ struct SVGInlineTextBoxClosestCharacterToPositionWalker { , m_distance(FLT_MAX) , m_x(x) , m_y(y) - , m_offset(0) + , m_offsetOfHitCharacter(0) { } void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { - RenderStyle* style = textBox->textObject()->style(); + RenderStyle* style = textBox->textRenderer()->style(); Vector<SVGChar>::iterator closestCharacter = 0; unsigned int closestOffset = UINT_MAX; @@ -164,7 +165,7 @@ struct SVGInlineTextBoxClosestCharacterToPositionWalker { if (closestOffset != UINT_MAX) { // Record current chunk, if it contains the current closest character next to the mouse. m_character = closestCharacter; - m_offset = closestOffset; + m_offsetOfHitCharacter = closestOffset; } } @@ -173,12 +174,12 @@ struct SVGInlineTextBoxClosestCharacterToPositionWalker { return m_character; } - int offset() const + int offsetOfHitCharacter() const { if (!m_character) return 0; - return m_offset; + return m_offsetOfHitCharacter; } private: @@ -187,7 +188,7 @@ private: int m_x; int m_y; - int m_offset; + int m_offsetOfHitCharacter; }; // Helper class for selectionRect() @@ -199,7 +200,7 @@ struct SVGInlineTextBoxSelectionRectWalker { void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { - RenderStyle* style = textBox->textObject()->style(); + RenderStyle* style = textBox->textRenderer()->style(); for (Vector<SVGChar>::iterator it = start; it != end; ++it) { if (it->isHidden()) @@ -221,7 +222,7 @@ private: FloatRect m_selectionRect; }; -SVGChar* SVGInlineTextBox::closestCharacterToPosition(int x, int y, int& offset) const +SVGChar* SVGInlineTextBox::closestCharacterToPosition(int x, int y, int& offsetOfHitCharacter) const { SVGRootInlineBox* rootBox = svgRootInlineBox(); if (!rootBox) @@ -232,25 +233,30 @@ SVGChar* SVGInlineTextBox::closestCharacterToPosition(int x, int y, int& offset) rootBox->walkTextChunks(&walker, this); - offset = walkerCallback.offset(); + offsetOfHitCharacter = walkerCallback.offsetOfHitCharacter(); return walkerCallback.character(); } -bool SVGInlineTextBox::svgCharacterHitsPosition(int x, int y, int& offset) const +bool SVGInlineTextBox::svgCharacterHitsPosition(int x, int y, int& closestOffsetInBox) const { - SVGChar* charAtPosPtr = closestCharacterToPosition(x, y, offset); + int offsetOfHitCharacter = 0; + SVGChar* charAtPosPtr = closestCharacterToPosition(x, y, offsetOfHitCharacter); if (!charAtPosPtr) return false; SVGChar& charAtPos = *charAtPosPtr; - RenderStyle* style = textObject()->style(m_firstLine); - FloatRect glyphRect = calculateGlyphBoundaries(style, offset, charAtPos); + RenderStyle* style = textRenderer()->style(m_firstLine); + FloatRect glyphRect = calculateGlyphBoundaries(style, offsetOfHitCharacter, charAtPos); + // FIXME: Why? if (direction() == RTL) - offset++; + offsetOfHitCharacter++; - // FIXME: todo list - // (#13910) This code does not handle bottom-to-top/top-to-bottom vertical text. + // The caller actually the closest offset before/after the hit char + // closestCharacterToPosition returns us offsetOfHitCharacter. + closestOffsetInBox = offsetOfHitCharacter; + + // FIXME: (bug 13910) This code does not handle bottom-to-top/top-to-bottom vertical text. // Check whether y position hits the current character if (y < charAtPos.y - glyphRect.height() || y > charAtPos.y) @@ -258,21 +264,21 @@ bool SVGInlineTextBox::svgCharacterHitsPosition(int x, int y, int& offset) const // Check whether x position hits the current character if (x < charAtPos.x) { - if (offset > 0 && direction() == LTR) + if (closestOffsetInBox > 0 && direction() == LTR) return true; - else if (offset < (int) end() && direction() == RTL) + else if (closestOffsetInBox < (int) end() && direction() == RTL) return true; return false; } - // If we are past the last glyph of this box, don't mark it as 'hit' anymore. - if (x >= charAtPos.x + glyphRect.width() && offset == (int) end()) - return false; - - // Snap to character at half of it's advance + // Adjust the closest offset to after the char if x was after the char midpoint if (x >= charAtPos.x + glyphRect.width() / 2.0) - offset += direction() == RTL ? -1 : 1; + closestOffsetInBox += direction() == RTL ? -1 : 1; + + // If we are past the last glyph of this box, don't mark it as 'hit' + if (x >= charAtPos.x + glyphRect.width() && closestOffsetInBox == (int) end()) + return false; return true; } @@ -296,8 +302,8 @@ bool SVGInlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, ASSERT(!isLineBreak()); IntRect rect = selectionRect(0, 0, 0, len()); - if (object()->style()->visibility() == VISIBLE && rect.contains(x, y)) { - object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + if (renderer()->style()->visibility() == VISIBLE && rect.contains(x, y)) { + renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } @@ -324,12 +330,12 @@ IntRect SVGInlineTextBox::selectionRect(int, int, int startPos, int endPos) void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int tx, int ty, const SVGChar& svgChar, const UChar* chars, int length, SVGPaintServer* activePaintServer) { - if (object()->style()->visibility() != VISIBLE || paintInfo.phase == PaintPhaseOutline) + if (renderer()->style()->visibility() != VISIBLE || paintInfo.phase == PaintPhaseOutline) return; ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines); - RenderText* text = textObject(); + RenderText* text = textRenderer(); ASSERT(text); bool isPrinting = text->document()->printing(); @@ -444,7 +450,7 @@ void SVGInlineTextBox::paintSelection(int boxStartOffset, const SVGChar& svgChar return; Color textColor = style->color(); - Color color = object()->selectionBackgroundColor(); + Color color = renderer()->selectionBackgroundColor(); if (!color.isValid() || color.alpha() == 0) return; @@ -471,7 +477,7 @@ void SVGInlineTextBox::paintSelection(int boxStartOffset, const SVGChar& svgChar p->save(); int adjust = startPos >= boxStartOffset ? boxStartOffset : 0; - p->drawHighlightForText(font, svgTextRunForInlineTextBox(textObject()->text()->characters() + start() + boxStartOffset, length, style, this, svgChar.x), + p->drawHighlightForText(font, svgTextRunForInlineTextBox(textRenderer()->text()->characters() + start() + boxStartOffset, length, style, this, svgChar.x), IntPoint((int) svgChar.x, (int) svgChar.y - font.ascent()), font.ascent() + font.descent(), color, startPos - adjust, endPos - adjust); @@ -496,7 +502,7 @@ static inline Path pathForDecoration(ETextDecoration decoration, RenderObject* o void SVGInlineTextBox::paintDecoration(ETextDecoration decoration, GraphicsContext* context, int tx, int ty, int width, const SVGChar& svgChar, const SVGTextDecorationInfo& info) { - if (object()->style()->visibility() != VISIBLE) + if (renderer()->style()->visibility() != VISIBLE) return; // This function does NOT accept combinated text decorations. It's meant to be invoked for just one. @@ -508,10 +514,11 @@ void SVGInlineTextBox::paintDecoration(ETextDecoration decoration, GraphicsConte if (!isFilled && !isStroked) return; + int baseline = renderer()->style(m_firstLine)->font().ascent(); if (decoration == UNDERLINE) - ty += m_baseline; + ty += baseline; else if (decoration == LINE_THROUGH) - ty += 2 * m_baseline / 3; + ty += 2 * baseline / 3; context->save(); context->beginPath(); diff --git a/WebCore/rendering/SVGInlineTextBox.h b/WebCore/rendering/SVGInlineTextBox.h index 9882128..6287c81 100644 --- a/WebCore/rendering/SVGInlineTextBox.h +++ b/WebCore/rendering/SVGInlineTextBox.h @@ -37,6 +37,9 @@ namespace WebCore { public: SVGInlineTextBox(RenderObject* obj); + virtual int height() const { return m_height; } + void setHeight(int h) { m_height = h; } + virtual int selectionTop(); virtual int selectionHeight(); @@ -67,6 +70,8 @@ namespace WebCore { private: friend class RenderSVGInlineText; bool svgCharacterHitsPosition(int x, int y, int& offset) const; + + int m_height; }; } // namespace WebCore diff --git a/WebCore/rendering/SVGRenderSupport.cpp b/WebCore/rendering/SVGRenderSupport.cpp index 9ffc533..dd154c7 100644 --- a/WebCore/rendering/SVGRenderSupport.cpp +++ b/WebCore/rendering/SVGRenderSupport.cpp @@ -46,9 +46,9 @@ void prepareToRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& pa UNUSED_PARAM(rootFilter); #endif - SVGElement* svgElement = static_cast<SVGElement*>(object->element()); - ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); ASSERT(object); + SVGElement* svgElement = static_cast<SVGElement*>(object->node()); + ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); const RenderStyle* style = object->style(); @@ -160,7 +160,7 @@ void clampImageBufferSizeToViewport(RenderObject* object, IntSize& size) if (!object || !object->isRenderView()) return; - RenderView* view = static_cast<RenderView*>(object); + RenderView* view = toRenderView(object); if (!view->frameView()) return; diff --git a/WebCore/rendering/SVGRenderTreeAsText.cpp b/WebCore/rendering/SVGRenderTreeAsText.cpp index 74a8af9..1f97c47 100644 --- a/WebCore/rendering/SVGRenderTreeAsText.cpp +++ b/WebCore/rendering/SVGRenderTreeAsText.cpp @@ -432,7 +432,7 @@ static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textB ts << " override"; } - ts << ": " << quoteAndEscapeNonPrintables(String(textBox->textObject()->text()).substring(textBox->start() + range.startOffset, offset)) << "\n"; + ts << ": " << quoteAndEscapeNonPrintables(String(textBox->textRenderer()->text()).substring(textBox->start() + range.startOffset, offset)) << "\n"; j++; } @@ -459,8 +459,8 @@ void write(TextStream& ts, const RenderSVGContainer& container, int indent) writeIndent(ts, indent); ts << container.renderName(); - if (container.element()) { - String tagName = getTagName(static_cast<SVGStyledElement*>(container.element())); + if (container.node()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(container.node())); if (!tagName.isEmpty()) ts << " {" << tagName << "}"; } @@ -476,8 +476,8 @@ void write(TextStream& ts, const RenderSVGRoot& root, int indent) writeIndent(ts, indent); ts << root.renderName(); - if (root.element()) { - String tagName = getTagName(static_cast<SVGStyledElement*>(root.element())); + if (root.node()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(root.node())); if (!tagName.isEmpty()) ts << " {" << tagName << "}"; } @@ -493,8 +493,8 @@ void write(TextStream& ts, const RenderSVGText& text, int indent) writeIndent(ts, indent); ts << text.renderName(); - if (text.element()) { - String tagName = getTagName(static_cast<SVGStyledElement*>(text.element())); + if (text.node()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(text.node())); if (!tagName.isEmpty()) ts << " {" << tagName << "}"; } @@ -510,8 +510,8 @@ void write(TextStream& ts, const RenderSVGInlineText& text, int indent) writeIndent(ts, indent); ts << text.renderName(); - if (text.element()) { - String tagName = getTagName(static_cast<SVGStyledElement*>(text.element())); + if (text.node()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(text.node())); if (!tagName.isEmpty()) ts << " {" << tagName << "}"; } @@ -527,8 +527,8 @@ void write(TextStream& ts, const RenderPath& path, int indent) writeIndent(ts, indent); ts << path.renderName(); - if (path.element()) { - String tagName = getTagName(static_cast<SVGStyledElement*>(path.element())); + if (path.node()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(path.node())); if (!tagName.isEmpty()) ts << " {" << tagName << "}"; } diff --git a/WebCore/rendering/SVGRootInlineBox.cpp b/WebCore/rendering/SVGRootInlineBox.cpp index 62a8b04..88c7542 100644 --- a/WebCore/rendering/SVGRootInlineBox.cpp +++ b/WebCore/rendering/SVGRootInlineBox.cpp @@ -344,7 +344,7 @@ struct SVGRootInlineBoxPaintWalker { , m_chunkStarted(false) , m_paintInfo(paintInfo) , m_savedInfo(paintInfo) - , m_boundingBox(tx + rootBox->xPos(), ty + rootBox->yPos(), rootBox->width(), rootBox->height()) + , m_boundingBox(tx + rootBox->x(), ty + rootBox->y(), rootBox->width(), rootBox->height()) , m_filter(0) , m_rootFilter(rootFilter) , m_fillPaintServer(0) @@ -396,14 +396,14 @@ struct SVGRootInlineBoxPaintWalker { InlineFlowBox* flowBox = box->parent(); // Initialize text rendering - RenderObject* object = flowBox->object(); + RenderObject* object = flowBox->renderer(); ASSERT(object); m_savedInfo = m_paintInfo; m_paintInfo.context->save(); if (!flowBox->isRootInlineBox()) - m_paintInfo.context->concatCTM(m_rootBox->object()->localTransform()); + m_paintInfo.context->concatCTM(m_rootBox->renderer()->localTransform()); m_paintInfo.context->concatCTM(object->localTransform()); @@ -420,7 +420,7 @@ struct SVGRootInlineBoxPaintWalker { InlineFlowBox* flowBox = box->parent(); - RenderObject* object = flowBox->object(); + RenderObject* object = flowBox->renderer(); ASSERT(object); // Clean up last used paint server @@ -443,7 +443,7 @@ struct SVGRootInlineBoxPaintWalker { InlineFlowBox* flowBox = box->parent(); // Setup fill paint server - RenderObject* object = flowBox->object(); + RenderObject* object = flowBox->renderer(); ASSERT(object); ASSERT(!m_strokePaintServer); @@ -464,7 +464,7 @@ struct SVGRootInlineBoxPaintWalker { InlineFlowBox* flowBox = box->parent(); // Setup stroke paint server - RenderObject* object = flowBox->object(); + RenderObject* object = flowBox->renderer(); ASSERT(object); // If we're both stroked & filled, teardown fill paint server before stroking. @@ -485,7 +485,7 @@ struct SVGRootInlineBoxPaintWalker { void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { - RenderText* text = textBox->textObject(); + RenderText* text = textBox->textRenderer(); ASSERT(text); RenderStyle* styleToUse = text->style(textBox->isFirstLineStyle()); @@ -581,12 +581,12 @@ void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) paintInfo.context->save(); SVGResourceFilter* filter = 0; - FloatRect boundingBox(tx + xPos(), ty + yPos(), width(), height()); + FloatRect boundingBox(tx + x(), ty + y(), width(), height()); // Initialize text rendering - paintInfo.context->concatCTM(object()->localTransform()); - prepareToRenderSVGContent(object(), paintInfo, boundingBox, filter); - paintInfo.context->concatCTM(object()->localTransform().inverse()); + paintInfo.context->concatCTM(renderer()->localTransform()); + prepareToRenderSVGContent(renderer(), paintInfo, boundingBox, filter); + paintInfo.context->concatCTM(renderer()->localTransform().inverse()); // Render text, chunk-by-chunk SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty); @@ -600,7 +600,7 @@ void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) walkTextChunks(&walker); // Finalize text rendering - finishRenderSVGContent(object(), paintInfo, boundingBox, filter, savedInfo.context); + finishRenderSVGContent(renderer(), paintInfo, boundingBox, filter, savedInfo.context); paintInfo.context->restore(); } @@ -625,7 +625,7 @@ float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range ASSERT(range.box->isInlineTextBox()); InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); - RenderText* text = textBox->textObject(); + RenderText* text = textBox->textRenderer(); RenderStyle* style = text->style(); return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox, 0)); @@ -638,7 +638,7 @@ float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& rang ASSERT(range.box->isInlineTextBox()); InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); - RenderText* text = textBox->textObject(); + RenderText* text = textBox->textRenderer(); const Font& font = text->style()->font(); return (range.endOffset - range.startOffset) * (font.ascent() + font.descent()); @@ -652,7 +652,7 @@ TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->direction() == RTL, textBox->m_dirOverride || style->visuallyOrdered()); #if ENABLE(SVG_FONTS) - run.setReferencingRenderObject(textBox->textObject()->parent()); + run.setReferencingRenderObject(textBox->textRenderer()->parent()); #endif // We handle letter & word spacing ourselves @@ -672,7 +672,7 @@ static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWi SVGInlineBoxCharacterRange& range = *it; SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box); - RenderStyle* style = box->object()->style(); + RenderStyle* style = box->renderer()->style(); for (int i = range.startOffset; i < range.endOffset; ++i) { ASSERT(charIt <= chunk.end); @@ -791,13 +791,12 @@ static void applyTextAnchorToTextChunk(SVGTextChunk& chunk) InlineBox* curBox = range.box; ASSERT(curBox->isInlineTextBox()); - ASSERT(curBox->parent() && (curBox->parent()->isRootInlineBox() || curBox->parent()->isInlineFlowBox())); // Move target box if (chunk.isVerticalText) - curBox->setYPos(curBox->yPos() + static_cast<int>(shift)); + curBox->setY(curBox->y() + static_cast<int>(shift)); else - curBox->setXPos(curBox->xPos() + static_cast<int>(shift)); + curBox->setX(curBox->x() + static_cast<int>(shift)); } } @@ -820,9 +819,9 @@ static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELen if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { if (chunk.isVerticalText) - chunk.ctm.scale(1.0f, chunk.textLength / computedLength); + chunk.ctm.scaleNonUniform(1.0f, chunk.textLength / computedLength); else - chunk.ctm.scale(chunk.textLength / computedLength, 1.0f); + chunk.ctm.scaleNonUniform(chunk.textLength / computedLength, 1.0f); return 0.0f; } @@ -898,9 +897,9 @@ void SVGRootInlineBox::computePerCharacterLayoutInformation() void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info) { if (start->isRootInlineBox()) { - ASSERT(start->object()->element()->hasTagName(SVGNames::textTag)); + ASSERT(start->renderer()->node()->hasTagName(SVGNames::textTag)); - SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->object()->element()); + SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->renderer()->node()); ASSERT(positioningElement); ASSERT(positioningElement->parentNode()); @@ -910,20 +909,20 @@ void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacter LastGlyphInfo lastGlyph; for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->object()->isText()) + if (curr->renderer()->isText()) buildLayoutInformationForTextBox(info, static_cast<InlineTextBox*>(curr), lastGlyph); else { ASSERT(curr->isInlineFlowBox()); InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); - if (!flowBox->object()->element()) + if (!flowBox->renderer()->node()) continue; // Skip generated content. - bool isAnchor = flowBox->object()->element()->hasTagName(SVGNames::aTag); - bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag); + bool isAnchor = flowBox->renderer()->node()->hasTagName(SVGNames::aTag); + bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag); if (!isTextPath && !isAnchor) { - SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->object()->element()); + SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->renderer()->node()); ASSERT(positioningElement); ASSERT(positioningElement->parentNode()); @@ -933,13 +932,13 @@ void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacter // Handle text-anchor/textLength on path, which is special. SVGTextContentElement* textContent = 0; - Node* node = flowBox->object()->element(); + Node* node = flowBox->renderer()->node(); if (node && node->isSVGElement()) textContent = static_cast<SVGTextContentElement*>(node); ASSERT(textContent); ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); - ETextAnchor anchor = flowBox->object()->style()->svgStyle()->textAnchor(); + ETextAnchor anchor = flowBox->renderer()->style()->svgStyle()->textAnchor(); float textAnchorStartOffset = 0.0f; // Initialize sub-layout. We need to create text chunks from the textPath @@ -1008,10 +1007,8 @@ void SVGRootInlineBox::layoutInlineBoxes() void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& lowX, int& highX, int& lowY, int& highY) { for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { - RenderStyle* style = curr->object()->style(); - const Font& font = style->font(); - - if (curr->object()->isText()) { + RenderStyle* style = curr->renderer()->style(); + if (curr->renderer()->isText()) { SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(curr); unsigned length = textBox->len(); @@ -1039,12 +1036,11 @@ void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>:: int minY = enclosedStringRect.y(); int maxY = minY + enclosedStringRect.height(); - curr->setXPos(minX - block()->x()); + curr->setX(minX - block()->x()); curr->setWidth(enclosedStringRect.width()); - curr->setYPos(minY - block()->y()); - curr->setBaseline(font.ascent()); - curr->setHeight(enclosedStringRect.height()); + curr->setY(minY - block()->y()); + textBox->setHeight(enclosedStringRect.height()); if (minX < lowX) lowX = minX; @@ -1067,17 +1063,16 @@ void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>:: InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); - if (!flowBox->object()->element()) + if (!flowBox->renderer()->node()) continue; // Skip generated content. layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY); - curr->setXPos(minX - block()->x()); + curr->setX(minX - block()->x()); curr->setWidth(maxX - minX); - curr->setYPos(minY - block()->y()); - curr->setBaseline(font.ascent()); - curr->setHeight(maxY - minY); + curr->setY(minY - block()->y()); + static_cast<SVGInlineFlowBox*>(curr)->setHeight(maxY - minY); if (minX < lowX) lowX = minX; @@ -1093,15 +1088,15 @@ void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>:: } } - if (start->isRootInlineBox()) { + if (start->isSVGRootInlineBox()) { int top = lowY - block()->y(); int bottom = highY - block()->y(); - start->setXPos(lowX - block()->x()); - start->setYPos(top); + start->setX(lowX - block()->x()); + start->setY(top); start->setWidth(highX - lowX); - start->setHeight(highY - lowY); + static_cast<SVGRootInlineBox*>(start)->setHeight(highY - lowY); start->setVerticalOverflowPositions(top, bottom); start->setVerticalSelectionPositions(top, bottom); @@ -1110,7 +1105,7 @@ void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>:: void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox, LastGlyphInfo& lastGlyph) { - RenderText* text = textBox->textObject(); + RenderText* text = textBox->textRenderer(); ASSERT(text); RenderStyle* style = text->style(textBox->isFirstLineStyle()); @@ -1141,11 +1136,11 @@ void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& if (textBox->direction() == RTL) { glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName); glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable); - unicodeStr = String(textBox->textObject()->text()->characters() + textBox->end() - i, charsConsumed); + unicodeStr = String(textBox->textRenderer()->text()->characters() + textBox->end() - i, charsConsumed); } else { glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName); glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable); - unicodeStr = String(textBox->textObject()->text()->characters() + textBox->start() + i, charsConsumed); + unicodeStr = String(textBox->textRenderer()->text()->characters() + textBox->start() + i, charsConsumed); } bool assignedX = false; @@ -1195,7 +1190,7 @@ void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& } // Take letter & word spacing and kerning into account - float spacing = font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer()); + float spacing = font.letterSpacing() + calculateKerning(textBox->renderer()->node()->renderer()); const UChar* currentCharacter = text->characters() + (textBox->direction() == RTL ? textBox->end() - i : textBox->start() + i); const UChar* lastCharacter = 0; @@ -1370,7 +1365,7 @@ void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* #endif for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->object()->isText()) { + if (curr->renderer()->isText()) { InlineTextBox* textBox = static_cast<InlineTextBox*>(curr); unsigned length = textBox->len(); @@ -1382,12 +1377,12 @@ void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath); #endif - RenderText* text = textBox->textObject(); + RenderText* text = textBox->textRenderer(); ASSERT(text); - ASSERT(text->element()); + ASSERT(text->node()); SVGTextContentElement* textContent = 0; - Node* node = text->element()->parent(); + Node* node = text->node()->parent(); while (node && node->isSVGElement() && !textContent) { if (static_cast<SVGElement*>(node)->isTextContent()) textContent = static_cast<SVGTextContentElement*>(node); @@ -1525,10 +1520,10 @@ void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* ASSERT(curr->isInlineFlowBox()); InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); - if (!flowBox->object()->element()) + if (!flowBox->renderer()->node()) continue; // Skip generated content. - bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag); + bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag); #if DEBUG_CHUNK_BUILDING > 1 fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath); diff --git a/WebCore/rendering/SVGRootInlineBox.h b/WebCore/rendering/SVGRootInlineBox.h index bfe9889..10c43ea 100644 --- a/WebCore/rendering/SVGRootInlineBox.h +++ b/WebCore/rendering/SVGRootInlineBox.h @@ -47,11 +47,15 @@ class SVGRootInlineBox : public RootInlineBox { public: SVGRootInlineBox(RenderObject* obj) : RootInlineBox(obj) + , m_height(0) { } virtual bool isSVGRootInlineBox() { return true; } + virtual int height() const { return m_height; } + void setHeight(int h) { m_height = h; } + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); virtual int placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing); @@ -80,6 +84,7 @@ private: SVGTextDecorationInfo retrievePaintServersForTextDecoration(RenderObject* start); private: + int m_height; Vector<SVGChar> m_svgChars; Vector<SVGTextChunk> m_svgTextChunks; }; diff --git a/WebCore/rendering/ScrollBehavior.cpp b/WebCore/rendering/ScrollBehavior.cpp new file mode 100644 index 0000000..232ea19 --- /dev/null +++ b/WebCore/rendering/ScrollBehavior.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * Other contributors: + * Robert O'Callahan <roc+@cs.cmu.edu> + * David Baron <dbaron@fas.harvard.edu> + * Christian Biesinger <cbiesinger@web.de> + * Randall Jesup <rjesup@wgate.com> + * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de> + * Josh Soref <timeless@mac.com> + * Boris Zbarsky <bzbarsky@mit.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#include "config.h" +#include "ScrollBehavior.h" + +namespace WebCore { + +const ScrollAlignment ScrollAlignment::alignCenterIfNeeded = { noScroll, alignCenter, alignToClosestEdge }; +const ScrollAlignment ScrollAlignment::alignToEdgeIfNeeded = { noScroll, alignToClosestEdge, alignToClosestEdge }; +const ScrollAlignment ScrollAlignment::alignCenterAlways = { alignCenter, alignCenter, alignCenter }; +const ScrollAlignment ScrollAlignment::alignTopAlways = { alignTop, alignTop, alignTop }; +const ScrollAlignment ScrollAlignment::alignBottomAlways = { alignBottom, alignBottom, alignBottom }; + +}; // namespace WebCore diff --git a/WebCore/rendering/ScrollBehavior.h b/WebCore/rendering/ScrollBehavior.h new file mode 100644 index 0000000..390c68a --- /dev/null +++ b/WebCore/rendering/ScrollBehavior.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2003, 2009 Apple Inc. All rights reserved. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * Other contributors: + * Robert O'Callahan <roc+@cs.cmu.edu> + * David Baron <dbaron@fas.harvard.edu> + * Christian Biesinger <cbiesinger@web.de> + * Randall Jesup <rjesup@wgate.com> + * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de> + * Josh Soref <timeless@mac.com> + * Boris Zbarsky <bzbarsky@mit.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#ifndef ScrollBehavior_h +#define ScrollBehavior_h + +namespace WebCore { + +enum ScrollBehavior { + noScroll, + alignCenter, + alignTop, + alignBottom, + alignLeft, + alignRight, + alignToClosestEdge +}; + +struct ScrollAlignment { + static ScrollBehavior getVisibleBehavior(const ScrollAlignment& s) { return s.m_rectVisible; } + static ScrollBehavior getPartialBehavior(const ScrollAlignment& s) { return s.m_rectPartial; } + static ScrollBehavior getHiddenBehavior(const ScrollAlignment& s) { return s.m_rectHidden; } + + static const ScrollAlignment alignCenterIfNeeded; + static const ScrollAlignment alignToEdgeIfNeeded; + static const ScrollAlignment alignCenterAlways; + static const ScrollAlignment alignTopAlways; + static const ScrollAlignment alignBottomAlways; + + ScrollBehavior m_rectVisible; + ScrollBehavior m_rectHidden; + ScrollBehavior m_rectPartial; +}; + + +}; // namespace WebCore + +#endif // ScrollBehavior_h diff --git a/WebCore/rendering/TextControlInnerElements.cpp b/WebCore/rendering/TextControlInnerElements.cpp index 452333c..cd067de 100644 --- a/WebCore/rendering/TextControlInnerElements.cpp +++ b/WebCore/rendering/TextControlInnerElements.cpp @@ -35,15 +35,19 @@ #include "HTMLNames.h" #include "HTMLTextAreaElement.h" #include "MouseEvent.h" +#include "RenderLayer.h" #include "RenderTextControlSingleLine.h" namespace WebCore { class RenderTextControlInnerBlock : public RenderBlock { public: - RenderTextControlInnerBlock(Node* node) : RenderBlock(node) { } + RenderTextControlInnerBlock(Node* node, bool isMultiLine) : RenderBlock(node), m_multiLine(isMultiLine) { } virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + virtual VisiblePosition positionForPoint(const IntPoint&); + private: + bool m_multiLine; }; bool RenderTextControlInnerBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) @@ -57,6 +61,22 @@ bool RenderTextControlInnerBlock::nodeAtPoint(const HitTestRequest& request, Hit return RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, placeholderIsVisible ? HitTestBlockBackground : hitTestAction); } +VisiblePosition RenderTextControlInnerBlock::positionForPoint(const IntPoint& point) +{ + int contentsX = point.x(); + int contentsY = point.y(); + + // Multiline text controls have the scroll on shadowAncestorNode, so we need to take that + // into account here. + if (m_multiLine) { + RenderTextControl* renderer = static_cast<RenderTextControl*>(node()->shadowAncestorNode()->renderer()); + if (renderer->hasOverflowClip()) + renderer->layer()->addScrolledContentOffset(contentsX, contentsY); + } + + return RenderBlock::positionForPoint(IntPoint(contentsX, contentsY)); +} + TextControlInnerElement::TextControlInnerElement(Document* doc, Node* shadowParent) : HTMLDivElement(HTMLNames::divTag, doc) , m_shadowParent(shadowParent) @@ -98,14 +118,15 @@ void TextControlInnerTextElement::defaultEventHandler(Event* evt) // FIXME: In the future, we should add a way to have default event listeners. Then we would add one to the text field's inner div, and we wouldn't need this subclass. Node* shadowAncestor = shadowAncestorNode(); if (shadowAncestor && shadowAncestor->renderer()) { - ASSERT(shadowAncestor->renderer()->isTextField() || shadowAncestor->renderer()->isTextArea()); - if (evt->isBeforeTextInsertedEvent()) + ASSERT(shadowAncestor->renderer()->isTextControl()); + if (evt->isBeforeTextInsertedEvent()) { if (shadowAncestor->renderer()->isTextField()) static_cast<HTMLInputElement*>(shadowAncestor)->defaultEventHandler(evt); else static_cast<HTMLTextAreaElement*>(shadowAncestor)->defaultEventHandler(evt); + } if (evt->type() == eventNames().webkitEditableContentChangedEvent) - static_cast<RenderTextControl*>(shadowAncestor->renderer())->subtreeHasChanged(); + toRenderTextControl(shadowAncestor->renderer())->subtreeHasChanged(); } if (!evt->defaultHandled()) HTMLDivElement::defaultEventHandler(evt); @@ -113,7 +134,13 @@ void TextControlInnerTextElement::defaultEventHandler(Event* evt) RenderObject* TextControlInnerTextElement::createRenderer(RenderArena* arena, RenderStyle*) { - return new (arena) RenderTextControlInnerBlock(this); + bool multiLine = false; + Node* shadowAncestor = shadowAncestorNode(); + if (shadowAncestor && shadowAncestor->renderer()) { + ASSERT(shadowAncestor->renderer()->isTextField() || shadowAncestor->renderer()->isTextArea()); + multiLine = shadowAncestor->renderer()->isTextArea(); + } + return new (arena) RenderTextControlInnerBlock(this, multiLine); } SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* doc) diff --git a/WebCore/rendering/TransformState.cpp b/WebCore/rendering/TransformState.cpp new file mode 100644 index 0000000..5021d1f --- /dev/null +++ b/WebCore/rendering/TransformState.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2009 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 COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TransformState.h" + +namespace WebCore { + +void TransformState::move(int x, int y, bool accumulateTransform) +{ + if (m_accumulatingTransform && m_accumulatedTransform) { + // If we're accumulating into an existing transform, apply the translation. + if (m_direction == ApplyTransformDirection) + m_accumulatedTransform->translateRight(x, y); + else + m_accumulatedTransform->translate(-x, -y); // We're unapplying, so negate + + // Then flatten if necessary. + if (!accumulateTransform) + flatten(); + } else { + // Just move the point and, optionally, the quad. + m_lastPlanarPoint.move(x, y); + if (m_mapQuad) + m_lastPlanarQuad.move(x, y); + } + m_accumulatingTransform = accumulateTransform; +} + +void TransformState::applyTransform(const TransformationMatrix& transformFromContainer, bool accumulateTransform) +{ + // If we have an accumulated transform from last time, multiply in this transform + if (m_accumulatedTransform) { + if (m_direction == ApplyTransformDirection) + m_accumulatedTransform->multiply(transformFromContainer); + else + m_accumulatedTransform->multLeft(transformFromContainer); + } else if (accumulateTransform) { + // Make one if we started to accumulate + m_accumulatedTransform.set(new TransformationMatrix(transformFromContainer)); + } + + if (!accumulateTransform) { + const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer; + flattenWithTransform(*finalTransform); + } + m_accumulatingTransform = accumulateTransform; +} + +void TransformState::flatten() +{ + if (!m_accumulatedTransform) { + m_accumulatingTransform = false; + return; + } + + flattenWithTransform(*m_accumulatedTransform); +} + +FloatPoint TransformState::mappedPoint() const +{ + if (!m_accumulatedTransform) + return m_lastPlanarPoint; + + if (m_direction == ApplyTransformDirection) + return m_accumulatedTransform->mapPoint(m_lastPlanarPoint); + + return m_accumulatedTransform->inverse().projectPoint(m_lastPlanarPoint); +} + +FloatQuad TransformState::mappedQuad() const +{ + if (!m_accumulatedTransform) + return m_lastPlanarQuad; + + if (m_direction == ApplyTransformDirection) + return m_accumulatedTransform->mapQuad(m_lastPlanarQuad); + + return m_accumulatedTransform->inverse().projectQuad(m_lastPlanarQuad); +} + +void TransformState::flattenWithTransform(const TransformationMatrix& t) +{ + if (m_direction == ApplyTransformDirection) { + m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint); + if (m_mapQuad) + m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad); + } else { + TransformationMatrix inverseTransform = t.inverse(); + m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint); + if (m_mapQuad) + m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad); + } + + // We could throw away m_accumulatedTransform if we wanted to here, but that + // would cause thrash when traversing hierarachies with alternating + // preserve-3d and flat elements. + if (m_accumulatedTransform) + m_accumulatedTransform->makeIdentity(); + m_accumulatingTransform = false; +} + +// HitTestingTransformState methods +void HitTestingTransformState::move(int x, int y) +{ + if (m_accumulatingTransform) + flatten(); + + m_lastPlanarPoint.move(x, y); + m_lastPlanarQuad.move(x, y); +} + +void HitTestingTransformState::applyTransform(const TransformationMatrix& transformFromContainer, bool accumulateTransform) +{ + m_accumulatedTransform.multLeft(transformFromContainer); + if (!accumulateTransform) + flatten(); + + m_accumulatingTransform = accumulateTransform; +} + +void HitTestingTransformState::flatten() +{ + m_lastPlanarPoint = mappedPoint(); + m_lastPlanarQuad = mappedQuad(); + m_accumulatedTransform.makeIdentity(); + m_accumulatingTransform = false; +} + +FloatPoint HitTestingTransformState::mappedPoint() const +{ + return m_accumulatedTransform.inverse().projectPoint(m_lastPlanarPoint); +} + +FloatQuad HitTestingTransformState::mappedQuad() const +{ + return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarQuad); +} + +} // namespace WebCore diff --git a/WebCore/rendering/TransformState.h b/WebCore/rendering/TransformState.h new file mode 100644 index 0000000..5190118 --- /dev/null +++ b/WebCore/rendering/TransformState.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2009 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 COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TransformState_h +#define TransformState_h + +#include "FloatPoint.h" +#include "FloatQuad.h" +#include "IntSize.h" +#include "TransformationMatrix.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class TransformState : Noncopyable { +public: + enum TransformDirection { ApplyTransformDirection, UnapplyInverseTransformDirection }; + // If quad is non-null, it will be mapped + TransformState(TransformDirection mappingDirection, const FloatPoint& p, const FloatQuad* quad = 0) + : m_lastPlanarPoint(p) + , m_accumulatingTransform(false) + , m_mapQuad(quad != 0) + , m_direction(mappingDirection) + { + if (quad) + m_lastPlanarQuad = *quad; + } + + void move(const IntSize& s, bool accumulateTransform = false) + { + move(s.width(), s.height(), accumulateTransform); + } + + void move(int x, int y, bool accumulateTransform = false); + void applyTransform(const TransformationMatrix& transformFromContainer, bool accumulateTransform = false); + void flatten(); + + // Return the coords of the point or quad in the last flattened layer + FloatPoint lastPlanarPoint() const { return m_lastPlanarPoint; } + FloatQuad lastPlanarQuad() const { return m_lastPlanarQuad; } + + // Return the point or quad mapped through the current transform + FloatPoint mappedPoint() const; + FloatQuad mappedQuad() const; + +private: + void flattenWithTransform(const TransformationMatrix&); + + FloatPoint m_lastPlanarPoint; + FloatQuad m_lastPlanarQuad; + + // We only allocate the transform if we need to + OwnPtr<TransformationMatrix> m_accumulatedTransform; + bool m_accumulatingTransform; + bool m_mapQuad; + TransformDirection m_direction; +}; + +class HitTestingTransformState : public RefCounted<HitTestingTransformState> { +public: + static PassRefPtr<HitTestingTransformState> create(const FloatPoint& p, const FloatQuad& quad) + { + return adoptRef(new HitTestingTransformState(p, quad)); + } + + static PassRefPtr<HitTestingTransformState> create(const HitTestingTransformState& other) + { + return adoptRef(new HitTestingTransformState(other)); + } + + void move(const IntSize& s) + { + move(s.width(), s.height()); + } + + void move(int x, int y); + void applyTransform(const TransformationMatrix& transformFromContainer, bool accumulateTransform); + FloatPoint mappedPoint() const; + FloatQuad mappedQuad() const; + void flatten(); + + FloatPoint m_lastPlanarPoint; + FloatQuad m_lastPlanarQuad; + TransformationMatrix m_accumulatedTransform; + bool m_accumulatingTransform; + +private: + HitTestingTransformState(const FloatPoint& p, const FloatQuad& quad) + : m_lastPlanarPoint(p) + , m_lastPlanarQuad(quad) + , m_accumulatingTransform(false) + { + } + + HitTestingTransformState(const HitTestingTransformState& other) + : RefCounted<HitTestingTransformState>() + , m_lastPlanarPoint(other.m_lastPlanarPoint) + , m_lastPlanarQuad(other.m_lastPlanarQuad) + , m_accumulatedTransform(other.m_accumulatedTransform) + , m_accumulatingTransform(other.m_accumulatingTransform) + { + } +}; + +} // namespace WebCore + +#endif // TransformState_h diff --git a/WebCore/rendering/bidi.cpp b/WebCore/rendering/bidi.cpp index 3b0e7c4..bfb5291 100644 --- a/WebCore/rendering/bidi.cpp +++ b/WebCore/rendering/bidi.cpp @@ -29,6 +29,7 @@ #include "InlineTextBox.h" #include "Logging.h" #include "RenderArena.h" +#include "RenderInline.h" #include "RenderLayer.h" #include "RenderListMarker.h" #include "RenderView.h" @@ -95,7 +96,7 @@ static bool betweenMidpoints; static bool isLineEmpty = true; static bool previousLineBrokeCleanly = true; -static int getBorderPaddingMargin(RenderBox* child, bool endOfInline) +static int getBorderPaddingMargin(RenderBoxModelObject* child, bool endOfInline) { bool leftSide = (child->style()->direction() == LTR) ? !endOfInline : endOfInline; if (leftSide) @@ -108,11 +109,11 @@ static int inlineWidth(RenderObject* child, bool start = true, bool end = true) unsigned lineDepth = 1; int extraWidth = 0; RenderObject* parent = child->parent(); - while (parent->isBox() && parent->isInline() && !parent->isInlineBlockOrInlineTable() && lineDepth++ < cMaxLineDepth) { - if (start && parent->firstChild() == child) - extraWidth += getBorderPaddingMargin(toRenderBox(parent), false); - if (end && parent->lastChild() == child) - extraWidth += getBorderPaddingMargin(toRenderBox(parent), true); + while (parent->isInline() && !parent->isInlineBlockOrInlineTable() && lineDepth++ < cMaxLineDepth) { + if (start && !child->previousSibling()) + extraWidth += getBorderPaddingMargin(toRenderBoxModelObject(parent), false); + if (end && !child->nextSibling()) + extraWidth += getBorderPaddingMargin(toRenderBoxModelObject(parent), true); child = parent; parent = child->parent(); } @@ -179,7 +180,7 @@ static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, while (current) { next = 0; - if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) { + if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned() && !current->isText()) { next = current->firstChild(); if (next && resolver && next->isRenderInline()) { EUnicodeBidi ub = next->style()->unicodeBidi(); @@ -393,7 +394,7 @@ static void addMidpoint(const InlineIterator& midpoint) static void appendRunsForObject(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) { if (start > end || obj->isFloating() || - (obj->isPositioned() && !obj->hasStaticX() && !obj->hasStaticY() && !obj->container()->isRenderInline())) + (obj->isPositioned() && !obj->style()->hasStaticX() && !obj->style()->hasStaticY() && !obj->container()->isRenderInline())) return; bool haveNextMidpoint = (sCurrMidpoint < sNumMidpoints); @@ -462,7 +463,37 @@ void InlineBidiResolver::appendRun() m_status.eor = OtherNeutral; } -InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj) +static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false) +{ + if (isRootLineBox) + return toRenderBlock(obj)->createRootInlineBox(); + + if (obj->isText()) { + InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox(); + // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode + // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.) + if (obj->isBR()) + textBox->setIsText(isOnlyRun || obj->document()->inStrictMode()); + return textBox; + } + + if (obj->isBox()) + return toRenderBox(obj)->createInlineBox(); + + return toRenderInline(obj)->createInlineFlowBox(); +} + +static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout) +{ + if (o->isText()) { + if (o->prefWidthsDirty() && o->isCounter()) + toRenderText(o)->calcPrefWidths(0); // FIXME: Counters depend on this hack. No clue why. Should be investigated and removed. + toRenderText(o)->dirtyLineBoxes(fullLayout); + } else + toRenderInline(o)->dirtyLineBoxes(fullLayout); +} + +InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, bool firstLine) { // See if we have an unconstructed line box for this object that is also // the last item on the line. @@ -472,10 +503,9 @@ InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj) InlineFlowBox* result = 0; do { ASSERT(obj->isRenderInline() || obj == this); - RenderFlow* flow = static_cast<RenderFlow*>(obj); - + // Get the last box we made for this render object. - parentBox = flow->lastLineBox(); + parentBox = obj->isRenderInline() ? toRenderInline(obj)->lastLineBox() : toRenderBlock(obj)->lastLineBox(); // If this box is constructed then it is from a previous line, and we need // to make a new box for our line. If this box is unconstructed but it has @@ -486,10 +516,10 @@ InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj) if (!parentBox || parentBox->isConstructed() || parentBox->nextOnLine()) { // We need to make a new box for this render object. Once // made, we need to place it at the end of the current line. - InlineBox* newBox = obj->createInlineBox(false, obj == this); + InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this); ASSERT(newBox->isInlineFlowBox()); parentBox = static_cast<InlineFlowBox*>(newBox); - parentBox->setFirstLineStyleBit(m_firstLine); + parentBox->setFirstLineStyleBit(firstLine); constructedNewBox = true; } @@ -516,7 +546,7 @@ InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj) return result; } -RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool lastLine, RenderObject* endObject) +RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool firstLine, bool lastLine, RenderObject* endObject) { ASSERT(firstRun); @@ -527,16 +557,16 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, if (runCount == 2 && !r->m_object->isListMarker()) isOnlyRun = ((style()->direction() == RTL) ? lastRun : firstRun)->m_object->isListMarker(); - InlineBox* box = r->m_object->createInlineBox(r->m_object->isPositioned(), false, isOnlyRun); + InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun); r->m_box = box; if (box) { // If we have no parent box yet, or if the run is not simply a sibling, // then we need to construct inline boxes as necessary to properly enclose the // run's inline box. - if (!parentBox || parentBox->object() != r->m_object->parent()) + if (!parentBox || parentBox->renderer() != r->m_object->parent()) // Create new inline boxes all the way back to the appropriate insertion point. - parentBox = createLineBoxes(r->m_object->parent()); + parentBox = createLineBoxes(r->m_object->parent(), firstLine); // Append the inline box to this line. parentBox->addToLine(box); @@ -570,10 +600,10 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, return lastRootBox(); } -void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd) +void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd) { // First determine our total width. - int availableWidth = lineWidth(height()); + int availableWidth = lineWidth(height(), firstLine); int totWidth = lineBox->getFlowSpacingWidth(); bool needsWordSpacing = false; unsigned numSpaces = 0; @@ -598,10 +628,10 @@ void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, Bidi if (int length = rt->textLength()) { if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characters()[r->m_start])) - totWidth += rt->style(m_firstLine)->font().wordSpacing(); + totWidth += rt->style(firstLine)->font().wordSpacing(); needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length; } - r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, m_firstLine)); + r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, firstLine)); } else if (!r->m_object->isRenderInline()) { RenderBox* renderBox = toRenderBox(r->m_object); renderBox->calcWidth(); @@ -616,7 +646,7 @@ void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, Bidi // we now examine our text-align property in order to determine where to position the // objects horizontally. The total width of the line can be increased if we end up // justifying text. - int x = leftOffset(height()); + int x = leftOffset(height(), firstLine); switch(textAlign) { case LEFT: case WEBKIT_LEFT: @@ -741,11 +771,14 @@ void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRu // Align positioned boxes with the top of the line box. This is // a reasonable approximation of an appropriate y position. if (r->m_object->isPositioned()) - r->m_box->setYPos(height()); + r->m_box->setY(height()); // Position is used to properly position both replaced elements and // to update the static normal flow x/y of positioned elements. - r->m_object->position(r->m_box); + if (r->m_object->isText()) + toRenderText(r->m_object)->positionLineBox(r->m_box); + else if (r->m_object->isBox()) + toRenderBox(r->m_object)->positionLineBox(r->m_box); } // Positioned objects and zero-length text nodes destroy their boxes in // position(), which unnecessarily dirties the line. @@ -772,8 +805,6 @@ static inline bool isCollapsibleSpace(UChar character, RenderText* renderer) void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, int& repaintBottom) { bool useRepaintBounds = false; - - invalidateVerticalPosition(); m_overflowHeight = 0; @@ -785,7 +816,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // FIXME: Do something better when floats are present. bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren; if (fullLayout) - deleteLineBoxes(); + lineBoxes()->deleteLineBoxes(renderArena()); // Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't // clip. @@ -824,9 +855,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i bool endOfInline = false; RenderObject* o = bidiFirst(this, 0, false); Vector<FloatWithRect> floats; - int containerWidth = max(0, containingBlockWidth()); while (o) { - o->invalidateVerticalPosition(); if (o->isReplaced() || o->isFloating() || o->isPositioned()) { RenderBox* box = toRenderBox(o); @@ -842,27 +871,23 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i else { #ifdef ANDROID_LAYOUT // ignore text wrap for textField or menuList - if (doTextWrap && (o->isTextField() || o->isMenuList())) - doTextWrap = false; + if (doTextWrap && (o->isTextField() || o->isMenuList())) + doTextWrap = false; #endif if (o->isFloating()) floats.append(FloatWithRect(box)); else if (fullLayout || o->needsLayout()) // Replaced elements - o->dirtyLineBoxes(fullLayout); + toRenderBox(o)->dirtyLineBoxes(fullLayout); o->layoutIfNeeded(); } } else if (o->isText() || (o->isRenderInline() && !endOfInline)) { if (fullLayout || o->selfNeedsLayout()) - o->dirtyLineBoxes(fullLayout); - - // Calculate margins of inline flows so that they can be used later by line layout. - if (o->isRenderInline()) - static_cast<RenderFlow*>(o)->calcMargins(containerWidth); + dirtyLineBoxesForRenderer(o, fullLayout); o->setNeedsLayout(false); #ifdef ANDROID_LAYOUT if (doTextWrap && !hasTextToWrap && o->isText()) { - Node* node = o->element(); + Node* node = o->node(); // as it is very common for sites to use a serial of <a> or // <li> as tabs, we don't force text to wrap if all the text // are short and within an <a> or <li> tag, and only separated @@ -880,6 +905,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i } } #endif + if (!o->isText()) + toRenderInline(o)->invalidateVerticalPosition(); // FIXME: Should do better here and not always invalidate everything. } o = bidiNext(this, o, 0, false, &endOfInline); } @@ -921,18 +948,19 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // We want to skip ahead to the first dirty line InlineBidiResolver resolver; unsigned floatIndex; - RootInlineBox* startLine = determineStartPosition(fullLayout, resolver, floats, floatIndex); + bool firstLine = true; + RootInlineBox* startLine = determineStartPosition(firstLine, fullLayout, resolver, floats, floatIndex); if (fullLayout && !selfNeedsLayout()) { setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like // we're supposed to. RenderView* v = view(); - if (v && !v->doingFullRepaint() && m_layer) { + if (v && !v->doingFullRepaint() && hasLayer()) { // Because we waited until we were already inside layout to discover // that the block really needed a full layout, we missed our chance to repaint the layer // before layout started. Luckily the layer has cached the repaint rect for its original // position and size, and so we can use that to make a repaint happen now. - v->repaintViewRectangle(m_layer->repaintRect()); + repaintUsingContainer(containerForRepaint(), layer()->repaintRect()); } } @@ -974,9 +1002,9 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // adjust the height accordingly. // A line break can be either the first or the last object on a line, depending on its direction. if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) { - RenderObject* lastObject = lastLeafChild->object(); + RenderObject* lastObject = lastLeafChild->renderer(); if (!lastObject->isBR()) - lastObject = lastRootBox()->firstLeafChild()->object(); + lastObject = lastRootBox()->firstLeafChild()->renderer(); if (lastObject->isBR()) { EClear clear = lastObject->style()->clear(); if (clear != CNONE) @@ -999,7 +1027,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i isLineEmpty = true; EClear clear = CNONE; - end = findNextLineBreak(resolver, &clear); + end = findNextLineBreak(resolver, firstLine, &clear); if (resolver.position().atEnd()) { resolver.deleteRuns(); checkForFloatsFromLastLine = true; @@ -1031,26 +1059,18 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i TextDirection direction = style()->direction(); bool shouldReorder = trailingSpaceRun != (direction == LTR ? resolver.lastRun() : resolver.firstRun()); if (firstSpace != trailingSpaceRun->start()) { - ETextAlign textAlign = style()->textAlign(); - // If the trailing white space is at the right hand side of a left-aligned line, then computeHorizontalPositionsForLine() - // does not care if trailingSpaceRun includes non-spaces at the beginning. In all other cases, trailingSpaceRun has to - // contain only the spaces, either because we re-order them or because computeHorizontalPositionsForLine() needs to know - // their width. - bool shouldSeparateSpaces = textAlign != LEFT && textAlign != WEBKIT_LEFT && textAlign != TAAUTO || trailingSpaceRun->m_level % 2 || direction == RTL || shouldReorder; - if (shouldSeparateSpaces) { - BidiContext* baseContext = resolver.context(); - while (BidiContext* parent = baseContext->parent()) - baseContext = parent; - - BidiRun* newTrailingRun = new (renderArena()) BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral); - trailingSpaceRun->m_stop = firstSpace; - if (direction == LTR) - resolver.addRun(newTrailingRun); - else - resolver.prependRun(newTrailingRun); - trailingSpaceRun = newTrailingRun; - shouldReorder = false; - } + BidiContext* baseContext = resolver.context(); + while (BidiContext* parent = baseContext->parent()) + baseContext = parent; + + BidiRun* newTrailingRun = new (renderArena()) BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral); + trailingSpaceRun->m_stop = firstSpace; + if (direction == LTR) + resolver.addRun(newTrailingRun); + else + resolver.prependRun(newTrailingRun); + trailingSpaceRun = newTrailingRun; + shouldReorder = false; } if (shouldReorder) { if (direction == LTR) { @@ -1072,12 +1092,12 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i RootInlineBox* lineBox = 0; if (resolver.runCount()) { - lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), !end.obj, end.obj && !end.pos ? end.obj : 0); + lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), firstLine, !end.obj, end.obj && !end.pos ? end.obj : 0); if (lineBox) { lineBox->setEndsWithBreak(previousLineBrokeCleanly); // Now we position all of our text runs horizontally. - computeHorizontalPositionsForLine(lineBox, resolver.firstRun(), trailingSpaceRun, end.atEnd()); + computeHorizontalPositionsForLine(lineBox, firstLine, resolver.firstRun(), trailingSpaceRun, end.atEnd()); // Now position our text runs vertically. computeVerticalPositionsForLine(lineBox, resolver.firstRun()); @@ -1105,7 +1125,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i } } - m_firstLine = false; + firstLine = false; newLine(clear); } @@ -1208,7 +1228,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i checkLinesForTextOverflow(); } -RootInlineBox* RenderBlock::determineStartPosition(bool& fullLayout, InlineBidiResolver& resolver, Vector<FloatWithRect>& floats, unsigned& numCleanFloats) +RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLayout, InlineBidiResolver& resolver, Vector<FloatWithRect>& floats, unsigned& numCleanFloats) { RootInlineBox* curr = 0; RootInlineBox* last = 0; @@ -1264,7 +1284,7 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& fullLayout, InlineBidiR // We have a dirty line. if (RootInlineBox* prevRootBox = curr->prevRootBox()) { // We have a previous line. - if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())) + if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength()))) // The previous line didn't break cleanly or broke at a newline // that has been deleted, so treat it as dirty too. curr = prevRootBox; @@ -1301,7 +1321,7 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& fullLayout, InlineBidiR setHeight(savedHeight); } - m_firstLine = !last; + firstLine = !last; previousLineBrokeCleanly = !last || last->endsWithBreak(); RenderObject* startObj; @@ -1470,12 +1490,12 @@ static inline bool shouldPreserveNewline(RenderObject* object) return object->style()->preserveNewline(); } -static bool inlineFlowRequiresLineBox(RenderBox* flow) +static bool inlineFlowRequiresLineBox(RenderInline* flow) { // FIXME: Right now, we only allow line boxes for inlines that are truly empty. // We need to fix this, though, because at the very least, inlines containing only // ignorable whitespace should should also have line boxes. - return flow->isRenderInline() && !flow->firstChild() && flow->hasHorizontalBordersPaddingOrMargin(); + return !flow->firstChild() && flow->hasHorizontalBordersPaddingOrMargin(); } static inline bool requiresLineBox(const InlineIterator& it) @@ -1483,7 +1503,7 @@ static inline bool requiresLineBox(const InlineIterator& it) if (it.obj->isFloatingOrPositioned()) return false; - if (it.obj->isRenderInline() && !inlineFlowRequiresLineBox(toRenderBox(it.obj))) + if (it.obj->isRenderInline() && !inlineFlowRequiresLineBox(toRenderInline(it.obj))) return false; if (!shouldCollapseWhiteSpace(it.obj->style()) || it.obj->isBR()) @@ -1524,33 +1544,34 @@ void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator) // A relative positioned inline encloses us. In this case, we also have to determine our // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned // inline so that we can obtain the value later. - c->setStaticX(style()->direction() == LTR ? leftOffset(height()) : rightOffset(height())); - c->setStaticY(height()); + toRenderInline(c)->layer()->setStaticX(style()->direction() == LTR ? leftOffset(height(), false) : rightOffset(height(), false)); + toRenderInline(c)->layer()->setStaticY(height()); } - if (object->hasStaticX()) { - if (object->style()->isOriginalDisplayInlineType()) - object->setStaticX(style()->direction() == LTR ? leftOffset(height()) : width() - rightOffset(height())); + RenderBox* box = toRenderBox(object); + if (box->style()->hasStaticX()) { + if (box->style()->isOriginalDisplayInlineType()) + box->layer()->setStaticX(style()->direction() == LTR ? leftOffset(height(), false) : width() - rightOffset(height(), false)); else - object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); + box->layer()->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); } - if (object->hasStaticY()) - object->setStaticY(height()); + if (box->style()->hasStaticY()) + box->layer()->setStaticY(height()); } iterator.increment(); } } -int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver) +int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver, bool firstLine) { - int availableWidth = lineWidth(height()); + int availableWidth = lineWidth(height(), firstLine); while (!resolver.position().atEnd() && !requiresLineBox(resolver.position())) { RenderObject* object = resolver.position().obj; if (object->isFloating()) { insertFloatingObject(toRenderBox(object)); positionNewFloats(); - availableWidth = lineWidth(height()); + availableWidth = lineWidth(height(), firstLine); } else if (object->isPositioned()) { // FIXME: The math here is actually not really right. It's a best-guess approximation that // will work for the common cases @@ -1559,19 +1580,20 @@ int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver) // A relative positioned inline encloses us. In this case, we also have to determine our // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned // inline so that we can obtain the value later. - c->setStaticX(style()->direction() == LTR ? leftOffset(height()) : rightOffset(height())); - c->setStaticY(height()); + toRenderInline(c)->layer()->setStaticX(style()->direction() == LTR ? leftOffset(height(), firstLine) : rightOffset(height(), firstLine)); + toRenderInline(c)->layer()->setStaticY(height()); } - if (object->hasStaticX()) { - if (object->style()->isOriginalDisplayInlineType()) - object->setStaticX(style()->direction() == LTR ? leftOffset(height()) : width() - rightOffset(height())); + RenderBox* box = toRenderBox(object); + if (box->style()->hasStaticX()) { + if (box->style()->isOriginalDisplayInlineType()) + box->layer()->setStaticX(style()->direction() == LTR ? leftOffset(height(), firstLine) : width() - rightOffset(height(), firstLine)); else - object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); + box->layer()->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); } - if (object->hasStaticY()) - object->setStaticY(height()); + if (box->style()->hasStaticY()) + box->layer()->setStaticY(height()); } resolver.increment(); } @@ -1596,7 +1618,7 @@ static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObjec return false; } -void RenderBlock::fitBelowFloats(int widthToFit, int& availableWidth) +void RenderBlock::fitBelowFloats(int widthToFit, bool firstLine, int& availableWidth) { ASSERT(widthToFit > availableWidth); @@ -1608,7 +1630,7 @@ void RenderBlock::fitBelowFloats(int widthToFit, int& availableWidth) if (!floatBottom) break; - newLineWidth = lineWidth(floatBottom); + newLineWidth = lineWidth(floatBottom, firstLine); lastFloatBottom = floatBottom; if (newLineWidth >= widthToFit) break; @@ -1620,13 +1642,20 @@ void RenderBlock::fitBelowFloats(int widthToFit, int& availableWidth) } } -InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, EClear* clear) +static inline unsigned textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, int xPos, bool isFixedPitch, bool collapseWhiteSpace) +{ + if (isFixedPitch || (!from && len == text->textLength())) + return text->width(from, len, font, xPos); + return font.width(TextRun(text->characters() + from, len, !collapseWhiteSpace, xPos)); +} + +InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool firstLine, EClear* clear) { ASSERT(resolver.position().block == this); bool appliedStartWidth = resolver.position().pos > 0; - int width = skipLeadingWhitespace(resolver); + int width = skipLeadingWhitespace(resolver, firstLine); int w = 0; int tmpW = 0; @@ -1715,16 +1744,17 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle // it after moving to next line (in newLine() func) if (floatsFitOnLine && floatBox->width() + floatBox->marginLeft() + floatBox->marginRight() + w + tmpW <= width) { positionNewFloats(); - width = lineWidth(height()); + width = lineWidth(height(), firstLine); } else floatsFitOnLine = false; } else if (o->isPositioned()) { // If our original display wasn't an inline type, then we can // go ahead and determine our static x position now. - bool isInlineType = o->style()->isOriginalDisplayInlineType(); - bool needToSetStaticX = o->hasStaticX(); - if (o->hasStaticX() && !isInlineType) { - o->setStaticX(o->parent()->style()->direction() == LTR ? + RenderBox* box = toRenderBox(o); + bool isInlineType = box->style()->isOriginalDisplayInlineType(); + bool needToSetStaticX = box->style()->hasStaticX(); + if (box->style()->hasStaticX() && !isInlineType) { + box->layer()->setStaticX(o->parent()->style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); needToSetStaticX = false; @@ -1732,9 +1762,9 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle // If our original display was an INLINE type, then we can go ahead // and determine our static y position now. - bool needToSetStaticY = o->hasStaticY(); - if (o->hasStaticY() && isInlineType) { - o->setStaticY(height()); + bool needToSetStaticY = box->style()->hasStaticY(); + if (box->style()->hasStaticY() && isInlineType) { + box->layer()->setStaticY(height()); needToSetStaticY = false; } @@ -1760,7 +1790,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle // Right now, we should only encounter empty inlines here. ASSERT(!o->firstChild()); - RenderBox* flowBox = toRenderBox(o); + RenderInline* flowBox = toRenderInline(o); // Now that some inline flows have line boxes, if we are already ignoring spaces, we need // to make sure that we stop to include this object and then start ignoring spaces again. @@ -1827,7 +1857,8 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle int len = strlen - pos; const UChar* str = t->characters(); - const Font& f = t->style(m_firstLine)->font(); + const Font& f = t->style(firstLine)->font(); + bool isFixedPitch = f.isFixedPitch(); int lastSpace = pos; int wordSpacing = o->style()->wordSpacing(); @@ -1876,12 +1907,12 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle addMidpoint(beforeSoftHyphen); // Add the width up to but not including the hyphen. - tmpW += t->width(lastSpace, pos - lastSpace, f, w + tmpW) + lastSpaceWordSpacing; + tmpW += textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; // For wrapping text only, include the hyphen. We need to ensure it will fit // on the line if it shows when we break. if (autoWrap) - tmpW += t->width(pos, 1, f, w + tmpW); + tmpW += textWidth(t, pos, 1, f, w + tmpW, isFixedPitch, collapseWhiteSpace); InlineIterator afterSoftHyphen(0, o, pos); afterSoftHyphen.increment(); @@ -1901,7 +1932,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle if ((breakAll || breakWords) && !midWordBreak) { wrapW += charWidth; - charWidth = t->width(pos, 1, f, w + wrapW); + charWidth = textWidth(t, pos, 1, f, w + wrapW, isFixedPitch, collapseWhiteSpace); midWordBreak = w + wrapW + charWidth > width; } @@ -1926,7 +1957,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle } } - int additionalTmpW = t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing; + int additionalTmpW = textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; tmpW += additionalTmpW; if (!appliedStartWidth) { tmpW += inlineWidth(o, true, false); @@ -1936,14 +1967,14 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle applyWordSpacing = wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace; if (!w && autoWrap && tmpW > width) - fitBelowFloats(tmpW, width); + fitBelowFloats(tmpW, firstLine, width); if (autoWrap || breakWords) { // If we break only after white-space, consider the current character // as candidate width for this line. bool lineWasTooWide = false; if (w + tmpW <= width && currentCharacterIsWS && o->style()->breakOnlyAfterWhiteSpace() && !midWordBreak) { - int charWidth = t->width(pos, 1, f, w + tmpW) + (applyWordSpacing ? wordSpacing : 0); + int charWidth = textWidth(t, pos, 1, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + (applyWordSpacing ? wordSpacing : 0); // Check if line is too big even without the extra space // at the end of the line. If it is not, do nothing. // If the line needs the extra whitespace to be too long, @@ -1973,7 +2004,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle tmpW -= additionalTmpW; if (pos > 0 && str[pos-1] == softHyphen) // Subtract the width of the soft hyphen out since we fit on a line. - tmpW -= t->width(pos-1, 1, f, w+tmpW); + tmpW -= textWidth(t, pos - 1, 1, f, w + tmpW, isFixedPitch, collapseWhiteSpace); } } @@ -2064,7 +2095,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle // IMPORTANT: pos is > length here! if (!ignoringSpaces) - tmpW += t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing; + tmpW += textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; tmpW += inlineWidth(o, !appliedStartWidth, true); } else ASSERT_NOT_REACHED(); @@ -2091,7 +2122,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle checkForBreak = true; bool willFitOnLine = w + tmpW <= width; if (!willFitOnLine && !w) { - fitBelowFloats(tmpW, width); + fitBelowFloats(tmpW, firstLine, width); willFitOnLine = tmpW <= width; } bool canPlaceOnLine = willFitOnLine || !autoWrapWasEverTrueOnLine; @@ -2114,7 +2145,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle if (w) goto end; - fitBelowFloats(tmpW, width); + fitBelowFloats(tmpW, firstLine, width); // |width| may have been adjusted because we got shoved down past a float (thus // giving us more room), so we need to retest, and only jump to @@ -2154,8 +2185,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, ECle } end: - - if (lBreak == resolver.position() && !lBreak.obj->isBR()) { + if (lBreak == resolver.position() && (!lBreak.obj || !lBreak.obj->isBR())) { // we just add as much as possible if (style()->whiteSpace() == PRE) { // FIXME: Don't really understand this case. @@ -2265,8 +2295,8 @@ void RenderBlock::checkLinesForTextOverflow() // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()" bool ltr = style()->direction() == LTR; for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { - int blockEdge = ltr ? rightOffset(curr->yPos()) : leftOffset(curr->yPos()); - int lineBoxEdge = ltr ? curr->xPos() + curr->width() : curr->xPos(); + int blockEdge = ltr ? rightOffset(curr->y(), curr == firstRootBox()) : leftOffset(curr->y(), curr == firstRootBox()); + int lineBoxEdge = ltr ? curr->x() + curr->width() : curr->x(); if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) { // This line spills out of our box in the appropriate direction. Now we need to see if the line // can be truncated. In order for truncation to be possible, the line must have sufficient space to diff --git a/WebCore/rendering/style/ContentData.cpp b/WebCore/rendering/style/ContentData.cpp index b38cc49..410cad4 100644 --- a/WebCore/rendering/style/ContentData.cpp +++ b/WebCore/rendering/style/ContentData.cpp @@ -30,22 +30,9 @@ namespace WebCore { void ContentData::clear() { - switch (m_type) { - case CONTENT_NONE: - break; - case CONTENT_OBJECT: - m_content.m_image->deref(); - break; - case CONTENT_TEXT: - m_content.m_text->deref(); - break; - case CONTENT_COUNTER: - delete m_content.m_counter; - break; - } + deleteContent(); ContentData* n = m_next; - m_type = CONTENT_NONE; m_next = 0; // Reverse the list so we can delete without recursing. @@ -63,4 +50,47 @@ void ContentData::clear() } } +bool ContentData::dataEquivalent(const ContentData& other) const +{ + if (type() != other.type()) + return false; + + switch (type()) { + case CONTENT_NONE: + return true; + break; + case CONTENT_TEXT: + return equal(text(), other.text()); + break; + case CONTENT_OBJECT: + return StyleImage::imagesEquivalent(image(), other.image()); + break; + case CONTENT_COUNTER: + return *counter() == *other.counter(); + break; + } + + ASSERT_NOT_REACHED(); + return false; +} + +void ContentData::deleteContent() +{ + switch (m_type) { + case CONTENT_NONE: + break; + case CONTENT_OBJECT: + m_content.m_image->deref(); + break; + case CONTENT_TEXT: + m_content.m_text->deref(); + break; + case CONTENT_COUNTER: + delete m_content.m_counter; + break; + } + + m_type = CONTENT_NONE; +} + } // namespace WebCore diff --git a/WebCore/rendering/style/ContentData.h b/WebCore/rendering/style/ContentData.h index d924d1a..24d5f86 100644 --- a/WebCore/rendering/style/ContentData.h +++ b/WebCore/rendering/style/ContentData.h @@ -25,16 +25,18 @@ #ifndef ContentData_h #define ContentData_h +#include "PlatformString.h" #include "RenderStyleConstants.h" +#include "StringImpl.h" +#include "StyleImage.h" #include <wtf/Noncopyable.h> namespace WebCore { class CounterContent; -class StringImpl; -class StyleImage; struct ContentData : Noncopyable { +public: ContentData() : m_type(CONTENT_NONE) , m_next(0) @@ -48,7 +50,49 @@ struct ContentData : Noncopyable { void clear(); - ContentType m_type; + bool isCounter() const { return m_type == CONTENT_COUNTER; } + bool isImage() const { return m_type == CONTENT_OBJECT; } + bool isNone() const { return m_type == CONTENT_NONE; } + bool isText() const { return m_type == CONTENT_TEXT; } + + StyleContentType type() const { return m_type; } + + bool dataEquivalent(const ContentData&) const; + + StyleImage* image() const { return m_content.m_image; } + void setImage(PassRefPtr<StyleImage> image) + { + deleteContent(); + m_type = CONTENT_OBJECT; + m_content.m_image = image.releaseRef(); + } + + StringImpl* text() const { return m_content.m_text; } + void setText(PassRefPtr<StringImpl> text) + { + deleteContent(); + m_type = CONTENT_TEXT; + m_content.m_text = text.releaseRef(); + } + + CounterContent* counter() const { return m_content.m_counter; } + void setCounter(CounterContent* counter) + { + deleteContent(); + m_type = CONTENT_COUNTER; + m_content.m_counter = counter; + } + + ContentData* next() const { return m_next; } + void setNext(ContentData* next) + { + m_next = next; + } + +private: + void deleteContent(); + + StyleContentType m_type; union { StyleImage* m_image; StringImpl* m_text; diff --git a/WebCore/rendering/style/CounterContent.h b/WebCore/rendering/style/CounterContent.h index 06440ad..cf11813 100644 --- a/WebCore/rendering/style/CounterContent.h +++ b/WebCore/rendering/style/CounterContent.h @@ -49,11 +49,11 @@ private: AtomicString m_separator; }; -static inline bool operator!=(const CounterContent& a, const CounterContent& b) +static inline bool operator==(const CounterContent& a, const CounterContent& b) { - return a.identifier() != b.identifier() - || a.listStyle() != b.listStyle() - || a.separator() != b.separator(); + return a.identifier() == b.identifier() + && a.listStyle() == b.listStyle() + && a.separator() == b.separator(); } diff --git a/WebCore/rendering/style/RenderStyle.cpp b/WebCore/rendering/style/RenderStyle.cpp index 09445b9..fe53d30 100644 --- a/WebCore/rendering/style/RenderStyle.cpp +++ b/WebCore/rendering/style/RenderStyle.cpp @@ -185,7 +185,7 @@ bool RenderStyle::isStyleAvailable() const return this != CSSStyleSelector::styleNotYetAvailable(); } -static inline int pseudoBit(RenderStyle::PseudoId pseudo) +static inline int pseudoBit(PseudoId pseudo) { return 1 << (pseudo - 1); } @@ -271,14 +271,16 @@ static bool positionedObjectMoved(const LengthBox& a, const LengthBox& b) optimisations are unimplemented, and currently result in the worst case result causing a relayout of the containing block. */ -RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const +StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedContextSensitiveProperties) const { + changedContextSensitiveProperties = ContextSensitivePropertyNone; + #if ENABLE(SVG) // This is horribly inefficient. Eventually we'll have to integrate // this more directly by calling: Diff svgDiff = svgStyle->diff(other) // and then checking svgDiff and returning from the appropriate places below. if (m_svgStyle != other->m_svgStyle) - return Layout; + return StyleDifferenceLayout; #endif if (box->width != other->box->width || @@ -287,19 +289,19 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const box->height != other->box->height || box->min_height != other->box->min_height || box->max_height != other->box->max_height) - return Layout; + return StyleDifferenceLayout; if (box->vertical_align != other->box->vertical_align || noninherited_flags._vertical_align != other->noninherited_flags._vertical_align) - return Layout; + return StyleDifferenceLayout; if (box->boxSizing != other->box->boxSizing) - return Layout; + return StyleDifferenceLayout; if (surround->margin != other->surround->margin) - return Layout; + return StyleDifferenceLayout; if (surround->padding != other->surround->padding) - return Layout; + return StyleDifferenceLayout; if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) { if (rareNonInheritedData->m_appearance != other->rareNonInheritedData->m_appearance || @@ -307,30 +309,47 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const rareNonInheritedData->marginBottomCollapse != other->rareNonInheritedData->marginBottomCollapse || rareNonInheritedData->lineClamp != other->rareNonInheritedData->lineClamp || rareNonInheritedData->textOverflow != other->rareNonInheritedData->textOverflow) - return Layout; + return StyleDifferenceLayout; if (rareNonInheritedData->flexibleBox.get() != other->rareNonInheritedData->flexibleBox.get() && *rareNonInheritedData->flexibleBox.get() != *other->rareNonInheritedData->flexibleBox.get()) - return Layout; + return StyleDifferenceLayout; if (!rareNonInheritedData->shadowDataEquivalent(*other->rareNonInheritedData.get())) - return Layout; + return StyleDifferenceLayout; if (!rareNonInheritedData->reflectionDataEquivalent(*other->rareNonInheritedData.get())) - return Layout; + return StyleDifferenceLayout; if (rareNonInheritedData->m_multiCol.get() != other->rareNonInheritedData->m_multiCol.get() && *rareNonInheritedData->m_multiCol.get() != *other->rareNonInheritedData->m_multiCol.get()) - return Layout; + return StyleDifferenceLayout; if (rareNonInheritedData->m_transform.get() != other->rareNonInheritedData->m_transform.get() && - *rareNonInheritedData->m_transform.get() != *other->rareNonInheritedData->m_transform.get()) - return Layout; + *rareNonInheritedData->m_transform.get() != *other->rareNonInheritedData->m_transform.get()) { +#if USE(ACCELERATED_COMPOSITING) + changedContextSensitiveProperties |= ContextSensitivePropertyTransform; + // Don't return; keep looking for another change +#else + return StyleDifferenceLayout; +#endif + } + +#if !USE(ACCELERATED_COMPOSITING) + if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) { + if (rareNonInheritedData->m_transformStyle3D != other->rareNonInheritedData->m_transformStyle3D || + rareNonInheritedData->m_backfaceVisibility != other->rareNonInheritedData->m_backfaceVisibility || + rareNonInheritedData->m_perspective != other->rareNonInheritedData->m_perspective || + rareNonInheritedData->m_perspectiveOriginX != other->rareNonInheritedData->m_perspectiveOriginX || + rareNonInheritedData->m_perspectiveOriginY != other->rareNonInheritedData->m_perspectiveOriginY) + return StyleDifferenceLayout; + } +#endif #if ENABLE(DASHBOARD_SUPPORT) // If regions change, trigger a relayout to re-calc regions. if (rareNonInheritedData->m_dashboardRegions != other->rareNonInheritedData->m_dashboardRegions) - return Layout; + return StyleDifferenceLayout; #endif } @@ -342,13 +361,13 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const rareInheritedData->nbspMode != other->rareInheritedData->nbspMode || rareInheritedData->khtmlLineBreak != other->rareInheritedData->khtmlLineBreak || rareInheritedData->textSecurity != other->rareInheritedData->textSecurity) - return Layout; + return StyleDifferenceLayout; if (!rareInheritedData->shadowDataEquivalent(*other->rareInheritedData.get())) - return Layout; + return StyleDifferenceLayout; if (textStrokeWidth() != other->textStrokeWidth()) - return Layout; + return StyleDifferenceLayout; } if (inherited->indent != other->inherited->indent || @@ -363,7 +382,7 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const noninherited_flags._position != other->noninherited_flags._position || noninherited_flags._floating != other->noninherited_flags._floating || noninherited_flags._originalDisplay != other->noninherited_flags._originalDisplay) - return Layout; + return StyleDifferenceLayout; if (((int)noninherited_flags._effectiveDisplay) >= TABLE) { @@ -371,26 +390,26 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const inherited_flags._empty_cells != other->inherited_flags._empty_cells || inherited_flags._caption_side != other->inherited_flags._caption_side || noninherited_flags._table_layout != other->noninherited_flags._table_layout) - return Layout; + return StyleDifferenceLayout; // In the collapsing border model, 'hidden' suppresses other borders, while 'none' // does not, so these style differences can be width differences. if (inherited_flags._border_collapse && - (borderTopStyle() == BHIDDEN && other->borderTopStyle() == BNONE || - borderTopStyle() == BNONE && other->borderTopStyle() == BHIDDEN || - borderBottomStyle() == BHIDDEN && other->borderBottomStyle() == BNONE || - borderBottomStyle() == BNONE && other->borderBottomStyle() == BHIDDEN || - borderLeftStyle() == BHIDDEN && other->borderLeftStyle() == BNONE || - borderLeftStyle() == BNONE && other->borderLeftStyle() == BHIDDEN || - borderRightStyle() == BHIDDEN && other->borderRightStyle() == BNONE || - borderRightStyle() == BNONE && other->borderRightStyle() == BHIDDEN)) - return Layout; + ((borderTopStyle() == BHIDDEN && other->borderTopStyle() == BNONE) || + (borderTopStyle() == BNONE && other->borderTopStyle() == BHIDDEN) || + (borderBottomStyle() == BHIDDEN && other->borderBottomStyle() == BNONE) || + (borderBottomStyle() == BNONE && other->borderBottomStyle() == BHIDDEN) || + (borderLeftStyle() == BHIDDEN && other->borderLeftStyle() == BNONE) || + (borderLeftStyle() == BNONE && other->borderLeftStyle() == BHIDDEN) || + (borderRightStyle() == BHIDDEN && other->borderRightStyle() == BNONE) || + (borderRightStyle() == BNONE && other->borderRightStyle() == BHIDDEN))) + return StyleDifferenceLayout; } if (noninherited_flags._effectiveDisplay == LIST_ITEM) { if (inherited_flags._list_style_type != other->inherited_flags._list_style_type || inherited_flags._list_style_position != other->inherited_flags._list_style_position) - return Layout; + return StyleDifferenceLayout; } if (inherited_flags._text_align != other->inherited_flags._text_align || @@ -398,12 +417,12 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const inherited_flags._direction != other->inherited_flags._direction || inherited_flags._white_space != other->inherited_flags._white_space || noninherited_flags._clear != other->noninherited_flags._clear) - return Layout; + return StyleDifferenceLayout; // Overflow returns a layout hint. if (noninherited_flags._overflowX != other->noninherited_flags._overflowX || noninherited_flags._overflowY != other->noninherited_flags._overflowY) - return Layout; + return StyleDifferenceLayout; // If our border widths change, then we need to layout. Other changes to borders // only necessitate a repaint. @@ -411,19 +430,19 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const borderTopWidth() != other->borderTopWidth() || borderBottomWidth() != other->borderBottomWidth() || borderRightWidth() != other->borderRightWidth()) - return Layout; + return StyleDifferenceLayout; // If the counter directives change, trigger a relayout to re-calculate counter values and rebuild the counter node tree. const CounterDirectiveMap* mapA = rareNonInheritedData->m_counterDirectives.get(); const CounterDirectiveMap* mapB = other->rareNonInheritedData->m_counterDirectives.get(); if (!(mapA == mapB || (mapA && mapB && *mapA == *mapB))) - return Layout; + return StyleDifferenceLayout; if (visual->counterIncrement != other->visual->counterIncrement || visual->counterReset != other->visual->counterReset) - return Layout; + return StyleDifferenceLayout; if (inherited->m_effectiveZoom != other->inherited->m_effectiveZoom) - return Layout; + return StyleDifferenceLayout; // Make sure these left/top/right/bottom checks stay below all layout checks and above // all visible checks. @@ -431,7 +450,7 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const if (surround->offset != other->surround->offset) { // Optimize for the case where a positioned layer is moving but not changing size. if (position() == AbsolutePosition && positionedObjectMoved(surround->offset, other->surround->offset)) - return LayoutPositionedMovementOnly; + return StyleDifferenceLayoutPositionedMovementOnly; // FIXME: We will need to do a bit of work in RenderObject/Box::setStyle before we // can stop doing a layout when relative positioned objects move. In particular, we'll need @@ -439,16 +458,24 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const //if (other->position() == RelativePosition) // return RepaintLayer; //else - return Layout; + return StyleDifferenceLayout; } else if (box->z_index != other->box->z_index || box->z_auto != other->box->z_auto || visual->clip != other->visual->clip || visual->hasClip != other->visual->hasClip) - return RepaintLayer; + return StyleDifferenceRepaintLayer; + } + + if (rareNonInheritedData->opacity != other->rareNonInheritedData->opacity) { +#if USE(ACCELERATED_COMPOSITING) + changedContextSensitiveProperties |= ContextSensitivePropertyOpacity; + // Don't return; keep looking for another change. +#else + return StyleDifferenceRepaintLayer; +#endif } - if (rareNonInheritedData->opacity != other->rareNonInheritedData->opacity || - rareNonInheritedData->m_mask != other->rareNonInheritedData->m_mask || + if (rareNonInheritedData->m_mask != other->rareNonInheritedData->m_mask || rareNonInheritedData->m_maskBoxImage != other->rareNonInheritedData->m_maskBoxImage) - return RepaintLayer; + return StyleDifferenceRepaintLayer; if (inherited->color != other->inherited->color || inherited_flags._visibility != other->inherited_flags._visibility || @@ -463,19 +490,30 @@ RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const rareNonInheritedData->m_borderFit != other->rareNonInheritedData->m_borderFit || rareInheritedData->textFillColor != other->rareInheritedData->textFillColor || rareInheritedData->textStrokeColor != other->rareInheritedData->textStrokeColor) - return Repaint; + return StyleDifferenceRepaint; + +#if USE(ACCELERATED_COMPOSITING) + if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) { + if (rareNonInheritedData->m_transformStyle3D != other->rareNonInheritedData->m_transformStyle3D || + rareNonInheritedData->m_backfaceVisibility != other->rareNonInheritedData->m_backfaceVisibility || + rareNonInheritedData->m_perspective != other->rareNonInheritedData->m_perspective || + rareNonInheritedData->m_perspectiveOriginX != other->rareNonInheritedData->m_perspectiveOriginX || + rareNonInheritedData->m_perspectiveOriginY != other->rareNonInheritedData->m_perspectiveOriginY) + return StyleDifferenceRecompositeLayer; + } +#endif // Cursors are not checked, since they will be set appropriately in response to mouse events, // so they don't need to cause any repaint or layout. // Animations don't need to be checked either. We always set the new style on the RenderObject, so we will get a chance to fire off // the resulting transition properly. - return Equal; + return StyleDifferenceEqual; } void RenderStyle::setClip(Length top, Length right, Length bottom, Length left) { - StyleVisualData *data = visual.access(); + StyleVisualData* data = visual.access(); data->clip.m_top = top; data->clip.m_right = right; data->clip.m_bottom = bottom; @@ -503,39 +541,6 @@ void RenderStyle::clearCursorList() inherited.access()->cursorData = 0; } -bool RenderStyle::contentDataEquivalent(const RenderStyle* otherStyle) const -{ - ContentData* c1 = rareNonInheritedData->m_content.get(); - ContentData* c2 = otherStyle->rareNonInheritedData->m_content.get(); - - while (c1 && c2) { - if (c1->m_type != c2->m_type) - return false; - - switch (c1->m_type) { - case CONTENT_NONE: - break; - case CONTENT_TEXT: - if (!equal(c1->m_content.m_text, c2->m_content.m_text)) - return false; - break; - case CONTENT_OBJECT: - if (!StyleImage::imagesEquivalent(c1->m_content.m_image, c2->m_content.m_image)) - return false; - break; - case CONTENT_COUNTER: - if (*c1->m_content.m_counter != *c2->m_content.m_counter) - return false; - break; - } - - c1 = c1->m_next; - c2 = c2->m_next; - } - - return !c1 && !c2; -} - void RenderStyle::clearContent() { if (rareNonInheritedData->m_content) @@ -549,8 +554,8 @@ void RenderStyle::setContent(PassRefPtr<StyleImage> image, bool add) OwnPtr<ContentData>& content = rareNonInheritedData.access()->m_content; ContentData* lastContent = content.get(); - while (lastContent && lastContent->m_next) - lastContent = lastContent->m_next; + while (lastContent && lastContent->next()) + lastContent = lastContent->next(); bool reuseContent = !add; ContentData* newContentData; @@ -561,34 +566,30 @@ void RenderStyle::setContent(PassRefPtr<StyleImage> image, bool add) newContentData = new ContentData; if (lastContent && !reuseContent) - lastContent->m_next = newContentData; + lastContent->setNext(newContentData); else content.set(newContentData); - newContentData->m_content.m_image = image.releaseRef(); - newContentData->m_type = CONTENT_OBJECT; + newContentData->setImage(image); } -void RenderStyle::setContent(StringImpl* s, bool add) +void RenderStyle::setContent(PassRefPtr<StringImpl> s, bool add) { if (!s) return; // The string is null. Nothing to do. Just bail. OwnPtr<ContentData>& content = rareNonInheritedData.access()->m_content; ContentData* lastContent = content.get(); - while (lastContent && lastContent->m_next) - lastContent = lastContent->m_next; + while (lastContent && lastContent->next()) + lastContent = lastContent->next(); bool reuseContent = !add; if (add && lastContent) { - if (lastContent->m_type == CONTENT_TEXT) { + if (lastContent->isText()) { // We can augment the existing string and share this ContentData node. - StringImpl* oldStr = lastContent->m_content.m_text; - String newStr = oldStr; - newStr.append(s); - newStr.impl()->ref(); - oldStr->deref(); - lastContent->m_content.m_text = newStr.impl(); + String newStr = lastContent->text(); + newStr.append(s.get()); + lastContent->setText(newStr.impl()); return; } } @@ -601,13 +602,11 @@ void RenderStyle::setContent(StringImpl* s, bool add) newContentData = new ContentData; if (lastContent && !reuseContent) - lastContent->m_next = newContentData; + lastContent->setNext(newContentData); else content.set(newContentData); - newContentData->m_content.m_text = s; - newContentData->m_content.m_text->ref(); - newContentData->m_type = CONTENT_TEXT; + newContentData->setText(s); } void RenderStyle::setContent(CounterContent* c, bool add) @@ -617,8 +616,8 @@ void RenderStyle::setContent(CounterContent* c, bool add) OwnPtr<ContentData>& content = rareNonInheritedData.access()->m_content; ContentData* lastContent = content.get(); - while (lastContent && lastContent->m_next) - lastContent = lastContent->m_next; + while (lastContent && lastContent->next()) + lastContent = lastContent->next(); bool reuseContent = !add; ContentData* newContentData = 0; @@ -629,15 +628,14 @@ void RenderStyle::setContent(CounterContent* c, bool add) newContentData = new ContentData; if (lastContent && !reuseContent) - lastContent->m_next = newContentData; + lastContent->setNext(newContentData); else content.set(newContentData); - newContentData->m_content.m_counter = c; - newContentData->m_type = CONTENT_COUNTER; + newContentData->setCounter(c); } -void RenderStyle::applyTransform(TransformationMatrix& transform, const IntSize& borderBoxSize, bool includeTransformOrigin) const +void RenderStyle::applyTransform(TransformationMatrix& transform, const IntSize& borderBoxSize, ApplyTransformOrigin applyOrigin) const { // transform-origin brackets the transform with translate operations. // Optimize for the case where the only transform is a translation, since the transform-origin is irrelevant @@ -645,26 +643,31 @@ void RenderStyle::applyTransform(TransformationMatrix& transform, const IntSize& bool applyTransformOrigin = false; unsigned s = rareNonInheritedData->m_transform->m_operations.operations().size(); unsigned i; - if (includeTransformOrigin) { + if (applyOrigin == IncludeTransformOrigin) { for (i = 0; i < s; i++) { TransformOperation::OperationType type = rareNonInheritedData->m_transform->m_operations.operations()[i]->getOperationType(); if (type != TransformOperation::TRANSLATE_X && type != TransformOperation::TRANSLATE_Y && - type != TransformOperation::TRANSLATE) { + type != TransformOperation::TRANSLATE && + type != TransformOperation::TRANSLATE_Z && + type != TransformOperation::TRANSLATE_3D + ) { applyTransformOrigin = true; break; } } } - if (applyTransformOrigin) - transform.translate(transformOriginX().calcFloatValue(borderBoxSize.width()), transformOriginY().calcFloatValue(borderBoxSize.height())); + if (applyTransformOrigin) { + transform.translate3d(transformOriginX().calcFloatValue(borderBoxSize.width()), transformOriginY().calcFloatValue(borderBoxSize.height()), transformOriginZ()); + } for (i = 0; i < s; i++) rareNonInheritedData->m_transform->m_operations.operations()[i]->apply(transform, borderBoxSize); - if (applyTransformOrigin) - transform.translate(-transformOriginX().calcFloatValue(borderBoxSize.width()), -transformOriginY().calcFloatValue(borderBoxSize.height())); + if (applyTransformOrigin) { + transform.translate3d(-transformOriginX().calcFloatValue(borderBoxSize.width()), -transformOriginY().calcFloatValue(borderBoxSize.height()), -transformOriginZ()); + } } #if ENABLE(XBL) @@ -818,7 +821,7 @@ AnimationList* RenderStyle::accessTransitions() return rareNonInheritedData->m_transitions.get(); } -const Animation* RenderStyle::transitionForProperty(int property) +const Animation* RenderStyle::transitionForProperty(int property) const { if (transitions()) { for (size_t i = 0; i < transitions()->size(); ++i) { diff --git a/WebCore/rendering/style/RenderStyle.h b/WebCore/rendering/style/RenderStyle.h index fed3057..32c0cf4 100644 --- a/WebCore/rendering/style/RenderStyle.h +++ b/WebCore/rendering/style/RenderStyle.h @@ -106,17 +106,6 @@ class StyleImage; class RenderStyle: public RefCounted<RenderStyle> { friend class CSSStyleSelector; - -public: - // static pseudo styles. Dynamic ones are produced on the fly. - enum PseudoId { NOPSEUDO, FIRST_LINE, FIRST_LETTER, BEFORE, AFTER, SELECTION, FIRST_LINE_INHERITED, SCROLLBAR, FILE_UPLOAD_BUTTON, INPUT_PLACEHOLDER, - SLIDER_THUMB, SEARCH_CANCEL_BUTTON, SEARCH_DECORATION, SEARCH_RESULTS_DECORATION, SEARCH_RESULTS_BUTTON, MEDIA_CONTROLS_PANEL, - MEDIA_CONTROLS_PLAY_BUTTON, MEDIA_CONTROLS_MUTE_BUTTON, MEDIA_CONTROLS_TIMELINE, MEDIA_CONTROLS_TIMELINE_CONTAINER, - MEDIA_CONTROLS_CURRENT_TIME_DISPLAY, MEDIA_CONTROLS_TIME_REMAINING_DISPLAY, MEDIA_CONTROLS_SEEK_BACK_BUTTON, - MEDIA_CONTROLS_SEEK_FORWARD_BUTTON, MEDIA_CONTROLS_FULLSCREEN_BUTTON, - SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER, RESIZER }; - static const int FIRST_INTERNAL_PSEUDOID = FILE_UPLOAD_BUTTON; - protected: // !START SYNC!: Keep this in sync with the copy constructor in RenderStyle.cpp @@ -335,6 +324,7 @@ public: return true; return background->m_background.hasImage(); } + bool hasBackgroundImage() const { return background->m_background.hasImage(); } bool hasFixedBackgroundImage() const { return background->m_background.hasFixedImage(); } bool hasAppearance() const { return appearance() != NoControlPart; } @@ -356,6 +346,11 @@ public: Length top() const { return surround->offset.top(); } Length bottom() const { return surround->offset.bottom(); } + // Whether or not a positioned element requires normal flow x/y to be computed + // to determine its position. + bool hasStaticX() const { return (left().isAuto() && right().isAuto()) || left().isStatic() || right().isStatic(); } + bool hasStaticY() const { return (top().isAuto() && bottom().isAuto()) || top().isStatic(); } + EPosition position() const { return static_cast<EPosition>(noninherited_flags._position); } EFloat floating() const { return static_cast<EFloat>(noninherited_flags._floating); } @@ -446,6 +441,19 @@ public: TextDirection direction() const { return static_cast<TextDirection>(inherited_flags._direction); } Length lineHeight() const { return inherited->line_height; } + int computedLineHeight() const + { + Length lh = lineHeight(); + + // Negative value means the line height is not set. Use the font's built-in spacing. + if (lh.isNegative()) + return font().lineSpacing(); + + if (lh.isPercent()) + return lh.calcMinValue(fontSize()); + + return lh.value(); + } EWhiteSpace whiteSpace() const { return static_cast<EWhiteSpace>(inherited_flags._white_space); } static bool autoWrap(EWhiteSpace ws) @@ -628,8 +636,16 @@ public: const TransformOperations& transform() const { return rareNonInheritedData->m_transform->m_operations; } Length transformOriginX() const { return rareNonInheritedData->m_transform->m_x; } Length transformOriginY() const { return rareNonInheritedData->m_transform->m_y; } + float transformOriginZ() const { return rareNonInheritedData->m_transform->m_z; } bool hasTransform() const { return !rareNonInheritedData->m_transform->m_operations.operations().isEmpty(); } - void applyTransform(TransformationMatrix&, const IntSize& borderBoxSize, bool includeTransformOrigin = true) const; + + // Return true if any transform related property (currently transform, transformStyle3D or perspective) + // indicates that we are transforming + bool hasTransformRelatedProperty() const { return hasTransform() || preserves3D() || hasPerspective(); } + + enum ApplyTransformOrigin { IncludeTransformOrigin, ExcludeTransformOrigin }; + void applyTransform(TransformationMatrix&, const IntSize& borderBoxSize, ApplyTransformOrigin = IncludeTransformOrigin) const; + bool hasMask() const { return rareNonInheritedData->m_mask.hasImage() || rareNonInheritedData->m_maskBoxImage.hasImage(); } // End CSS3 Getters @@ -645,7 +661,21 @@ public: bool hasTransitions() const { return rareNonInheritedData->m_transitions && rareNonInheritedData->m_transitions->size() > 0; } // return the first found Animation (including 'all' transitions) - const Animation* transitionForProperty(int property); + const Animation* transitionForProperty(int property) const; + + ETransformStyle3D transformStyle3D() const { return rareNonInheritedData->m_transformStyle3D; } + bool preserves3D() const { return rareNonInheritedData->m_transformStyle3D == TransformStyle3DPreserve3D; } + + EBackfaceVisibility backfaceVisibility() const { return rareNonInheritedData->m_backfaceVisibility; } + float perspective() const { return rareNonInheritedData->m_perspective; } + bool hasPerspective() const { return rareNonInheritedData->m_perspective > 0; } + Length perspectiveOriginX() const { return rareNonInheritedData->m_perspectiveOriginX; } + Length perspectiveOriginY() const { return rareNonInheritedData->m_perspectiveOriginY; } + +#if USE(ACCELERATED_COMPOSITING) + // When set, this ensures that styles compare as different. Used during accelerated animations. + bool isRunningAcceleratedAnimation() const { return rareNonInheritedData->m_runningAcceleratedAnimation; } +#endif int lineClamp() const { return rareNonInheritedData->lineClamp; } bool textSizeAdjust() const { return rareInheritedData->textSizeAdjust; } @@ -926,6 +956,7 @@ public: void setTransform(const TransformOperations& ops) { SET_VAR(rareNonInheritedData.access()->m_transform, m_operations, ops); } void setTransformOriginX(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_x, l); } void setTransformOriginY(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_y, l); } + void setTransformOriginZ(float f) { SET_VAR(rareNonInheritedData.access()->m_transform, m_z, f); } // End CSS3 Setters // Apple-specific property setters @@ -946,6 +977,16 @@ public: void adjustAnimations(); void adjustTransitions(); + void setTransformStyle3D(ETransformStyle3D b) { SET_VAR(rareNonInheritedData, m_transformStyle3D, b); } + void setBackfaceVisibility(EBackfaceVisibility b) { SET_VAR(rareNonInheritedData, m_backfaceVisibility, b); } + void setPerspective(float p) { SET_VAR(rareNonInheritedData, m_perspective, p); } + void setPerspectiveOriginX(Length l) { SET_VAR(rareNonInheritedData, m_perspectiveOriginX, l); } + void setPerspectiveOriginY(Length l) { SET_VAR(rareNonInheritedData, m_perspectiveOriginY, l); } + +#if USE(ACCELERATED_COMPOSITING) + void setIsRunningAcceleratedAnimation(bool b = true) { SET_VAR(rareNonInheritedData, m_runningAcceleratedAnimation, b); } +#endif + void setLineClamp(int c) { SET_VAR(rareNonInheritedData, lineClamp, c); } void setTextSizeAdjust(bool b) { SET_VAR(rareInheritedData, textSizeAdjust, b); } void setTextSecurity(ETextSecurity aTextSecurity) { SET_VAR(rareInheritedData, textSecurity, aTextSecurity); } @@ -969,9 +1010,9 @@ public: #endif const ContentData* contentData() const { return rareNonInheritedData->m_content.get(); } - bool contentDataEquivalent(const RenderStyle* otherStyle) const; + bool contentDataEquivalent(const RenderStyle* otherStyle) const { return const_cast<RenderStyle*>(this)->rareNonInheritedData->contentDataEquivalent(*const_cast<RenderStyle*>(otherStyle)->rareNonInheritedData); } void clearContent(); - void setContent(StringImpl*, bool add = false); + void setContent(PassRefPtr<StringImpl>, bool add = false); void setContent(PassRefPtr<StyleImage>, bool add = false); void setContent(CounterContent*, bool add = false); @@ -980,13 +1021,7 @@ public: bool inheritedNotEqual(RenderStyle*) const; - // The difference between two styles. The following values are used: - // (1) Equal - The two styles are identical - // (2) Repaint - The object just needs to be repainted. - // (3) RepaintLayer - The layer and its descendant layers needs to be repainted. - // (4) Layout - A layout is required. - enum Diff { Equal, Repaint, RepaintLayer, LayoutPositionedMovementOnly, Layout }; - Diff diff(const RenderStyle*) const; + StyleDifference diff(const RenderStyle*, unsigned& changedContextSensitiveProperties) const; bool isDisplayReplacedType() const { @@ -1119,6 +1154,12 @@ public: static Length initialTransformOriginX() { return Length(50.0, Percent); } static Length initialTransformOriginY() { return Length(50.0, Percent); } static EPointerEvents initialPointerEvents() { return PE_AUTO; } + static float initialTransformOriginZ() { return 0; } + static ETransformStyle3D initialTransformStyle3D() { return TransformStyle3DFlat; } + static EBackfaceVisibility initialBackfaceVisibility() { return BackfaceVisibilityVisible; } + static float initialPerspective() { return 0; } + static Length initialPerspectiveOriginX() { return Length(50.0, Percent); } + static Length initialPerspectiveOriginY() { return Length(50.0, Percent); } // Keep these at the end. static int initialLineClamp() { return -1; } diff --git a/WebCore/rendering/style/RenderStyleConstants.h b/WebCore/rendering/style/RenderStyleConstants.h index 40ad3cc..405cf7c 100644 --- a/WebCore/rendering/style/RenderStyleConstants.h +++ b/WebCore/rendering/style/RenderStyleConstants.h @@ -36,6 +36,44 @@ namespace WebCore { * and produce invalid results. */ +// The difference between two styles. The following values are used: +// (1) StyleDifferenceEqual - The two styles are identical +// (2) StyleDifferenceRecompositeLayer - The layer needs its position and transform updated, but no repaint +// (3) StyleDifferenceRepaint - The object just needs to be repainted. +// (4) StyleDifferenceRepaintLayer - The layer and its descendant layers needs to be repainted. +// (5) StyleDifferenceLayout - A layout is required. +enum StyleDifference { + StyleDifferenceEqual, +#if USE(ACCELERATED_COMPOSITING) + StyleDifferenceRecompositeLayer, +#endif + StyleDifferenceRepaint, + StyleDifferenceRepaintLayer, + StyleDifferenceLayoutPositionedMovementOnly, + StyleDifferenceLayout +}; + +// When some style properties change, different amounts of work have to be done depending on +// context (e.g. whether the property is changing on an element which has a compositing layer). +// A simple StyleDifference does not provide enough information so we return a bit mask of +// StyleDifferenceContextSensitiveProperties from RenderStyle::diff() too. +enum StyleDifferenceContextSensitiveProperty { + ContextSensitivePropertyNone = 0, + ContextSensitivePropertyTransform = (1 << 0), + ContextSensitivePropertyOpacity = (1 << 1) +}; + +// Static pseudo styles. Dynamic ones are produced on the fly. +enum PseudoId { + NOPSEUDO, FIRST_LINE, FIRST_LETTER, BEFORE, AFTER, SELECTION, FIRST_LINE_INHERITED, SCROLLBAR, FILE_UPLOAD_BUTTON, INPUT_PLACEHOLDER, + SLIDER_THUMB, SEARCH_CANCEL_BUTTON, SEARCH_DECORATION, SEARCH_RESULTS_DECORATION, SEARCH_RESULTS_BUTTON, MEDIA_CONTROLS_PANEL, + MEDIA_CONTROLS_PLAY_BUTTON, MEDIA_CONTROLS_MUTE_BUTTON, MEDIA_CONTROLS_TIMELINE, MEDIA_CONTROLS_TIMELINE_CONTAINER, + MEDIA_CONTROLS_CURRENT_TIME_DISPLAY, MEDIA_CONTROLS_TIME_REMAINING_DISPLAY, MEDIA_CONTROLS_SEEK_BACK_BUTTON, + MEDIA_CONTROLS_SEEK_FORWARD_BUTTON, MEDIA_CONTROLS_FULLSCREEN_BUTTON, + SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER, RESIZER, + + FIRST_INTERNAL_PSEUDOID = FILE_UPLOAD_BUTTON +}; // These have been defined in the order of their precedence for border-collapsing. Do // not change this order! @@ -163,7 +201,7 @@ enum EListStyleType { HIRAGANA, KATAKANA, HIRAGANA_IROHA, KATAKANA_IROHA, LNONE }; -enum ContentType { +enum StyleContentType { CONTENT_NONE, CONTENT_OBJECT, CONTENT_TEXT, CONTENT_COUNTER }; @@ -171,11 +209,6 @@ enum EBorderFit { BorderFitBorder, BorderFitLines }; enum ETimingFunctionType { LinearTimingFunction, CubicBezierTimingFunction }; -enum EAnimPlayState { - AnimPlayStatePlaying = 0x0, - AnimPlayStatePaused = 0x1 -}; - enum EWhiteSpace { NORMAL, PRE, PRE_WRAP, PRE_LINE, NOWRAP, KHTML_NOWRAP }; @@ -263,6 +296,14 @@ enum EPointerEvents { PE_VISIBLE_STROKE, PE_VISIBLE_FILL, PE_VISIBLE_PAINTED, PE_ALL }; +enum ETransformStyle3D { + TransformStyle3DFlat, TransformStyle3DPreserve3D +}; + +enum EBackfaceVisibility { + BackfaceVisibilityVisible, BackfaceVisibilityHidden +}; + } // namespace WebCore #endif // RenderStyleConstants_h diff --git a/WebCore/rendering/style/SVGRenderStyle.cpp b/WebCore/rendering/style/SVGRenderStyle.cpp index 1749978..c5f0648 100644 --- a/WebCore/rendering/style/SVGRenderStyle.cpp +++ b/WebCore/rendering/style/SVGRenderStyle.cpp @@ -128,7 +128,7 @@ float SVGRenderStyle::cssPrimitiveToLength(const RenderObject* item, CSSValue* v return defaultValue; if (cssType == CSSPrimitiveValue::CSS_PERCENTAGE) { - SVGStyledElement* element = static_cast<SVGStyledElement*>(item->element()); + SVGStyledElement* element = static_cast<SVGStyledElement*>(item->node()); SVGElement* viewportElement = (element ? element->viewportElement() : 0); if (viewportElement) { float result = primitive->getFloatValue() / 100.0f; diff --git a/WebCore/rendering/style/StyleInheritedData.cpp b/WebCore/rendering/style/StyleInheritedData.cpp index 56d2686..f59c0c2 100644 --- a/WebCore/rendering/style/StyleInheritedData.cpp +++ b/WebCore/rendering/style/StyleInheritedData.cpp @@ -66,7 +66,7 @@ static bool cursorDataEquivalent(const CursorList* c1, const CursorList* c2) { if (c1 == c2) return true; - if (!c1 && c2 || c1 && !c2) + if ((!c1 && c2) || (c1 && !c2)) return false; return (*c1 == *c2); } diff --git a/WebCore/rendering/style/StyleRareInheritedData.cpp b/WebCore/rendering/style/StyleRareInheritedData.cpp index 2294568..f26baa6 100644 --- a/WebCore/rendering/style/StyleRareInheritedData.cpp +++ b/WebCore/rendering/style/StyleRareInheritedData.cpp @@ -94,7 +94,7 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const bool StyleRareInheritedData::shadowDataEquivalent(const StyleRareInheritedData& o) const { - if (!textShadow && o.textShadow || textShadow && !o.textShadow) + if ((!textShadow && o.textShadow) || (textShadow && !o.textShadow)) return false; if (textShadow && o.textShadow && (*textShadow != *o.textShadow)) return false; diff --git a/WebCore/rendering/style/StyleRareNonInheritedData.cpp b/WebCore/rendering/style/StyleRareNonInheritedData.cpp index e8ceeeb..401c04f 100644 --- a/WebCore/rendering/style/StyleRareNonInheritedData.cpp +++ b/WebCore/rendering/style/StyleRareNonInheritedData.cpp @@ -23,7 +23,10 @@ #include "StyleRareNonInheritedData.h" #include "CSSStyleSelector.h" +#include "ContentData.h" +#include "RenderCounter.h" #include "RenderStyle.h" +#include "StyleImage.h" namespace WebCore { @@ -39,10 +42,18 @@ StyleRareNonInheritedData::StyleRareNonInheritedData() , matchNearestMailBlockquoteColor(RenderStyle::initialMatchNearestMailBlockquoteColor()) , m_appearance(RenderStyle::initialAppearance()) , m_borderFit(RenderStyle::initialBorderFit()) +#if USE(ACCELERATED_COMPOSITING) + , m_runningAcceleratedAnimation(false) +#endif , m_boxShadow(0) , m_animations(0) , m_transitions(0) , m_mask(FillLayer(MaskFillLayer)) + , m_transformStyle3D(RenderStyle::initialTransformStyle3D()) + , m_backfaceVisibility(RenderStyle::initialBackfaceVisibility()) + , m_perspective(RenderStyle::initialPerspective()) + , m_perspectiveOriginX(RenderStyle::initialPerspectiveOriginX()) + , m_perspectiveOriginY(RenderStyle::initialPerspectiveOriginY()) #if ENABLE(XBL) , bindingURI(0) #endif @@ -66,12 +77,20 @@ StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonInherited , matchNearestMailBlockquoteColor(o.matchNearestMailBlockquoteColor) , m_appearance(o.m_appearance) , m_borderFit(o.m_borderFit) +#if USE(ACCELERATED_COMPOSITING) + , m_runningAcceleratedAnimation(o.m_runningAcceleratedAnimation) +#endif , m_boxShadow(o.m_boxShadow ? new ShadowData(*o.m_boxShadow) : 0) , m_boxReflect(o.m_boxReflect) , m_animations(o.m_animations ? new AnimationList(*o.m_animations) : 0) , m_transitions(o.m_transitions ? new AnimationList(*o.m_transitions) : 0) , m_mask(o.m_mask) , m_maskBoxImage(o.m_maskBoxImage) + , m_transformStyle3D(o.m_transformStyle3D) + , m_backfaceVisibility(o.m_backfaceVisibility) + , m_perspective(o.m_perspective) + , m_perspectiveOriginX(o.m_perspectiveOriginX) + , m_perspectiveOriginY(o.m_perspectiveOriginY) #if ENABLE(XBL) , bindingURI(o.bindingURI ? o.bindingURI->copy() : 0) #endif @@ -105,7 +124,7 @@ bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) c && marquee == o.marquee && m_multiCol == o.m_multiCol && m_transform == o.m_transform - && m_content == o.m_content + && contentDataEquivalent(o) && m_counterDirectives == o.m_counterDirectives && userDrag == o.userDrag && textOverflow == o.textOverflow @@ -114,6 +133,9 @@ bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) c && matchNearestMailBlockquoteColor == o.matchNearestMailBlockquoteColor && m_appearance == o.m_appearance && m_borderFit == o.m_borderFit +#if USE(ACCELERATED_COMPOSITING) + && !m_runningAcceleratedAnimation && !o.m_runningAcceleratedAnimation +#endif && shadowDataEquivalent(o) && reflectionDataEquivalent(o) && animationDataEquivalent(o) @@ -123,12 +145,32 @@ bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) c #if ENABLE(XBL) && bindingsEquivalent(o) #endif + && (m_transformStyle3D == o.m_transformStyle3D) + && (m_backfaceVisibility == o.m_backfaceVisibility) + && (m_perspective == o.m_perspective) + && (m_perspectiveOriginX == o.m_perspectiveOriginX) + && (m_perspectiveOriginY == o.m_perspectiveOriginY) ; } +bool StyleRareNonInheritedData::contentDataEquivalent(const StyleRareNonInheritedData& o) const +{ + ContentData* c1 = m_content.get(); + ContentData* c2 = o.m_content.get(); + + while (c1 && c2) { + if (!c1->dataEquivalent(*c2)) + return false; + c1 = c1->next(); + c2 = c2->next(); + } + + return !c1 && !c2; +} + bool StyleRareNonInheritedData::shadowDataEquivalent(const StyleRareNonInheritedData& o) const { - if (!m_boxShadow && o.m_boxShadow || m_boxShadow && !o.m_boxShadow) + if ((!m_boxShadow && o.m_boxShadow) || (m_boxShadow && !o.m_boxShadow)) return false; if (m_boxShadow && o.m_boxShadow && (*m_boxShadow != *o.m_boxShadow)) return false; @@ -148,7 +190,7 @@ bool StyleRareNonInheritedData::reflectionDataEquivalent(const StyleRareNonInher bool StyleRareNonInheritedData::animationDataEquivalent(const StyleRareNonInheritedData& o) const { - if (!m_animations && o.m_animations || m_animations && !o.m_animations) + if ((!m_animations && o.m_animations) || (m_animations && !o.m_animations)) return false; if (m_animations && o.m_animations && (*m_animations != *o.m_animations)) return false; @@ -157,7 +199,7 @@ bool StyleRareNonInheritedData::animationDataEquivalent(const StyleRareNonInheri bool StyleRareNonInheritedData::transitionDataEquivalent(const StyleRareNonInheritedData& o) const { - if (!m_transitions && o.m_transitions || m_transitions && !o.m_transitions) + if ((!m_transitions && o.m_transitions) || (m_transitions && !o.m_transitions)) return false; if (m_transitions && o.m_transitions && (*m_transitions != *o.m_transitions)) return false; diff --git a/WebCore/rendering/style/StyleRareNonInheritedData.h b/WebCore/rendering/style/StyleRareNonInheritedData.h index 6ce6a33..8dd22b3 100644 --- a/WebCore/rendering/style/StyleRareNonInheritedData.h +++ b/WebCore/rendering/style/StyleRareNonInheritedData.h @@ -30,6 +30,7 @@ #include "DataRef.h" #include "FillLayer.h" #include "NinePieceImage.h" +#include "StyleTransformData.h" #include <wtf/OwnPtr.h> #include <wtf/PassRefPtr.h> #include <wtf/Vector.h> @@ -69,7 +70,8 @@ public: bool operator==(const StyleRareNonInheritedData&) const; bool operator!=(const StyleRareNonInheritedData& o) const { return !(*this == o); } - + + bool contentDataEquivalent(const StyleRareNonInheritedData& o) const; bool shadowDataEquivalent(const StyleRareNonInheritedData& o) const; bool reflectionDataEquivalent(const StyleRareNonInheritedData& o) const; bool animationDataEquivalent(const StyleRareNonInheritedData&) const; @@ -96,6 +98,9 @@ public: unsigned matchNearestMailBlockquoteColor : 1; // EMatchNearestMailBlockquoteColor, FIXME: This property needs to be eliminated. It should never have been added. unsigned m_appearance : 6; // EAppearance unsigned m_borderFit : 1; // EBorderFit +#if USE(ACCELERATED_COMPOSITING) + bool m_runningAcceleratedAnimation : 1; +#endif OwnPtr<ShadowData> m_boxShadow; // For box-shadow decorations. RefPtr<StyleReflection> m_boxReflect; @@ -106,6 +111,12 @@ public: FillLayer m_mask; NinePieceImage m_maskBoxImage; + ETransformStyle3D m_transformStyle3D; + EBackfaceVisibility m_backfaceVisibility; + float m_perspective; + Length m_perspectiveOriginX; + Length m_perspectiveOriginY; + #if ENABLE(XBL) OwnPtr<BindingURI> bindingURI; // The XBL binding URI list. #endif diff --git a/WebCore/rendering/style/StyleTransformData.cpp b/WebCore/rendering/style/StyleTransformData.cpp index de20e77..2baebf9 100644 --- a/WebCore/rendering/style/StyleTransformData.cpp +++ b/WebCore/rendering/style/StyleTransformData.cpp @@ -30,6 +30,7 @@ StyleTransformData::StyleTransformData() : m_operations(RenderStyle::initialTransform()) , m_x(RenderStyle::initialTransformOriginX()) , m_y(RenderStyle::initialTransformOriginY()) + , m_z(RenderStyle::initialTransformOriginZ()) { } @@ -38,12 +39,13 @@ StyleTransformData::StyleTransformData(const StyleTransformData& o) , m_operations(o.m_operations) , m_x(o.m_x) , m_y(o.m_y) + , m_z(o.m_z) { } bool StyleTransformData::operator==(const StyleTransformData& o) const { - return m_x == o.m_x && m_y == o.m_y && m_operations == o.m_operations; + return m_x == o.m_x && m_y == o.m_y && m_z == o.m_z && m_operations == o.m_operations; } } // namespace WebCore diff --git a/WebCore/rendering/style/StyleTransformData.h b/WebCore/rendering/style/StyleTransformData.h index 157e600..6039824 100644 --- a/WebCore/rendering/style/StyleTransformData.h +++ b/WebCore/rendering/style/StyleTransformData.h @@ -46,6 +46,7 @@ public: TransformOperations m_operations; Length m_x; Length m_y; + float m_z; private: StyleTransformData(); |