diff options
Diffstat (limited to 'WebCore/rendering')
210 files changed, 11864 insertions, 5711 deletions
diff --git a/WebCore/rendering/AutoTableLayout.cpp b/WebCore/rendering/AutoTableLayout.cpp index c37fba4..2a4958a 100644 --- a/WebCore/rendering/AutoTableLayout.cpp +++ b/WebCore/rendering/AutoTableLayout.cpp @@ -382,7 +382,7 @@ int AutoTableLayout::calcEffectiveWidth() float spanMax = max(maxWidth, cMaxWidth); tMaxWidth = max(tMaxWidth, spanMax * 100 * percentScaleFactor / w.rawValue()); - // all non percent columns in the span get percent vlaues to sum up correctly. + // all non percent columns in the span get percent values to sum up correctly. int percentMissing = w.rawValue() - totalPercent; float totalWidth = 0; for (unsigned int pos = col; pos < lastCol; pos++) { @@ -495,10 +495,10 @@ void AutoTableLayout::insertSpanCell(RenderTableCell *cell) void AutoTableLayout::layout() { -#ifdef ANDROID_LAYOUT +#ifdef ANDROID_LAYOUT if (m_table->isSingleColumn()) return; -#endif +#endif // table layout based on the values collected in the layout structure. int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing(); int available = tableWidth; @@ -668,8 +668,8 @@ void AutoTableLayout::layout() } } - // if we have overallocated, reduce every cell according to the difference between desired width and minwidth - // this seems to produce to the pixel exaxt results with IE. Wonder is some of this also holds for width distributing. + // If we have overallocated, reduce every cell according to the difference between desired width and minwidth + // this seems to produce to the pixel exact results with IE. Wonder is some of this also holds for width distributing. if (available < 0) { // Need to reduce cells with the following prioritization: // (1) Auto diff --git a/WebCore/rendering/AutoTableLayout.h b/WebCore/rendering/AutoTableLayout.h index 641a68b..f1ef768 100644 --- a/WebCore/rendering/AutoTableLayout.h +++ b/WebCore/rendering/AutoTableLayout.h @@ -1,6 +1,4 @@ /* - * This file is part of the HTML rendering engine for KDE. - * * Copyright (C) 2002 Lars Knoll (knoll@kde.org) * (C) 2002 Dirk Mueller (mueller@kde.org) * diff --git a/WebCore/rendering/BidiRun.cpp b/WebCore/rendering/BidiRun.cpp new file mode 100644 index 0000000..ac13046 --- /dev/null +++ b/WebCore/rendering/BidiRun.cpp @@ -0,0 +1,74 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * This library is free software; you can redistribute it and/or + * 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 "BidiRun.h" +#include "InlineBox.h" +#include "RenderArena.h" +#include <wtf/RefCountedLeakCounter.h> + +using namespace WTF; + +namespace WebCore { + +#ifndef NDEBUG +static RefCountedLeakCounter bidiRunCounter("BidiRun"); + +static bool inBidiRunDestroy; +#endif + +void BidiRun::destroy() +{ +#ifndef NDEBUG + inBidiRunDestroy = true; +#endif + RenderArena* renderArena = m_object->renderArena(); + delete this; +#ifndef NDEBUG + inBidiRunDestroy = false; +#endif + + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*reinterpret_cast<size_t*>(this), this); +} + +void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw() +{ +#ifndef NDEBUG + bidiRunCounter.increment(); +#endif + return renderArena->allocate(sz); +} + +void BidiRun::operator delete(void* ptr, size_t sz) +{ +#ifndef NDEBUG + bidiRunCounter.decrement(); +#endif + ASSERT(inBidiRunDestroy); + + // Stash size where destroy() can find it. + *(size_t*)ptr = sz; +} + +} diff --git a/WebCore/rendering/BidiRun.h b/WebCore/rendering/BidiRun.h new file mode 100644 index 0000000..542081a --- /dev/null +++ b/WebCore/rendering/BidiRun.h @@ -0,0 +1,65 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * This library is free software; you can redistribute it and/or + * 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 BidiRun_h +#define BidiRun_h + +#include <wtf/StdLibExtras.h> +#include "BidiResolver.h" +#include "RenderText.h" + +namespace WebCore { + +class BidiContext; +class InlineBox; + +struct BidiRun : BidiCharacterRun { + BidiRun(int start, int stop, RenderObject* object, BidiContext* context, WTF::Unicode::Direction dir) + : BidiCharacterRun(start, stop, context, dir) + , m_object(object) + , m_box(0) + { + } + + void destroy(); + + // Overloaded new operator. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + + BidiRun* next() { return static_cast<BidiRun*>(m_next); } + +private: + // The normal operator new is disallowed. + void* operator new(size_t) throw(); + +public: + RenderObject* m_object; + InlineBox* m_box; +}; + +} + +#endif // BidiRun_h diff --git a/WebCore/rendering/CounterNode.cpp b/WebCore/rendering/CounterNode.cpp index c30ca9a..c164c81 100644 --- a/WebCore/rendering/CounterNode.cpp +++ b/WebCore/rendering/CounterNode.cpp @@ -1,6 +1,4 @@ /* - * This file is part of the HTML rendering engine for KDE. - * * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * @@ -24,20 +22,14 @@ #include "config.h" #include "CounterNode.h" +#include "RenderCounter.h" #include "RenderObject.h" #include <stdio.h> -// FIXME: There's currently no strategy for getting the counter tree updated when new -// elements with counter-reset and counter-increment styles are added to the render tree. -// Also, the code can't handle changes where an existing node needs to change into a -// "reset" node, or from a "reset" node back to not a "reset" node. As of this writing, -// at least some of these problems manifest as failures in the t1204-increment and -// t1204-reset tests in the CSS 2.1 test suite. - namespace WebCore { -CounterNode::CounterNode(RenderObject* o, bool isReset, int value) - : m_isReset(isReset) +CounterNode::CounterNode(RenderObject* o, bool hasResetType, int value) + : m_hasResetType(hasResetType) , m_value(value) , m_countInParent(0) , m_renderer(o) @@ -46,32 +38,95 @@ CounterNode::CounterNode(RenderObject* o, bool isReset, int value) , m_nextSibling(0) , m_firstChild(0) , m_lastChild(0) -{ +{ +} + +CounterNode* CounterNode::nextInPreOrderAfterChildren(const CounterNode* stayWithin) const +{ + if (this == stayWithin) + return 0; + + const CounterNode* current = this; + CounterNode* next; + while (!(next = current->m_nextSibling)) { + current = current->m_parent; + if (!current || current == stayWithin) + return 0; + } + return next; +} + +CounterNode* CounterNode::nextInPreOrder(const CounterNode* stayWithin) const +{ + if (CounterNode* next = m_firstChild) + return next; + + return nextInPreOrderAfterChildren(stayWithin); +} + +CounterNode* CounterNode::lastDescendant() const +{ + CounterNode* last = m_lastChild; + if (!last) + return 0; + + while (CounterNode* lastChild = last->m_lastChild) + last = lastChild; + + return last; +} + +CounterNode* CounterNode::previousInPreOrder() const +{ + CounterNode* previous = m_previousSibling; + if (!previous) + return m_parent; + + while (CounterNode* lastChild = previous->m_lastChild) + previous = lastChild; + + return previous; } int CounterNode::computeCountInParent() const { - int increment = m_isReset ? 0 : m_value; + int increment = actsAsReset() ? 0 : m_value; if (m_previousSibling) return m_previousSibling->m_countInParent + increment; ASSERT(m_parent->m_firstChild == this); return m_parent->m_value + increment; } -void CounterNode::recount() +void CounterNode::resetRenderer(const AtomicString& identifier) const { - for (CounterNode* c = this; c; c = c->m_nextSibling) { - int oldCount = c->m_countInParent; - int newCount = c->computeCountInParent(); - c->m_countInParent = newCount; + if (!m_renderer || m_renderer->documentBeingDestroyed()) + return; + if (RenderObjectChildList* children = m_renderer->virtualChildren()) + children->invalidateCounters(m_renderer, identifier); +} + +void CounterNode::resetRenderers(const AtomicString& identifier) const +{ + const CounterNode* node = this; + do { + node->resetRenderer(identifier); + node = node->nextInPreOrder(this); + } while (node); +} + +void CounterNode::recount(const AtomicString& identifier) +{ + for (CounterNode* node = this; node; node = node->m_nextSibling) { + int oldCount = node->m_countInParent; + int newCount = node->computeCountInParent(); if (oldCount == newCount) break; - if (c->m_renderer->isCounter()) - c->m_renderer->setNeedsLayoutAndPrefWidthsRecalc(); + node->m_countInParent = newCount; + node->resetRenderers(identifier); } } -void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild) +void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild, const AtomicString& identifier) { ASSERT(newChild); ASSERT(!newChild->m_parent); @@ -79,6 +134,11 @@ void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild) ASSERT(!newChild->m_nextSibling); ASSERT(!refChild || refChild->m_parent == this); + if (newChild->m_hasResetType) { + while (m_lastChild != refChild) + RenderCounter::destroyCounterNode(m_lastChild->renderer(), identifier); + } + CounterNode* next; if (refChild) { @@ -89,91 +149,106 @@ void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild) m_firstChild = newChild; } - if (next) { - ASSERT(next->m_previousSibling == refChild); - next->m_previousSibling = newChild; - } else { - ASSERT(m_lastChild == refChild); - m_lastChild = newChild; - } - newChild->m_parent = this; newChild->m_previousSibling = refChild; - newChild->m_nextSibling = next; - newChild->m_countInParent = newChild->computeCountInParent(); + if (!newChild->m_firstChild || newChild->m_hasResetType) { + newChild->m_nextSibling = next; + if (next) { + ASSERT(next->m_previousSibling == refChild); + next->m_previousSibling = newChild; + } else { + ASSERT(m_lastChild == refChild); + m_lastChild = newChild; + } + + newChild->m_countInParent = newChild->computeCountInParent(); + newChild->resetRenderers(identifier); + if (next) + next->recount(identifier); + return; + } + + // The code below handles the case when a formerly root increment counter is loosing its root position + // and therefore its children become next siblings. + CounterNode* last = newChild->m_lastChild; + CounterNode* first = newChild->m_firstChild; + + newChild->m_nextSibling = first; + first->m_previousSibling = newChild; + // The case when the original next sibling of the inserted node becomes a child of + // one of the former children of the inserted node is not handled as it is believed + // to be impossible since: + // 1. if the increment counter node lost it's root position as a result of another + // counter node being created, it will be inserted as the last child so next is null. + // 2. if the increment counter node lost it's root position as a result of a renderer being + // inserted into the document's render tree, all its former children counters are attached + // to children of the inserted renderer and hence cannot be in scope for counter nodes + // attached to renderers that were already in the document's render tree. + last->m_nextSibling = next; if (next) - next->recount(); + next->m_previousSibling = last; + else + m_lastChild = last; + for (next = first; ; next = next->m_nextSibling) { + next->m_parent = this; + if (last == next) + break; + } + newChild->m_firstChild = 0; + newChild->m_lastChild = 0; + newChild->m_countInParent = newChild->computeCountInParent(); + newChild->resetRenderer(identifier); + first->recount(identifier); } -void CounterNode::removeChild(CounterNode* oldChild) +void CounterNode::removeChild(CounterNode* oldChild, const AtomicString& identifier) { ASSERT(oldChild); ASSERT(!oldChild->m_firstChild); ASSERT(!oldChild->m_lastChild); CounterNode* next = oldChild->m_nextSibling; - CounterNode* prev = oldChild->m_previousSibling; + CounterNode* previous = oldChild->m_previousSibling; oldChild->m_nextSibling = 0; oldChild->m_previousSibling = 0; oldChild->m_parent = 0; - if (prev) - prev->m_nextSibling = next; + if (previous) + previous->m_nextSibling = next; else { ASSERT(m_firstChild == oldChild); m_firstChild = next; } - + if (next) - next->m_previousSibling = prev; + next->m_previousSibling = previous; else { ASSERT(m_lastChild == oldChild); - m_lastChild = prev; + m_lastChild = previous; } - + if (next) - next->recount(); + next->recount(identifier); } #ifndef NDEBUG -static const CounterNode* nextInPreOrderAfterChildren(const CounterNode* node) -{ - CounterNode* next = node->nextSibling(); - if (!next) { - next = node->parent(); - while (next && !next->nextSibling()) - next = next->parent(); - if (next) - next = next->nextSibling(); - } - return next; -} - -static const CounterNode* nextInPreOrder(const CounterNode* node) -{ - if (CounterNode* child = node->firstChild()) - return child; - return nextInPreOrderAfterChildren(node); -} - static void showTreeAndMark(const CounterNode* node) { const CounterNode* root = node; while (root->parent()) root = root->parent(); - for (const CounterNode* c = root; c; c = nextInPreOrder(c)) { - if (c == node) - fprintf(stderr, "*"); - for (const CounterNode* d = c; d && d != root; d = d->parent()) - fprintf(stderr, "\t"); - if (c->isReset()) - fprintf(stderr, "reset: %d\n", c->value()); - else - fprintf(stderr, "increment: %d\n", c->value()); + for (const CounterNode* current = root; current; current = current->nextInPreOrder()) { + fwrite((current == node) ? "*" : " ", 1, 1, stderr); + for (const CounterNode* parent = current; parent && parent != root; parent = parent->parent()) + fwrite(" ", 1, 2, stderr); + fprintf(stderr, "%p %s: %d %d P:%p PS:%p NS:%p R:%p\n", + current, current->actsAsReset() ? "reset____" : "increment", current->value(), + current->countInParent(), current->parent(), current->previousSibling(), + current->nextSibling(), current->renderer()); } } diff --git a/WebCore/rendering/CounterNode.h b/WebCore/rendering/CounterNode.h index b432e1d..15f2eb8 100644 --- a/WebCore/rendering/CounterNode.h +++ b/WebCore/rendering/CounterNode.h @@ -35,13 +35,15 @@ namespace WebCore { +class AtomicString; class RenderObject; class CounterNode : public Noncopyable { public: CounterNode(RenderObject*, bool isReset, int value); - bool isReset() const { return m_isReset; } + bool actsAsReset() const { return m_hasResetType || !m_parent; } + bool hasResetType() const { return m_hasResetType; } int value() const { return m_value; } int countInParent() const { return m_countInParent; } RenderObject* renderer() const { return m_renderer; } @@ -51,15 +53,30 @@ public: CounterNode* nextSibling() const { return m_nextSibling; } CounterNode* firstChild() const { return m_firstChild; } CounterNode* lastChild() const { return m_lastChild; } + CounterNode* lastDescendant() const; + CounterNode* previousInPreOrder() const; + CounterNode* nextInPreOrder(const CounterNode* stayWithin = 0) const; + CounterNode* nextInPreOrderAfterChildren(const CounterNode* stayWithin = 0) const; - void insertAfter(CounterNode* newChild, CounterNode* beforeChild); - void removeChild(CounterNode*); + void insertAfter(CounterNode* newChild, CounterNode* beforeChild, const AtomicString& identifier); + + // identifier must match the identifier of this counter. + void removeChild(CounterNode*, const AtomicString& identifier); private: int computeCountInParent() const; - void recount(); + void recount(const AtomicString& identifier); + + // Invalidates the text in the renderer of this counter, if any. + // identifier must match the identifier of this counter. + void resetRenderer(const AtomicString& identifier) const; + + // Invalidates the text in the renderer of this counter, if any, + // and in the renderers of all descendants of this counter, if any. + // identifier must match the identifier of this counter. + void resetRenderers(const AtomicString& identifier) const; - bool m_isReset; + bool m_hasResetType; int m_value; int m_countInParent; RenderObject* m_renderer; diff --git a/WebCore/rendering/EllipsisBox.cpp b/WebCore/rendering/EllipsisBox.cpp index db9a101..6ec3195 100644 --- a/WebCore/rendering/EllipsisBox.cpp +++ b/WebCore/rendering/EllipsisBox.cpp @@ -1,6 +1,4 @@ /** -* This file is part of the html renderer for KDE. - * * Copyright (C) 2003, 2006 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or @@ -25,6 +23,7 @@ #include "Document.h" #include "GraphicsContext.h" #include "HitTestResult.h" +#include "RootInlineBox.h" namespace WebCore { @@ -34,17 +33,30 @@ void EllipsisBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) RenderStyle* style = m_renderer->style(m_firstLine); Color textColor = style->color(); if (textColor != context->fillColor()) - context->setFillColor(textColor); + context->setFillColor(textColor, style->colorSpace()); bool setShadow = false; if (style->textShadow()) { context->setShadow(IntSize(style->textShadow()->x, style->textShadow()->y), - style->textShadow()->blur, style->textShadow()->color); + style->textShadow()->blur, style->textShadow()->color, style->colorSpace()); setShadow = true; } + if (selectionState() != RenderObject::SelectionNone) { + paintSelection(context, tx, ty, style, style->font()); + + // Select the correct color for painting the text. + Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor(); + if (foreground.isValid() && foreground != textColor) + context->setFillColor(foreground, style->colorSpace()); + } + const String& str = m_str; context->drawText(style->font(), TextRun(str.characters(), str.length(), false, 0, 0, false, style->visuallyOrdered()), IntPoint(m_x + tx, m_y + ty + style->font().ascent())); + // Restore the regular fill color. + if (textColor != context->fillColor()) + context->setFillColor(textColor, style->colorSpace()); + if (setShadow) context->clearShadow(); @@ -56,6 +68,35 @@ void EllipsisBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) } } +IntRect EllipsisBox::selectionRect(int tx, int ty) +{ + RenderStyle* style = m_renderer->style(m_firstLine); + const Font& f = style->font(); + return enclosingIntRect(f.selectionRectForText(TextRun(m_str.characters(), m_str.length(), false, 0, 0, false, style->visuallyOrdered()), + IntPoint(m_x + tx, m_y + ty + root()->selectionTop()), root()->selectionHeight())); +} + +void EllipsisBox::paintSelection(GraphicsContext* context, int tx, int ty, RenderStyle* style, const Font& font) +{ + Color textColor = style->color(); + Color c = m_renderer->selectionBackgroundColor(); + if (!c.isValid() || !c.alpha()) + return; + + // If the text color ends up being the same as the selection background, invert the selection + // background. + if (textColor == c) + c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); + + context->save(); + int y = root()->selectionTop(); + int h = root()->selectionHeight(); + context->clip(IntRect(m_x + tx, y + ty, m_width, h)); + context->drawHighlightForText(font, TextRun(m_str.characters(), m_str.length(), false, 0, 0, false, style->visuallyOrdered()), + IntPoint(m_x + tx, m_y + ty + y), h, c, style->colorSpace()); + context->restore(); +} + bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) { tx += m_x; diff --git a/WebCore/rendering/EllipsisBox.h b/WebCore/rendering/EllipsisBox.h index 9dbd27f..087fc72 100644 --- a/WebCore/rendering/EllipsisBox.h +++ b/WebCore/rendering/EllipsisBox.h @@ -1,6 +1,4 @@ /** -* This file is part of the html renderer for KDE. - * * Copyright (C) 2003, 2006 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or @@ -37,18 +35,24 @@ public: , m_height(height) , m_str(ellipsisStr) , m_markupBox(markupBox) + , m_selectionState(RenderObject::SelectionNone) { } virtual void paint(RenderObject::PaintInfo&, int tx, int ty); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + void setSelectionState(RenderObject::SelectionState s) { m_selectionState = s; } + IntRect selectionRect(int tx, int ty); private: virtual int height() const { return m_height; } + virtual RenderObject::SelectionState selectionState() { return m_selectionState; } + void paintSelection(GraphicsContext*, int tx, int ty, RenderStyle*, const Font&); int m_height; AtomicString m_str; InlineBox* m_markupBox; + RenderObject::SelectionState m_selectionState; }; } // namespace WebCore diff --git a/WebCore/rendering/FixedTableLayout.cpp b/WebCore/rendering/FixedTableLayout.cpp index ee3e75a..09af518 100644 --- a/WebCore/rendering/FixedTableLayout.cpp +++ b/WebCore/rendering/FixedTableLayout.cpp @@ -1,6 +1,4 @@ /* - * This file is part of the HTML rendering engine for KDE. - * * Copyright (C) 2002 Lars Knoll (knoll@kde.org) * (C) 2002 Dirk Mueller (mueller@kde.org) * Copyright (C) 2003, 2006 Apple Computer, Inc. @@ -188,6 +186,11 @@ int FixedTableLayout::calcWidthArray(int) return usedWidth; } +// Use a very large value (in effect infinite). But not too large! +// numeric_limits<int>::max() will too easily overflow widths. +// Keep this in synch with BLOCK_MAX_WIDTH in RenderBlock.cpp +#define TABLE_MAX_WIDTH 15000 + void FixedTableLayout::calcPrefWidths(int& minWidth, int& maxWidth) { // FIXME: This entire calculation is incorrect for both minwidth and maxwidth. @@ -206,6 +209,24 @@ void FixedTableLayout::calcPrefWidths(int& minWidth, int& maxWidth) minWidth = max(mw, tableWidth); maxWidth = minWidth; + + // This quirk is very similar to one that exists in RenderBlock::calcBlockPrefWidths(). + // Here's the example for this one: + /* + <table style="width:100%; background-color:red"><tr><td> + <table style="background-color:blue"><tr><td> + <table style="width:100%; background-color:green; table-layout:fixed"><tr><td> + Content + </td></tr></table> + </td></tr></table> + </td></tr></table> + */ + // In this example, the two inner tables should be as large as the outer table. + // We can achieve this effect by making the maxwidth of fixed tables with percentage + // widths be infinite. + if (m_table->style()->htmlHacks() && m_table->style()->width().isPercent() + && maxWidth < TABLE_MAX_WIDTH) + maxWidth = TABLE_MAX_WIDTH; } void FixedTableLayout::layout() diff --git a/WebCore/rendering/FixedTableLayout.h b/WebCore/rendering/FixedTableLayout.h index ed7c089..758ddbb 100644 --- a/WebCore/rendering/FixedTableLayout.h +++ b/WebCore/rendering/FixedTableLayout.h @@ -1,6 +1,4 @@ /* - * This file is part of the HTML rendering engine for KDE. - * * Copyright (C) 2002 Lars Knoll (knoll@kde.org) * (C) 2002 Dirk Mueller (mueller@kde.org) * diff --git a/WebCore/rendering/HitTestRequest.h b/WebCore/rendering/HitTestRequest.h index 46dd7b8..ca1445a 100644 --- a/WebCore/rendering/HitTestRequest.h +++ b/WebCore/rendering/HitTestRequest.h @@ -1,6 +1,4 @@ /* - * 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/ * diff --git a/WebCore/rendering/HitTestResult.cpp b/WebCore/rendering/HitTestResult.cpp index b7de46b..50933c6 100644 --- a/WebCore/rendering/HitTestResult.cpp +++ b/WebCore/rendering/HitTestResult.cpp @@ -123,17 +123,6 @@ Frame* HitTestResult::targetFrame() const return frame->tree()->find(m_innerURLElement->target()); } -IntRect HitTestResult::boundingBox() const -{ - if (m_innerNonSharedNode) { - RenderObject* renderer = m_innerNonSharedNode->renderer(); - if (renderer) - return renderer->absoluteBoundingBoxRect(); - } - - return IntRect(); -} - bool HitTestResult::isSelected() const { if (!m_innerNonSharedNode) @@ -209,7 +198,7 @@ String HitTestResult::altDisplayString() const if (m_innerNonSharedNode->hasTagName(imgTag)) { HTMLImageElement* image = static_cast<HTMLImageElement*>(m_innerNonSharedNode.get()); - return displayString(image->alt(), m_innerNonSharedNode.get()); + return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get()); } if (m_innerNonSharedNode->hasTagName(inputTag)) { @@ -246,7 +235,7 @@ IntRect HitTestResult::imageRect() const { if (!image()) return IntRect(); - return m_innerNonSharedNode->renderBox()->absoluteContentBox(); + return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox(); } KURL HitTestResult::absoluteImageURL() const diff --git a/WebCore/rendering/HitTestResult.h b/WebCore/rendering/HitTestResult.h index f29ca41..d1906ba 100644 --- a/WebCore/rendering/HitTestResult.h +++ b/WebCore/rendering/HitTestResult.h @@ -1,6 +1,4 @@ /* - * This file is part of the HTML rendering engine for KDE. - * * Copyright (C) 2006 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or @@ -63,7 +61,6 @@ public: void setIsOverWidget(bool b) { m_isOverWidget = b; } Frame* targetFrame() const; - IntRect boundingBox() const; bool isSelected() const; String spellingToolTip(TextDirection&) const; String replacedString() const; diff --git a/WebCore/rendering/InlineBox.cpp b/WebCore/rendering/InlineBox.cpp index bbf11b3..2575fb7 100644 --- a/WebCore/rendering/InlineBox.cpp +++ b/WebCore/rendering/InlineBox.cpp @@ -98,18 +98,11 @@ int InlineBox::height() const return toRenderBox(m_renderer)->height(); ASSERT(isInlineFlowBox()); - const InlineFlowBox* flowBox = static_cast<const InlineFlowBox*>(this); RenderBoxModelObject* flowObject = boxModelObject(); const Font& font = renderer()->style(m_firstLine)->font(); int result = font.height(); - bool strictMode = renderer()->document()->inStrictMode(); if (parent()) result += flowObject->borderTop() + flowObject->paddingTop() + flowObject->borderBottom() + flowObject->paddingBottom(); - if (strictMode || flowBox->hasTextChildren() || flowObject->hasHorizontalBordersOrPadding()) - return result; - int bottom = root()->bottomOverflow(); - if (y() + result > bottom) - result = bottom - y(); return result; } diff --git a/WebCore/rendering/InlineBox.h b/WebCore/rendering/InlineBox.h index c03758d..e165f0c 100644 --- a/WebCore/rendering/InlineBox.h +++ b/WebCore/rendering/InlineBox.h @@ -208,11 +208,6 @@ public: inline int baselinePosition(bool isRootLineBox) const { return renderer()->baselinePosition(m_firstLine, isRootLineBox); } inline int lineHeight(bool isRootLineBox) const { return renderer()->lineHeight(m_firstLine, isRootLineBox); } - virtual int 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; virtual unsigned caretMaxRenderedOffset() const; diff --git a/WebCore/rendering/InlineFlowBox.cpp b/WebCore/rendering/InlineFlowBox.cpp index 543c190..34eec30 100644 --- a/WebCore/rendering/InlineFlowBox.cpp +++ b/WebCore/rendering/InlineFlowBox.cpp @@ -29,6 +29,7 @@ #include "RootInlineBox.h" #include "RenderBlock.h" #include "RenderInline.h" +#include "RenderLayer.h" #include "RenderListMarker.h" #include "RenderTableCell.h" #include "RootInlineBox.h" @@ -167,6 +168,8 @@ void InlineFlowBox::adjustPosition(int dx, int dy) InlineRunBox::adjustPosition(dx, dy); for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) child->adjustPosition(dx, dy); + if (m_overflow) + m_overflow->move(dx, dy); } RenderLineBoxList* InlineFlowBox::rendererLineBoxes() const @@ -248,16 +251,21 @@ void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* en } } -int InlineFlowBox::placeBoxesHorizontally(int xPos, int& leftPosition, int& rightPosition, bool& needsWordSpacing) +int InlineFlowBox::placeBoxesHorizontally(int xPos, bool& needsWordSpacing) { // Set our x position. setX(xPos); + int leftLayoutOverflow = xPos; + int rightLayoutOverflow = xPos; + int leftVisualOverflow = xPos; + int rightVisualOverflow = xPos; + int boxShadowLeft; int boxShadowRight; renderer()->style(m_firstLine)->getBoxShadowHorizontalExtent(boxShadowLeft, boxShadowRight); - leftPosition = min(xPos + boxShadowLeft, leftPosition); + leftVisualOverflow = min(xPos + boxShadowLeft, leftVisualOverflow); int startX = xPos; xPos += borderLeft() + paddingLeft(); @@ -275,23 +283,24 @@ int InlineFlowBox::placeBoxesHorizontally(int xPos, int& leftPosition, int& righ int strokeOverflow = static_cast<int>(ceilf(rt->style()->textStrokeWidth() / 2.0f)); - // If letter-spacing is negative, we should factor that into right overflow. (Even in RTL, letter-spacing is + // If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is // applied to the right, so this is not an issue with left overflow. int letterSpacing = min(0, (int)rt->style(m_firstLine)->font().letterSpacing()); - + rightLayoutOverflow = max(xPos + text->width() - letterSpacing, rightLayoutOverflow); + int leftGlyphOverflow = -strokeOverflow; int rightGlyphOverflow = strokeOverflow - letterSpacing; - int visualOverflowLeft = leftGlyphOverflow; - int visualOverflowRight = rightGlyphOverflow; + int childOverflowLeft = leftGlyphOverflow; + int childOverflowRight = rightGlyphOverflow; for (ShadowData* shadow = rt->style()->textShadow(); shadow; shadow = shadow->next) { - visualOverflowLeft = min(visualOverflowLeft, shadow->x - shadow->blur + leftGlyphOverflow); - visualOverflowRight = max(visualOverflowRight, shadow->x + shadow->blur + rightGlyphOverflow); + childOverflowLeft = min(childOverflowLeft, shadow->x - shadow->blur + leftGlyphOverflow); + childOverflowRight = max(childOverflowRight, shadow->x + shadow->blur + rightGlyphOverflow); } - leftPosition = min(xPos + visualOverflowLeft, leftPosition); - rightPosition = max(xPos + text->width() + visualOverflowRight, rightPosition); - m_maxHorizontalVisualOverflow = max(max(visualOverflowRight, -visualOverflowLeft), (int)m_maxHorizontalVisualOverflow); + leftVisualOverflow = min(xPos + childOverflowLeft, leftVisualOverflow); + rightVisualOverflow = max(xPos + text->width() + childOverflowRight, rightVisualOverflow); + xPos += text->width(); } else { if (curr->renderer()->isPositioned()) { @@ -307,13 +316,26 @@ int InlineFlowBox::placeBoxesHorizontally(int xPos, int& leftPosition, int& righ if (curr->renderer()->isRenderInline()) { InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr); xPos += flow->marginLeft(); - xPos = flow->placeBoxesHorizontally(xPos, leftPosition, rightPosition, needsWordSpacing); + xPos = flow->placeBoxesHorizontally(xPos, needsWordSpacing); xPos += flow->marginRight(); + leftLayoutOverflow = min(leftLayoutOverflow, flow->leftLayoutOverflow()); + rightLayoutOverflow = max(rightLayoutOverflow, flow->rightLayoutOverflow()); + leftVisualOverflow = min(leftVisualOverflow, flow->leftVisualOverflow()); + rightVisualOverflow = max(rightVisualOverflow, flow->rightVisualOverflow()); } else if (!curr->renderer()->isListMarker() || toRenderListMarker(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); + + RenderBox* box = toRenderBox(curr->renderer()); + int childLeftOverflow = box->hasOverflowClip() ? 0 : box->leftLayoutOverflow(); + int childRightOverflow = box->hasOverflowClip() ? curr->width() : box->rightLayoutOverflow(); + + leftLayoutOverflow = min(xPos + childLeftOverflow, leftLayoutOverflow); + rightLayoutOverflow = max(xPos + childRightOverflow, rightLayoutOverflow); + + leftVisualOverflow = min(xPos + box->leftVisualOverflow(), leftVisualOverflow); + rightVisualOverflow = max(xPos + box->rightVisualOverflow(), rightVisualOverflow); + xPos += curr->width() + curr->boxModelObject()->marginRight(); } } @@ -321,45 +343,13 @@ int InlineFlowBox::placeBoxesHorizontally(int xPos, int& leftPosition, int& righ xPos += borderRight() + paddingRight(); setWidth(xPos - startX); - rightPosition = max(x() + width() + boxShadowRight, rightPosition); + rightVisualOverflow = max(x() + width() + boxShadowRight, rightVisualOverflow); + rightLayoutOverflow = max(x() + width(), rightLayoutOverflow); + setHorizontalOverflowPositions(leftLayoutOverflow, rightLayoutOverflow, leftVisualOverflow, rightVisualOverflow); return xPos; } -int InlineFlowBox::verticallyAlignBoxes(int heightOfBlock) -{ - int maxPositionTop = 0; - int maxPositionBottom = 0; - int maxAscent = 0; - int maxDescent = 0; - - // 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 = renderer(); - while (curr && !curr->node()) - curr = curr->container(); - bool strictMode = (curr && curr->document()->inStrictMode()); - - computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode); - - if (maxAscent + maxDescent < max(maxPositionTop, maxPositionBottom)) - adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); - - int maxHeight = maxAscent + maxDescent; - int topPosition = heightOfBlock; - int bottomPosition = heightOfBlock; - int selectionTop = heightOfBlock; - int selectionBottom = heightOfBlock; - placeBoxesVertically(heightOfBlock, maxHeight, maxAscent, strictMode, topPosition, bottomPosition, selectionTop, selectionBottom); - - setVerticalOverflowPositions(topPosition, bottomPosition); - setVerticalSelectionPositions(selectionTop, selectionBottom); - - heightOfBlock += maxHeight; - - return heightOfBlock; -} - void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, int maxPositionTop, int maxPositionBottom) { @@ -479,11 +469,10 @@ void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositi } } -void InlineFlowBox::placeBoxesVertically(int yPos, int maxHeight, int maxAscent, bool strictMode, - int& topPosition, int& bottomPosition, int& selectionTop, int& selectionBottom) +void InlineFlowBox::placeBoxesVertically(int yPos, int maxHeight, int maxAscent, bool strictMode, int& selectionTop, int& selectionBottom) { if (isRootInlineBox()) - setY(yPos + max(0, maxAscent - baselinePosition(true))); // Place our root box. + setY(yPos + maxAscent - baselinePosition(true)); // Place our root box. for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { if (curr->renderer()->isPositioned()) @@ -493,7 +482,7 @@ void InlineFlowBox::placeBoxesVertically(int yPos, int maxHeight, int maxAscent, // line-height). bool isInlineFlow = curr->isInlineFlowBox(); if (isInlineFlow) - static_cast<InlineFlowBox*>(curr)->placeBoxesVertically(yPos, maxHeight, maxAscent, strictMode, topPosition, bottomPosition, selectionTop, selectionBottom); + static_cast<InlineFlowBox*>(curr)->placeBoxesVertically(yPos, maxHeight, maxAscent, strictMode, selectionTop, selectionBottom); bool childAffectsTopBottomPos = true; if (curr->y() == PositionTop) @@ -504,43 +493,18 @@ void InlineFlowBox::placeBoxesVertically(int yPos, int maxHeight, int maxAscent, if ((isInlineFlow && !static_cast<InlineFlowBox*>(curr)->hasTextChildren()) && !curr->boxModelObject()->hasHorizontalBordersOrPadding() && !strictMode) childAffectsTopBottomPos = false; int posAdjust = maxAscent - curr->baselinePosition(false); - if (!childAffectsTopBottomPos) - posAdjust = max(0, posAdjust); curr->setY(curr->y() + yPos + posAdjust); } - // FIXME: By only considering overflow as part of the root line box, we can't get an accurate picture regarding what the line - // actually needs to paint. A line box that is part of a self-painting layer technically shouldn't contribute to the overflow - // of the line, but in order to not do this and paint accurately, we have to track the overflow somewhere else (either by storing overflow - // in each InlineFlowBox up the chain or in the layer itself). Relative positioned objects on a line will cause scrollbars - // to appear when they shouldn't until we fix this issue. int newY = curr->y(); - int overflowTop = 0; - int overflowBottom = 0; if (curr->isText() || curr->isInlineFlowBox()) { const Font& font = curr->renderer()->style(m_firstLine)->font(); newY += curr->baselinePosition(false) - font.ascent(); - - curr->renderer()->style(m_firstLine)->getBoxShadowVerticalExtent(overflowTop, overflowBottom); - - 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->renderer()->hasReflection()) { - RenderBox* box = toRenderBox(curr->renderer()); - overflowTop = min(overflowTop, box->reflectionBox().y()); - overflowBottom = max(overflowBottom, box->reflectionBox().bottom()); - } - 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->setY(newY); @@ -549,8 +513,6 @@ void InlineFlowBox::placeBoxesVertically(int yPos, int maxHeight, int maxAscent, int boxHeight = curr->height(); selectionTop = min(selectionTop, newY); selectionBottom = max(selectionBottom, newY + boxHeight); - topPosition = min(topPosition, newY + overflowTop); - bottomPosition = max(bottomPosition, newY + boxHeight + overflowBottom); } } @@ -564,8 +526,85 @@ void InlineFlowBox::placeBoxesVertically(int yPos, int maxHeight, int maxAscent, } } +void InlineFlowBox::computeVerticalOverflow(int lineTop, int lineBottom, bool strictMode) +{ + int boxHeight = height(); + + // Any spillage outside of the line top and bottom is not considered overflow. We just ignore this, since it only happens + // from the "your ascent/descent don't affect the line" quirk. + int topOverflow = max(y(), lineTop); + int bottomOverflow = min(y() + boxHeight, lineBottom); + + int topLayoutOverflow = topOverflow; + int bottomLayoutOverflow = bottomOverflow; + + int topVisualOverflow = topOverflow; + int bottomVisualOverflow = bottomOverflow; + + // box-shadow on root line boxes is applying to the block and not to the lines. + if (parent()) { + int boxShadowTop; + int boxShadowBottom; + renderer()->style(m_firstLine)->getBoxShadowVerticalExtent(boxShadowTop, boxShadowBottom); + + topVisualOverflow = min(y() + boxShadowTop, topVisualOverflow); + bottomVisualOverflow = max(y() + boxHeight + boxShadowBottom, bottomVisualOverflow); + } + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (curr->renderer()->isText()) { + InlineTextBox* text = static_cast<InlineTextBox*>(curr); + RenderText* rt = toRenderText(text->renderer()); + if (rt->isBR()) + continue; + + int strokeOverflow = static_cast<int>(ceilf(rt->style()->textStrokeWidth() / 2.0f)); + + int topGlyphOverflow = -strokeOverflow; + int bottomGlyphOverflow = strokeOverflow; + + int childOverflowTop = topGlyphOverflow; + int childOverflowBottom = bottomGlyphOverflow; + for (ShadowData* shadow = rt->style()->textShadow(); shadow; shadow = shadow->next) { + childOverflowTop = min(childOverflowTop, shadow->y - shadow->blur + topGlyphOverflow); + childOverflowBottom = max(childOverflowBottom, shadow->y + shadow->blur + bottomGlyphOverflow); + } + + topVisualOverflow = min(curr->y() + childOverflowTop, topVisualOverflow); + bottomVisualOverflow = max(curr->y() + text->height() + childOverflowBottom, bottomVisualOverflow); + } else if (curr->renderer()->isRenderInline()) { + InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr); + flow->computeVerticalOverflow(lineTop, lineBottom, strictMode); + topLayoutOverflow = min(topLayoutOverflow, flow->topLayoutOverflow()); + bottomLayoutOverflow = max(bottomLayoutOverflow, flow->bottomLayoutOverflow()); + topVisualOverflow = min(topVisualOverflow, flow->topVisualOverflow()); + bottomVisualOverflow = max(bottomVisualOverflow, flow->bottomVisualOverflow()); + } else if (!curr->boxModelObject()->hasSelfPaintingLayer()){ + // Only include overflow from replaced inlines if they do not paint themselves. + RenderBox* box = toRenderBox(curr->renderer()); + int boxY = curr->y(); + int childTopOverflow = box->hasOverflowClip() ? 0 : box->topLayoutOverflow(); + int childBottomOverflow = box->hasOverflowClip() ? curr->height() : box->bottomLayoutOverflow(); + topLayoutOverflow = min(boxY + childTopOverflow, topLayoutOverflow); + bottomLayoutOverflow = max(boxY + childBottomOverflow, bottomLayoutOverflow); + topVisualOverflow = min(boxY + box->topVisualOverflow(), topVisualOverflow); + bottomVisualOverflow = max(boxY + box->bottomVisualOverflow(), bottomVisualOverflow); + } + } + + setVerticalOverflowPositions(topLayoutOverflow, bottomLayoutOverflow, topVisualOverflow, bottomVisualOverflow, boxHeight); +} + bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) { + IntRect overflowRect(visibleOverflowRect()); + overflowRect.move(tx, ty); + if (!overflowRect.contains(x, y)) + return false; + // Check children first. for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) { if ((curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) && curr->nodeAtPoint(request, result, x, y, tx, ty)) { @@ -586,23 +625,14 @@ bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re void InlineFlowBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) { - int xPos = tx + m_x - renderer()->maximalOutlineSize(paintInfo.phase); - int w = width() + 2 * renderer()->maximalOutlineSize(paintInfo.phase); - int shadowLeft; - int shadowRight; - - renderer()->style(m_firstLine)->getBoxShadowHorizontalExtent(shadowLeft, shadowRight); - - 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); - } - - xPos += shadowLeft; - w += -shadowLeft + shadowRight; - bool intersectsDamageRect = xPos < paintInfo.rect.right() && xPos + w > paintInfo.rect.x(); + IntRect overflowRect(visibleOverflowRect()); + overflowRect.inflate(renderer()->maximalOutlineSize(paintInfo.phase)); + overflowRect.move(tx, ty); + + if (!paintInfo.rect.intersects(overflowRect)) + return; - if (intersectsDamageRect && paintInfo.phase != PaintPhaseChildOutlines) { + if (paintInfo.phase != PaintPhaseChildOutlines) { if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) { // Add ourselves to the paint info struct's list of inlines that need to paint their // outlines. @@ -645,7 +675,7 @@ void InlineFlowBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) } // 4. Paint our strike-through - if (intersectsDamageRect && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) + if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection) paintTextDecorations(paintInfo, tx, ty, true); } @@ -702,13 +732,24 @@ void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) return; - // Move x/y to our coordinates. - tx += m_x; - ty += m_y; - + int x = m_x; + int y = m_y; int w = width(); int h = height(); + // Constrain our background/border painting to the line top and bottom if necessary. + bool strictMode = renderer()->document()->inStrictMode(); + if (!hasTextChildren() && !strictMode) { + RootInlineBox* rootBox = root(); + int bottom = min(rootBox->lineBottom(), y + h); + y = max(rootBox->lineTop(), y); + h = bottom - y; + } + + // Move x/y to our coordinates. + tx += x; + ty += y; + GraphicsContext* context = paintInfo.context; // You can use p::first-line to specify a background. If so, the root line boxes for @@ -767,25 +808,41 @@ void InlineFlowBox::paintMask(RenderObject::PaintInfo& paintInfo, int tx, int ty if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; - // Move x/y to our coordinates. - tx += m_x; - ty += m_y; - + int x = m_x; + int y = m_y; int w = width(); int h = height(); - // Figure out if we need to push a transparency layer to render our mask. - bool pushTransparencyLayer = false; + // Constrain our background/border painting to the line top and bottom if necessary. + bool strictMode = renderer()->document()->inStrictMode(); + if (!hasTextChildren() && !strictMode) { + RootInlineBox* rootBox = root(); + int bottom = min(rootBox->lineBottom(), y + h); + y = max(rootBox->lineTop(), y); + h = bottom - y; + } + + // Move x/y to our coordinates. + tx += x; + ty += y; + const NinePieceImage& maskNinePieceImage = renderer()->style()->maskBoxImage(); StyleImage* maskBoxImage = renderer()->style()->maskBoxImage().image(); - if ((maskBoxImage && renderer()->style()->maskLayers()->hasImage()) || renderer()->style()->maskLayers()->next()) - pushTransparencyLayer = true; - - CompositeOperator compositeOp = CompositeDestinationIn; - if (pushTransparencyLayer) { - paintInfo.context->setCompositeOperation(CompositeDestinationIn); - paintInfo.context->beginTransparencyLayer(1.0f); - compositeOp = CompositeSourceOver; + + // Figure out if we need to push a transparency layer to render our mask. + bool pushTransparencyLayer = false; + bool compositedMask = renderer()->hasLayer() && boxModelObject()->layer()->hasCompositedMask(); + CompositeOperator compositeOp = CompositeSourceOver; + if (!compositedMask) { + if ((maskBoxImage && renderer()->style()->maskLayers()->hasImage()) || renderer()->style()->maskLayers()->next()) + pushTransparencyLayer = true; + + compositeOp = CompositeDestinationIn; + if (pushTransparencyLayer) { + paintInfo.context->setCompositeOperation(CompositeDestinationIn); + paintInfo.context->beginTransparencyLayer(1.0f); + compositeOp = CompositeSourceOver; + } } paintFillLayers(paintInfo, Color(), renderer()->style()->maskLayers(), tx, ty, w, h, compositeOp); @@ -936,6 +993,7 @@ void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int setClip = true; } + ColorSpace colorSpace = renderer()->style()->colorSpace(); bool setShadow = false; do { if (shadow) { @@ -944,24 +1002,24 @@ void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int ty -= extraOffset; extraOffset = 0; } - context->setShadow(IntSize(shadow->x, shadow->y - extraOffset), shadow->blur, shadow->color); + context->setShadow(IntSize(shadow->x, shadow->y - extraOffset), shadow->blur, shadow->color, colorSpace); setShadow = true; shadow = shadow->next; } if (paintUnderline) { - context->setStrokeColor(underline); + context->setStrokeColor(underline, colorSpace); context->setStrokeStyle(SolidStroke); // Leave one pixel of white between the baseline and the underline. context->drawLineForText(IntPoint(tx, ty + baselinePos + 1), w, isPrinting); } if (paintOverline) { - context->setStrokeColor(overline); + context->setStrokeColor(overline, colorSpace); context->setStrokeStyle(SolidStroke); context->drawLineForText(IntPoint(tx, ty), w, isPrinting); } if (paintLineThrough) { - context->setStrokeColor(linethrough); + context->setStrokeColor(linethrough, colorSpace); context->setStrokeStyle(SolidStroke); context->drawLineForText(IntPoint(tx, ty + 2 * baselinePos / 3), w, isPrinting); } diff --git a/WebCore/rendering/InlineFlowBox.h b/WebCore/rendering/InlineFlowBox.h index 809fd54..23b5cc9 100644 --- a/WebCore/rendering/InlineFlowBox.h +++ b/WebCore/rendering/InlineFlowBox.h @@ -22,6 +22,7 @@ #define InlineFlowBox_h #include "InlineRunBox.h" +#include "RenderOverflow.h" namespace WebCore { @@ -35,10 +36,8 @@ public: : InlineRunBox(obj) , m_firstChild(0) , m_lastChild(0) - , m_maxHorizontalVisualOverflow(0) , m_includeLeftEdge(false) , m_includeRightEdge(false) - , m_hasTextChildren(true) #ifndef NDEBUG , m_hasBadChildList(false) #endif @@ -121,18 +120,13 @@ public: void determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject); int getFlowSpacingWidth(); bool onEndChain(RenderObject* endObject); - virtual int placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing); - virtual int verticallyAlignBoxes(int heightOfBlock); + virtual int placeBoxesHorizontally(int x, bool& needsWordSpacing); void computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, int& maxAscent, int& maxDescent, bool strictMode); void adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, int maxPositionTop, int maxPositionBottom); - void placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode, - int& topPosition, int& bottomPosition, int& selectionTop, int& selectionBottom); - - virtual void setVerticalOverflowPositions(int /*top*/, int /*bottom*/) { } - virtual void setVerticalSelectionPositions(int /*top*/, int /*bottom*/) { } - short maxHorizontalVisualOverflow() const { return m_maxHorizontalVisualOverflow; } + void placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode, int& lineTop, int& lineBottom); + void computeVerticalOverflow(int lineTop, int lineBottom, bool strictMode); void removeChild(InlineBox* child); @@ -146,12 +140,35 @@ public: void checkConsistency() const; void setHasBadChildList(); + int topVisibleOverflow() const { return std::min(topLayoutOverflow(), topVisualOverflow()); } + int bottomVisibleOverflow() const { return std::max(bottomLayoutOverflow(), bottomVisualOverflow()); } + int leftVisibleOverflow() const { return std::min(leftLayoutOverflow(), leftVisualOverflow()); } + int rightVisibleOverflow() const { return std::max(rightLayoutOverflow(), rightVisualOverflow()); } + IntRect visibleOverflowRect() const { return m_overflow ? m_overflow->visibleOverflowRect() : IntRect(m_x, m_y, m_width, height()); } + + int topLayoutOverflow() const { return m_overflow ? m_overflow->topLayoutOverflow() : m_y; } + int bottomLayoutOverflow() const { return m_overflow ? m_overflow->bottomLayoutOverflow() : m_y + height(); } + int leftLayoutOverflow() const { return m_overflow ? m_overflow->leftLayoutOverflow() : m_x; } + int rightLayoutOverflow() const { return m_overflow ? m_overflow->rightLayoutOverflow() : m_x + m_width; } + IntRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : IntRect(m_x, m_y, m_width, height()); } + + int topVisualOverflow() const { return m_overflow ? m_overflow->topVisualOverflow() : m_y; } + int bottomVisualOverflow() const { return m_overflow ? m_overflow->bottomVisualOverflow() : m_y + height(); } + int leftVisualOverflow() const { return m_overflow ? m_overflow->leftVisualOverflow() : m_x; } + int rightVisualOverflow() const { return m_overflow ? m_overflow->rightVisualOverflow() : m_x + m_width; } + IntRect visualOverflowRect() const { return m_overflow ? m_overflow->visualOverflowRect() : IntRect(m_x, m_y, m_width, height()); } + + void setHorizontalOverflowPositions(int leftLayoutOverflow, int rightLayoutOverflow, int leftVisualOverflow, int rightVisualOverflow); + void setVerticalOverflowPositions(int topLayoutOverflow, int bottomLayoutOverflow, int topVisualOverflow, int bottomVisualOverflow, int boxHeight); + +protected: + OwnPtr<RenderOverflow> m_overflow; + private: virtual bool isInlineFlowBox() const { return true; } InlineBox* m_firstChild; InlineBox* m_lastChild; - short m_maxHorizontalVisualOverflow; bool m_includeLeftEdge : 1; bool m_includeRightEdge : 1; @@ -162,6 +179,34 @@ private: #endif }; +inline void InlineFlowBox::setHorizontalOverflowPositions(int leftLayoutOverflow, int rightLayoutOverflow, int leftVisualOverflow, int rightVisualOverflow) +{ + if (!m_overflow) { + if (leftLayoutOverflow == m_x && rightLayoutOverflow == m_x + m_width && leftVisualOverflow == m_x && rightVisualOverflow == m_x + m_width) + return; + m_overflow.set(new RenderOverflow(IntRect(m_x, m_y, m_width, m_renderer->style(m_firstLine)->font().height()))); + } + + m_overflow->setLeftLayoutOverflow(leftLayoutOverflow); + m_overflow->setRightLayoutOverflow(rightLayoutOverflow); + m_overflow->setLeftVisualOverflow(leftVisualOverflow); + m_overflow->setRightVisualOverflow(rightVisualOverflow); +} + +inline void InlineFlowBox::setVerticalOverflowPositions(int topLayoutOverflow, int bottomLayoutOverflow, int topVisualOverflow, int bottomVisualOverflow, int boxHeight) +{ + if (!m_overflow) { + if (topLayoutOverflow == m_y && bottomLayoutOverflow == m_y + boxHeight && topVisualOverflow == m_y && bottomVisualOverflow == m_y + boxHeight) + return; + m_overflow.set(new RenderOverflow(IntRect(m_x, m_y, m_width, boxHeight))); + } + + m_overflow->setTopLayoutOverflow(topLayoutOverflow); + m_overflow->setBottomLayoutOverflow(bottomLayoutOverflow); + m_overflow->setTopVisualOverflow(topVisualOverflow); + m_overflow->setBottomVisualOverflow(bottomVisualOverflow); +} + #ifdef NDEBUG inline void InlineFlowBox::checkConsistency() const { diff --git a/WebCore/rendering/InlineIterator.h b/WebCore/rendering/InlineIterator.h new file mode 100644 index 0000000..9310ea8 --- /dev/null +++ b/WebCore/rendering/InlineIterator.h @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. All right reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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 InlineIterator_h +#define InlineIterator_h + +#include "BidiRun.h" +#include "RenderBlock.h" +#include "RenderText.h" +#include <wtf/AlwaysInline.h> +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +class InlineIterator { +public: + InlineIterator() + : block(0) + , obj(0) + , pos(0) + , nextBreakablePosition(-1) + { + } + + InlineIterator(RenderBlock* b, RenderObject* o, unsigned p) + : block(b) + , obj(o) + , pos(p) + , nextBreakablePosition(-1) + { + } + + void increment(InlineBidiResolver* resolver = 0); + bool atEnd() const; + + UChar current() const; + WTF::Unicode::Direction direction() const; + + RenderBlock* block; + RenderObject* obj; + unsigned pos; + int nextBreakablePosition; +}; + +inline bool operator==(const InlineIterator& it1, const InlineIterator& it2) +{ + return it1.pos == it2.pos && it1.obj == it2.obj; +} + +inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2) +{ + return it1.pos != it2.pos || it1.obj != it2.obj; +} + +static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0) +{ + RenderObject* next = 0; + bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false; + bool endOfInline = false; + + while (current) { + next = 0; + if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned() && !current->isText()) { + next = current->firstChild(); + if (next && resolver && next->isRenderInline()) { + EUnicodeBidi ub = next->style()->unicodeBidi(); + if (ub != UBNormal) { + TextDirection dir = next->style()->direction(); + WTF::Unicode::Direction d = (ub == Embed + ? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding : WTF::Unicode::LeftToRightEmbedding) + : (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride)); + resolver->embed(d); + } + } + } + + if (!next) { + if (!skipInlines && !oldEndOfInline && current->isRenderInline()) { + next = current; + endOfInline = true; + break; + } + + while (current && current != block) { + if (resolver && current->isRenderInline() && current->style()->unicodeBidi() != UBNormal) + resolver->embed(WTF::Unicode::PopDirectionalFormat); + + next = current->nextSibling(); + if (next) { + if (resolver && next->isRenderInline()) { + EUnicodeBidi ub = next->style()->unicodeBidi(); + if (ub != UBNormal) { + TextDirection dir = next->style()->direction(); + WTF::Unicode::Direction d = (ub == Embed + ? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding: WTF::Unicode::LeftToRightEmbedding) + : (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride)); + resolver->embed(d); + } + } + break; + } + + current = current->parent(); + if (!skipInlines && current && current != block && current->isRenderInline()) { + next = current; + endOfInline = true; + break; + } + } + } + + if (!next) + break; + + if (next->isText() || next->isFloating() || next->isReplaced() || next->isPositioned() + || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines. + && next->isRenderInline())) + break; + current = next; + } + + if (endOfInlinePtr) + *endOfInlinePtr = endOfInline; + + return next; +} + +static inline RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* resolver, bool skipInlines = true) +{ + if (!block->firstChild()) + return 0; + + RenderObject* o = block->firstChild(); + if (o->isRenderInline()) { + if (resolver) { + EUnicodeBidi ub = o->style()->unicodeBidi(); + if (ub != UBNormal) { + TextDirection dir = o->style()->direction(); + WTF::Unicode::Direction d = (ub == Embed + ? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding : WTF::Unicode::LeftToRightEmbedding) + : (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride)); + resolver->embed(d); + } + } + if (skipInlines && o->firstChild()) + o = bidiNext(block, o, resolver, skipInlines); + else { + // Never skip empty inlines. + if (resolver) + resolver->commitExplicitEmbedding(); + return o; + } + } + + if (o && !o->isText() && !o->isReplaced() && !o->isFloating() && !o->isPositioned()) + o = bidiNext(block, o, resolver, skipInlines); + + if (resolver) + resolver->commitExplicitEmbedding(); + return o; +} + +inline void InlineIterator::increment(InlineBidiResolver* resolver) +{ + if (!obj) + return; + if (obj->isText()) { + pos++; + if (pos >= toRenderText(obj)->textLength()) { + obj = bidiNext(block, obj, resolver); + pos = 0; + nextBreakablePosition = -1; + } + } else { + obj = bidiNext(block, obj, resolver); + pos = 0; + nextBreakablePosition = -1; + } +} + +inline bool InlineIterator::atEnd() const +{ + return !obj; +} + +inline UChar InlineIterator::current() const +{ + if (!obj || !obj->isText()) + return 0; + + RenderText* text = toRenderText(obj); + if (pos >= text->textLength()) + return 0; + + return text->characters()[pos]; +} + +ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const +{ + if (UChar c = current()) + return WTF::Unicode::direction(c); + + if (obj && obj->isListMarker()) + return obj->style()->direction() == LTR ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; + + return WTF::Unicode::OtherNeutral; +} + +template<> +inline void InlineBidiResolver::increment() +{ + current.increment(this); +} + +template <> +inline void InlineBidiResolver::appendRun() +{ + if (!emptyRun && !eor.atEnd()) { + int start = sor.pos; + RenderObject *obj = sor.obj; + while (obj && obj != eor.obj && obj != endOfLine.obj) { + RenderBlock::appendRunsForObject(start, obj->length(), obj, *this); + start = 0; + obj = bidiNext(sor.block, obj); + } + if (obj) { + unsigned pos = obj == eor.obj ? eor.pos : UINT_MAX; + if (obj == endOfLine.obj && endOfLine.pos <= pos) { + reachedEndOfLine = true; + pos = endOfLine.pos; + } + // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be + int end = obj->length() ? pos+1 : 0; + RenderBlock::appendRunsForObject(start, end, obj, *this); + } + + eor.increment(); + sor = eor; + } + + m_direction = WTF::Unicode::OtherNeutral; + m_status.eor = WTF::Unicode::OtherNeutral; +} + +} + +#endif // InlineIterator_h diff --git a/WebCore/rendering/InlineRunBox.h b/WebCore/rendering/InlineRunBox.h index 0f7c29b..cbc82d5 100644 --- a/WebCore/rendering/InlineRunBox.h +++ b/WebCore/rendering/InlineRunBox.h @@ -1,6 +1,4 @@ /* - * This file is part of the line box implementation for KDE. - * * Copyright (C) 2003, 2006 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or diff --git a/WebCore/rendering/InlineTextBox.cpp b/WebCore/rendering/InlineTextBox.cpp index 619fb95..b7e6de2 100644 --- a/WebCore/rendering/InlineTextBox.cpp +++ b/WebCore/rendering/InlineTextBox.cpp @@ -23,9 +23,11 @@ #include "config.h" #include "InlineTextBox.h" +#include "Chrome.h" #include "ChromeClient.h" #include "Document.h" #include "Editor.h" +#include "EllipsisBox.h" #include "Frame.h" #include "GraphicsContext.h" #include "HitTestResult.h" @@ -81,6 +83,23 @@ RenderObject::SelectionState InlineTextBox::selectionState() else if (state == RenderObject::SelectionBoth) state = RenderObject::SelectionNone; } + + // If there are ellipsis following, make sure their selection is updated. + if (m_truncation != cNoTruncation && root()->ellipsisBox()) { + EllipsisBox* ellipsis = root()->ellipsisBox(); + if (state != RenderObject::SelectionNone) { + int start, end; + selectionStartEnd(start, end); + // The ellipsis should be considered to be selected if the end of + // the selection is past the beginning of the truncation and the + // beginning of the selection is before or at the beginning of the + // truncation. + ellipsis->setSelectionState(end >= m_truncation && start <= m_truncation ? + RenderObject::SelectionInside : RenderObject::SelectionNone); + } else + ellipsis->setSelectionState(RenderObject::SelectionNone); + } + return state; } @@ -89,7 +108,7 @@ IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos) int sPos = max(startPos - m_start, 0); int ePos = min(endPos - m_start, (int)m_len); - if (sPos >= ePos) + if (sPos > ePos) return IntRect(); RenderText* textObj = textRenderer(); @@ -214,7 +233,7 @@ Color correctedTextColor(Color textColor, Color backgroundColor) return textColor.light(); } -void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness) +void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness, ColorSpace colorSpace) { int mode = context->textDrawingMode(); if (strokeThickness > 0) { @@ -225,12 +244,12 @@ void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, con } } - if (mode & cTextFill && fillColor != context->fillColor()) - context->setFillColor(fillColor); + if (mode & cTextFill && (fillColor != context->fillColor() || colorSpace != context->fillColorSpace())) + context->setFillColor(fillColor, colorSpace); if (mode & cTextStroke) { if (strokeColor != context->strokeColor()) - context->setStrokeColor(strokeColor); + context->setStrokeColor(strokeColor, colorSpace); if (strokeThickness != context->strokeThickness()) context->setStrokeThickness(strokeThickness); } @@ -254,12 +273,13 @@ bool InlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, in return false; } -static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, int startOffset, int endOffset, const IntPoint& textOrigin, int x, int y, int w, int h, ShadowData* shadow, bool stroked) +static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, int startOffset, int endOffset, int truncationPoint, const IntPoint& textOrigin, int x, int y, int w, int h, ShadowData* shadow, bool stroked) { Color fillColor = context->fillColor(); + ColorSpace fillColorSpace = context->fillColorSpace(); bool opaque = fillColor.alpha() == 255; if (!opaque) - context->setFillColor(Color::black); + context->setFillColor(Color::black, fillColorSpace); do { IntSize extraOffset; @@ -279,17 +299,17 @@ static void paintTextWithShadows(GraphicsContext* context, const Font& font, con extraOffset = IntSize(0, 2 * h + max(0, shadowOffset.height()) + shadowBlur); shadowOffset -= extraOffset; } - context->setShadow(shadowOffset, shadowBlur, shadowColor); + context->setShadow(shadowOffset, shadowBlur, shadowColor, fillColorSpace); } else if (!opaque) - context->setFillColor(fillColor); + context->setFillColor(fillColor, fillColorSpace); if (startOffset <= endOffset) context->drawText(font, textRun, textOrigin + extraOffset, startOffset, endOffset); else { if (endOffset > 0) context->drawText(font, textRun, textOrigin + extraOffset, 0, endOffset); - if (startOffset < textRun.length()) - context->drawText(font, textRun, textOrigin + extraOffset, startOffset); + if (startOffset < truncationPoint) + context->drawText(font, textRun, textOrigin + extraOffset, startOffset, truncationPoint); } if (!shadow) @@ -312,8 +332,12 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines); - int xPos = tx + m_x - parent()->maxHorizontalVisualOverflow(); - int w = width() + 2 * parent()->maxHorizontalVisualOverflow(); + // FIXME: Technically we're potentially incorporating other visual overflow that had nothing to do with us. + // Would it be simpler to just check our own shadow and stroke overflow by hand here? + int leftOverflow = parent()->x() - parent()->leftVisualOverflow(); + int rightOverflow = parent()->rightVisualOverflow() - (parent()->x() + parent()->width()); + int xPos = tx + m_x - leftOverflow; + int w = width() + leftOverflow + rightOverflow; if (xPos >= paintInfo.rect.right() || xPos + w <= paintInfo.rect.x()) return; @@ -455,18 +479,25 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) if (paintSelectedTextOnly || paintSelectedTextSeparately) selectionStartEnd(sPos, ePos); + int length = m_len; + if (m_truncation != cNoTruncation) { + sPos = min<int>(sPos, m_truncation); + ePos = min<int>(ePos, m_truncation); + length = m_truncation; + } + if (!paintSelectedTextOnly) { // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side // effect, so only when we know we're stroking, do a save/restore. if (textStrokeWidth > 0) context->save(); - updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth); + updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); if (!paintSelectedTextSeparately || ePos <= sPos) { // FIXME: Truncate right-to-left text correctly. - paintTextWithShadows(context, font, textRun, 0, m_truncation == cNoTruncation ? m_len : m_truncation, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); + paintTextWithShadows(context, font, textRun, 0, length, length, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); } else - paintTextWithShadows(context, font, textRun, ePos, sPos, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); + paintTextWithShadows(context, font, textRun, ePos, sPos, length, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); if (textStrokeWidth > 0) context->restore(); @@ -477,8 +508,8 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) if (selectionStrokeWidth > 0) context->save(); - updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth); - paintTextWithShadows(context, font, textRun, sPos, ePos, textOrigin, m_x + tx, m_y + ty, width(), height(), selectionShadow, selectionStrokeWidth > 0); + updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth, styleToUse->colorSpace()); + paintTextWithShadows(context, font, textRun, sPos, ePos, length, textOrigin, m_x + tx, m_y + ty, width(), height(), selectionShadow, selectionStrokeWidth > 0); if (selectionStrokeWidth > 0) context->restore(); @@ -486,7 +517,7 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) // Paint decorations if (d != TDNONE && paintInfo.phase != PaintPhaseSelection && styleToUse->htmlHacks()) { - context->setStrokeColor(styleToUse->color()); + context->setStrokeColor(styleToUse->color(), styleToUse->colorSpace()); paintDecoration(context, tx, ty, d, textShadow); } @@ -557,13 +588,16 @@ void InlineTextBox::paintSelection(GraphicsContext* context, int tx, int ty, Ren c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); context->save(); - updateGraphicsContext(context, c, c, 0); // Don't draw text at all! + updateGraphicsContext(context, c, c, 0, style->colorSpace()); // Don't draw text at all! int y = selectionTop(); int h = selectionHeight(); + // If the text is truncated, let the thing being painted in the truncation + // draw its own highlight. + int length = m_truncation != cNoTruncation ? m_truncation : m_len; context->clip(IntRect(m_x + tx, y + ty, m_width, h)); - context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, + context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, length, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), - IntPoint(m_x + tx, y + ty), h, c, sPos, ePos); + IntPoint(m_x + tx, y + ty), h, c, style->colorSpace(), sPos, ePos); context->restore(); } @@ -580,13 +614,13 @@ void InlineTextBox::paintCompositionBackground(GraphicsContext* context, int tx, Color c = Color(225, 221, 85); - updateGraphicsContext(context, c, c, 0); // Don't draw text at all! + updateGraphicsContext(context, c, c, 0, style->colorSpace()); // Don't draw text at all! int y = selectionTop(); int h = selectionHeight(); context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), - IntPoint(m_x + tx, y + ty), h, c, sPos, ePos); + IntPoint(m_x + tx, y + ty), h, c, style->colorSpace(), sPos, ePos); context->restore(); } @@ -656,6 +690,7 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, in setClip = true; } + ColorSpace colorSpace = renderer()->style()->colorSpace(); bool setShadow = false; do { @@ -665,24 +700,24 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, in ty -= extraOffset; extraOffset = 0; } - context->setShadow(IntSize(shadow->x, shadow->y - extraOffset), shadow->blur, shadow->color); + context->setShadow(IntSize(shadow->x, shadow->y - extraOffset), shadow->blur, shadow->color, colorSpace); setShadow = true; shadow = shadow->next; } if (deco & UNDERLINE) { - context->setStrokeColor(underline); + context->setStrokeColor(underline, colorSpace); context->setStrokeStyle(SolidStroke); // Leave one pixel of white between the baseline and the underline. context->drawLineForText(IntPoint(tx, ty + baseline + 1), width, isPrinting); } if (deco & OVERLINE) { - context->setStrokeColor(overline); + context->setStrokeColor(overline, colorSpace); context->setStrokeStyle(SolidStroke); context->drawLineForText(IntPoint(tx, ty), width, isPrinting); } if (deco & LINE_THROUGH) { - context->setStrokeColor(linethrough); + context->setStrokeColor(linethrough, colorSpace); context->setStrokeStyle(SolidStroke); context->drawLineForText(IntPoint(tx, ty + 2 * baseline / 3), width, isPrinting); } @@ -694,7 +729,7 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, in context->clearShadow(); } -void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font& font, bool grammar) +void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, int ty, const DocumentMarker& marker, RenderStyle* style, const Font& font, bool grammar) { // Never print spelling/grammar markers (5327887) if (textRenderer()->document()->printing()) @@ -733,8 +768,11 @@ 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) + if (grammar) { + markerRect.move(-tx, -ty); + markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox(); renderer()->document()->setRenderedRectForMarker(renderer()->node(), marker, markerRect); + } } // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to @@ -757,7 +795,7 @@ void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, in pt->drawLineForMisspellingOrBadGrammar(IntPoint(tx + m_x + start, ty + m_y + underlineOffset), width, grammar); } -void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font& font) +void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, const DocumentMarker& marker, RenderStyle* style, const Font& font) { // Use same y positioning and height as for selection, so that when the selection and this highlight are on // the same word there are no pieces sticking out. @@ -767,10 +805,10 @@ 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(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)); + // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates. + IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(m_x, y), h, sPos, ePos)); + markerRect = root()->block()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox(); renderer()->document()->setRenderedRectForMarker(renderer()->node(), marker, markerRect); // Optionally highlight the text @@ -779,14 +817,14 @@ void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, Do renderer()->theme()->platformActiveTextSearchHighlightColor() : renderer()->theme()->platformInactiveTextSearchHighlightColor(); pt->save(); - updateGraphicsContext(pt, color, color, 0); // Don't draw text at all! + updateGraphicsContext(pt, color, color, 0, style->colorSpace()); // Don't draw text at all! pt->clip(IntRect(tx + m_x, ty + y, m_width, h)); - pt->drawHighlightForText(font, run, startPoint, h, color, sPos, ePos); + pt->drawHighlightForText(font, run, IntPoint(m_x + tx, y + ty), h, color, style->colorSpace(), sPos, ePos); pt->restore(); } } -void InlineTextBox::computeRectForReplacementMarker(int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font& font) +void InlineTextBox::computeRectForReplacementMarker(int /*tx*/, int /*ty*/, const DocumentMarker& marker, RenderStyle* style, const Font& font) { // Replacement markers are not actually drawn, but their rects need to be computed for hit testing. int y = selectionTop(); @@ -795,10 +833,11 @@ void InlineTextBox::computeRectForReplacementMarker(int tx, int ty, DocumentMark int sPos = max(marker.startOffset - m_start, (unsigned)0); int ePos = min(marker.endOffset - m_start, (unsigned)m_len); TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); - IntPoint startPoint = IntPoint(m_x + tx, y + ty); + IntPoint startPoint = IntPoint(m_x, y); // Compute and store the rect associated with this marker. IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos)); + markerRect = root()->block()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox(); renderer()->document()->setRenderedRectForMarker(renderer()->node(), marker, markerRect); } @@ -813,7 +852,7 @@ void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, int tx, int ty, Re // Give any document markers that touch this run a chance to draw before the text has been drawn. // Note end() points at the last char, not one past it like endOffset and ranges do. for ( ; markerIt != markers.end(); markerIt++) { - DocumentMarker marker = *markerIt; + const DocumentMarker& marker = *markerIt; // Paint either the background markers or the foreground markers, but not both switch (marker.type) { @@ -907,7 +946,7 @@ void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, int tx, int start += 1; width -= 2; - ctx->setStrokeColor(underline.color); + ctx->setStrokeColor(underline.color, renderer()->style()->colorSpace()); ctx->setStrokeThickness(lineThickness); ctx->drawLineForText(IntPoint(tx + start, ty + height() - lineThickness), width, textRenderer()->document()->printing()); } @@ -932,7 +971,7 @@ int InlineTextBox::textPos() const if (x() == 0) return 0; - RenderBlock *blockElement = renderer()->containingBlock(); + RenderBlock* blockElement = renderer()->containingBlock(); return direction() == RTL ? x() - blockElement->borderRight() - blockElement->paddingRight() : x() - blockElement->borderLeft() - blockElement->paddingLeft(); } @@ -943,7 +982,7 @@ int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const return 0; RenderText* text = toRenderText(renderer()); - RenderStyle *style = text->style(m_firstLine); + RenderStyle* style = text->style(m_firstLine); const Font* f = &style->font(); return f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), _x - m_x, includePartialGlyphs); diff --git a/WebCore/rendering/InlineTextBox.h b/WebCore/rendering/InlineTextBox.h index 3bbb453..0a83ddd 100644 --- a/WebCore/rendering/InlineTextBox.h +++ b/WebCore/rendering/InlineTextBox.h @@ -34,7 +34,7 @@ const unsigned short cNoTruncation = USHRT_MAX; const unsigned short cFullTruncation = USHRT_MAX - 1; // Helper functions shared by InlineTextBox / SVGRootInlineBox -void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness); +void updateGraphicsContext(GraphicsContext*, const Color& fillColor, const Color& strokeColor, float strokeThickness, ColorSpace); Color correctedTextColor(Color textColor, Color backgroundColor); class InlineTextBox : public InlineRunBox { @@ -62,6 +62,8 @@ public: void setFallbackFonts(const HashSet<const SimpleFontData*>&); void takeFallbackFonts(Vector<const SimpleFontData*>&); + unsigned short truncation() { return m_truncation; } + private: virtual int selectionTop(); virtual int selectionHeight(); @@ -131,9 +133,9 @@ protected: private: void paintDecoration(GraphicsContext*, int tx, int ty, int decoration, ShadowData*); void paintSelection(GraphicsContext*, int tx, int ty, RenderStyle*, const Font&); - void paintSpellingOrGrammarMarker(GraphicsContext*, int tx, int ty, DocumentMarker, RenderStyle*, const Font&, bool grammar); - void paintTextMatchMarker(GraphicsContext*, int tx, int ty, DocumentMarker, RenderStyle*, const Font&); - void computeRectForReplacementMarker(int tx, int ty, DocumentMarker, RenderStyle*, const Font&); + void paintSpellingOrGrammarMarker(GraphicsContext*, int tx, int ty, const DocumentMarker&, RenderStyle*, const Font&, bool grammar); + void paintTextMatchMarker(GraphicsContext*, int tx, int ty, const DocumentMarker&, RenderStyle*, const Font&); + void computeRectForReplacementMarker(int tx, int ty, const DocumentMarker&, RenderStyle*, const Font&); }; inline RenderText* InlineTextBox::textRenderer() const diff --git a/WebCore/rendering/MediaControlElements.cpp b/WebCore/rendering/MediaControlElements.cpp index 352f270..2b7a8c8 100644 --- a/WebCore/rendering/MediaControlElements.cpp +++ b/WebCore/rendering/MediaControlElements.cpp @@ -32,28 +32,38 @@ #include "MediaControlElements.h" -#include "LocalizedStrings.h" #include "EventNames.h" #include "FloatConversion.h" #include "Frame.h" #include "HTMLNames.h" +#include "LocalizedStrings.h" #include "MouseEvent.h" +#include "Page.h" #include "RenderMedia.h" #include "RenderSlider.h" #include "RenderTheme.h" -#include "CString.h" namespace WebCore { using namespace HTMLNames; -// FIXME: These constants may need to be tweaked to better match the seeking in the QT plugin +HTMLMediaElement* toParentMediaElement(RenderObject* o) +{ + Node* node = o->node(); + Node* mediaNode = node ? node->shadowAncestorNode() : 0; + if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag))) + return 0; + + return static_cast<HTMLMediaElement*>(mediaNode); +} + +// FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in. static const float cSeekRepeatDelay = 0.1f; static const float cStepTime = 0.07f; static const float cSeekTime = 0.2f; -MediaControlShadowRootElement::MediaControlShadowRootElement(Document* doc, HTMLMediaElement* mediaElement) - : HTMLDivElement(divTag, doc) +MediaControlShadowRootElement::MediaControlShadowRootElement(Document* document, HTMLMediaElement* mediaElement) + : HTMLDivElement(divTag, document) , m_mediaElement(mediaElement) { RefPtr<RenderStyle> rootStyle = RenderStyle::create(); @@ -76,14 +86,36 @@ void MediaControlShadowRootElement::updateStyle() } // ---------------------------- - -MediaControlElement::MediaControlElement(Document* doc, PseudoId pseudo, HTMLMediaElement* mediaElement) - : HTMLDivElement(divTag, doc) +MediaControlElement::MediaControlElement(Document* document, PseudoId pseudo, HTMLMediaElement* mediaElement) + : HTMLDivElement(divTag, document) , m_mediaElement(mediaElement) , m_pseudoStyleId(pseudo) { setInDocument(true); + switch (pseudo) { + case MEDIA_CONTROLS_CURRENT_TIME_DISPLAY: + m_displayType = MediaCurrentTimeDisplay; + break; + case MEDIA_CONTROLS_TIME_REMAINING_DISPLAY: + m_displayType = MediaTimeRemainingDisplay; + break; + case MEDIA_CONTROLS_TIMELINE_CONTAINER: + m_displayType = MediaTimelineContainer; + break; + case MEDIA_CONTROLS_STATUS_DISPLAY: + m_displayType = MediaStatusDisplay; + break; + case MEDIA_CONTROLS_PANEL: + m_displayType = MediaControlsPanel; + break; + case MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER: + m_displayType = MediaVolumeSliderContainer; + break; + default: + ASSERT_NOT_REACHED(); + break; + } } void MediaControlElement::attachToParent(Element* parent) @@ -114,7 +146,10 @@ PassRefPtr<RenderStyle> MediaControlElement::styleForElement() bool MediaControlElement::rendererIsNeeded(RenderStyle* style) { - return HTMLDivElement::rendererIsNeeded(style) && parent() && parent()->renderer(); + ASSERT(document()->page()); + + return HTMLDivElement::rendererIsNeeded(style) && parent() && parent()->renderer() + && (!style->hasAppearance() || document()->page()->theme()->shouldRenderMediaControlPart(style->appearance(), m_mediaElement)); } void MediaControlElement::attach() @@ -165,8 +200,8 @@ void MediaControlElement::updateStyle() // ---------------------------- -MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* doc, HTMLMediaElement* element) -: MediaControlElement(doc, MEDIA_CONTROLS_TIMELINE_CONTAINER, element) +MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* document, HTMLMediaElement* element) + : MediaControlElement(document, MEDIA_CONTROLS_TIMELINE_CONTAINER, element) { } @@ -185,12 +220,54 @@ bool MediaControlTimelineContainerElement::rendererIsNeeded(RenderStyle* style) return !isnan(duration) && !isinf(duration); } - // ---------------------------- -MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* doc, HTMLMediaElement* element) -: MediaControlElement(doc, MEDIA_CONTROLS_STATUS_DISPLAY, element) -, m_stateBeingDisplayed(Nothing) +MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(Document* doc, HTMLMediaElement* element) + : MediaControlElement(doc, MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER, element) + , m_isVisible(false) + , m_x(0) + , m_y(0) +{ +} + +PassRefPtr<RenderStyle> MediaControlVolumeSliderContainerElement::styleForElement() +{ + RefPtr<RenderStyle> style = MediaControlElement::styleForElement(); + style->setPosition(AbsolutePosition); + style->setLeft(Length(m_x, Fixed)); + style->setTop(Length(m_y, Fixed)); + style->setDisplay(m_isVisible ? BLOCK : NONE); + return style; +} + +void MediaControlVolumeSliderContainerElement::setVisible(bool visible) +{ + if (visible == m_isVisible) + return; + m_isVisible = visible; +} + +void MediaControlVolumeSliderContainerElement::setPosition(int x, int y) +{ + if (x == m_x && y == m_y) + return; + m_x = x; + m_y = y; +} + +bool MediaControlVolumeSliderContainerElement::hitTest(const IntPoint& absPoint) +{ + if (renderer() && renderer()->style()->hasAppearance()) + return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint); + + return false; +} + +// ---------------------------- + +MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* document, HTMLMediaElement* element) + : MediaControlElement(document, MEDIA_CONTROLS_STATUS_DISPLAY, element) + , m_stateBeingDisplayed(Nothing) { } @@ -235,14 +312,49 @@ bool MediaControlStatusDisplayElement::rendererIsNeeded(RenderStyle* style) // ---------------------------- -MediaControlInputElement::MediaControlInputElement(Document* doc, PseudoId pseudo, const String& type, HTMLMediaElement* mediaElement, MediaControlElementType displayType) - : HTMLInputElement(inputTag, doc) +MediaControlInputElement::MediaControlInputElement(Document* document, PseudoId pseudo, const String& type, HTMLMediaElement* mediaElement) + : HTMLInputElement(inputTag, document) , m_mediaElement(mediaElement) , m_pseudoStyleId(pseudo) - , m_displayType(displayType) { setInputType(type); setInDocument(true); + + switch (pseudo) { + case MEDIA_CONTROLS_MUTE_BUTTON: + m_displayType = MediaMuteButton; + break; + case MEDIA_CONTROLS_PLAY_BUTTON: + m_displayType = MediaPlayButton; + break; + case MEDIA_CONTROLS_SEEK_FORWARD_BUTTON: + m_displayType = MediaSeekForwardButton; + break; + case MEDIA_CONTROLS_SEEK_BACK_BUTTON: + m_displayType = MediaSeekBackButton; + break; + case MEDIA_CONTROLS_FULLSCREEN_BUTTON: + m_displayType = MediaFullscreenButton; + break; + case MEDIA_CONTROLS_TIMELINE: + m_displayType = MediaSlider; + break; + case MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON: + m_displayType = MediaReturnToRealtimeButton; + break; + case MEDIA_CONTROLS_REWIND_BUTTON: + m_displayType = MediaRewindButton; + break; + case MEDIA_CONTROLS_VOLUME_SLIDER: + m_displayType = MediaVolumeSlider; + break; + case MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON: + m_displayType = MediaShowClosedCaptionsButton; + break; + default: + ASSERT_NOT_REACHED(); + break; + } } void MediaControlInputElement::attachToParent(Element* parent) @@ -265,7 +377,10 @@ PassRefPtr<RenderStyle> MediaControlInputElement::styleForElement() bool MediaControlInputElement::rendererIsNeeded(RenderStyle* style) { - return HTMLInputElement::rendererIsNeeded(style) && parent() && parent()->renderer(); + ASSERT(document()->page()); + + return HTMLInputElement::rendererIsNeeded(style) && parent() && parent()->renderer() + && (!style->hasAppearance() || document()->page()->theme()->shouldRenderMediaControlPart(style->appearance(), m_mediaElement)); } void MediaControlInputElement::attach() @@ -324,14 +439,14 @@ void MediaControlInputElement::setDisplayType(MediaControlElementType displayTyp return; m_displayType = displayType; - if (RenderObject* o = renderer()) - o->repaint(); + if (RenderObject* object = renderer()) + object->repaint(); } // ---------------------------- -MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* doc, HTMLMediaElement* element) - : MediaControlInputElement(doc, MEDIA_CONTROLS_MUTE_BUTTON, "button", element, element->muted() ? MediaUnMuteButton : MediaMuteButton) +MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* document, HTMLMediaElement* element) + : MediaControlInputElement(document, MEDIA_CONTROLS_MUTE_BUTTON, "button", element) { } @@ -351,8 +466,8 @@ void MediaControlMuteButtonElement::updateDisplayType() // ---------------------------- -MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* doc, HTMLMediaElement* element) - : MediaControlInputElement(doc, MEDIA_CONTROLS_PLAY_BUTTON, "button", element, element->canPlay() ? MediaPlayButton : MediaPauseButton) +MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* document, HTMLMediaElement* element) + : MediaControlInputElement(document, MEDIA_CONTROLS_PLAY_BUTTON, "button", element) { } @@ -372,9 +487,9 @@ void MediaControlPlayButtonElement::updateDisplayType() // ---------------------------- -MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* doc, HTMLMediaElement* element, bool forward) - : MediaControlInputElement(doc, forward ? MEDIA_CONTROLS_SEEK_FORWARD_BUTTON : MEDIA_CONTROLS_SEEK_BACK_BUTTON, - "button", element, forward ? MediaSeekForwardButton : MediaSeekBackButton) +MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* document, HTMLMediaElement* element, bool forward) + : MediaControlInputElement(document, forward ? MEDIA_CONTROLS_SEEK_FORWARD_BUTTON : MEDIA_CONTROLS_SEEK_BACK_BUTTON, + "button", element) , m_forward(forward) , m_seeking(false) , m_capturing(false) @@ -389,7 +504,7 @@ void MediaControlSeekButtonElement::defaultEventHandler(Event* event) m_capturing = true; frame->eventHandler()->setCapturingMouseEventsNode(this); } - m_mediaElement->pause(); + m_mediaElement->pause(event->fromUserGesture()); m_seekTimer.startRepeating(cSeekRepeatDelay); event->setDefaultHandled(); } else if (event->type() == eventNames().mouseupEvent) { @@ -432,8 +547,8 @@ void MediaControlSeekButtonElement::detach() // ---------------------------- -MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* doc, HTMLMediaElement* element) -: MediaControlInputElement(doc, MEDIA_CONTROLS_REWIND_BUTTON, "button", element, MediaRewindButton) +MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* document, HTMLMediaElement* element) + : MediaControlInputElement(document, MEDIA_CONTROLS_REWIND_BUTTON, "button", element) { } @@ -446,44 +561,58 @@ void MediaControlRewindButtonElement::defaultEventHandler(Event* event) HTMLInputElement::defaultEventHandler(event); } -bool MediaControlRewindButtonElement::rendererIsNeeded(RenderStyle* style) + +// ---------------------------- + +MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* document, HTMLMediaElement* element) + : MediaControlInputElement(document, MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, "button", element) +{ +} + +void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event) { - return MediaControlInputElement::rendererIsNeeded(style) && m_mediaElement->movieLoadType() != MediaPlayer::LiveStream; + if (event->type() == eventNames().clickEvent) { + m_mediaElement->returnToRealtime(); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); } // ---------------------------- -MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* doc, HTMLMediaElement* element) -: MediaControlInputElement(doc, MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, "button", element, MediaReturnToRealtimeButton) +MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* doc, HTMLMediaElement* element) + : MediaControlInputElement(doc, MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON, "button", element) { } -void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event) +void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event) { if (event->type() == eventNames().clickEvent) { - m_mediaElement->returnToRealtime(); + m_mediaElement->setClosedCaptionsVisible(!m_mediaElement->closedCaptionsVisible()); + setChecked(m_mediaElement->closedCaptionsVisible()); event->setDefaultHandled(); } HTMLInputElement::defaultEventHandler(event); } -bool MediaControlReturnToRealtimeButtonElement::rendererIsNeeded(RenderStyle* style) +void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() { - return MediaControlInputElement::rendererIsNeeded(style) && m_mediaElement->movieLoadType() == MediaPlayer::LiveStream; + setDisplayType(m_mediaElement->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton); } + // ---------------------------- MediaControlTimelineElement::MediaControlTimelineElement(Document* document, HTMLMediaElement* element) - : MediaControlInputElement(document, MEDIA_CONTROLS_TIMELINE, "range", element, MediaTimelineContainer) -{ + : MediaControlInputElement(document, MEDIA_CONTROLS_TIMELINE, "range", element) +{ } void MediaControlTimelineElement::defaultEventHandler(Event* event) { - // Left button is 0. Accepts only if mouse event is from left button. - if (!event->isMouseEvent() || static_cast<MouseEvent*>(event)->button()) + // Left button is 0. Rejects mouse events not from left button. + if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button()) return; if (event->type() == eventNames().mousedownEvent) @@ -520,29 +649,59 @@ void MediaControlTimelineElement::update(bool updateDuration) // ---------------------------- -MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* doc, HTMLMediaElement* element) - : MediaControlInputElement(doc, MEDIA_CONTROLS_FULLSCREEN_BUTTON, "button", element, MediaFullscreenButton) +MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(Document* document, HTMLMediaElement* element) + : MediaControlInputElement(document, MEDIA_CONTROLS_VOLUME_SLIDER, "range", element) { } -void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event) +void MediaControlVolumeSliderElement::defaultEventHandler(Event* event) { - if (event->type() == eventNames().clickEvent) { - event->setDefaultHandled(); + // Left button is 0. Rejects mouse events not from left button. + if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button()) + return; + + MediaControlInputElement::defaultEventHandler(event); + + if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent) + return; + + float volume = narrowPrecisionToFloat(value().toDouble()); + if (volume != m_mediaElement->volume()) { + ExceptionCode ec = 0; + m_mediaElement->setVolume(volume, ec); + ASSERT(!ec); } - HTMLInputElement::defaultEventHandler(event); } -bool MediaControlFullscreenButtonElement::rendererIsNeeded(RenderStyle* style) +void MediaControlVolumeSliderElement::update() { - return MediaControlInputElement::rendererIsNeeded(style) && m_mediaElement->supportsFullscreen(); + float volume = m_mediaElement->volume(); + if (value().toFloat() != volume) + setValue(String::number(volume)); + MediaControlInputElement::update(); } +// ---------------------------- + +MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document, HTMLMediaElement* element) + : MediaControlInputElement(document, MEDIA_CONTROLS_FULLSCREEN_BUTTON, "button", element) +{ +} + +void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + m_mediaElement->enterFullscreen(); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} // ---------------------------- -MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(Document* doc, PseudoId pseudo, HTMLMediaElement* element) - : MediaControlElement(doc, pseudo, element) +MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(Document* document, PseudoId pseudo, HTMLMediaElement* element) + : MediaControlElement(document, pseudo, element) + , m_currentValue(0) , m_isVisible(true) { } @@ -572,6 +731,11 @@ void MediaControlTimeDisplayElement::setVisible(bool visible) renderer()->setStyle(style.get()); } +void MediaControlTimeDisplayElement::setCurrentValue(float time) +{ + m_currentValue = time; +} + } //namespace WebCore #endif // enable(video) diff --git a/WebCore/rendering/MediaControlElements.h b/WebCore/rendering/MediaControlElements.h index d5fa5d2..21831ce 100644 --- a/WebCore/rendering/MediaControlElements.h +++ b/WebCore/rendering/MediaControlElements.h @@ -54,15 +54,22 @@ enum MediaControlElementType { MediaSliderThumb, MediaRewindButton, MediaReturnToRealtimeButton, + MediaShowClosedCaptionsButton, + MediaHideClosedCaptionsButton, MediaUnMuteButton, MediaPauseButton, MediaTimelineContainer, - MediaCurrentTimeDisplay, + MediaCurrentTimeDisplay, MediaTimeRemainingDisplay, MediaStatusDisplay, - MediaControlsPanel + MediaControlsPanel, + MediaVolumeSliderContainer, + MediaVolumeSlider, + MediaVolumeSliderThumb }; +HTMLMediaElement* toParentMediaElement(RenderObject*); + class MediaControlShadowRootElement : public HTMLDivElement { public: MediaControlShadowRootElement(Document*, HTMLMediaElement*); @@ -89,9 +96,15 @@ public: void update(); virtual void updateStyle(); + MediaControlElementType displayType() const { return m_displayType; } + + HTMLMediaElement* mediaElement() const { return m_mediaElement; } + virtual bool isMediaControlElement() const { return true; } + protected: HTMLMediaElement* m_mediaElement; PseudoId m_pseudoStyleId; + MediaControlElementType m_displayType; // some elements can show multiple types (e.g. play/pause) }; // ---------------------------- @@ -104,6 +117,22 @@ public: // ---------------------------- +class MediaControlVolumeSliderContainerElement : public MediaControlElement { +public: + MediaControlVolumeSliderContainerElement(Document*, HTMLMediaElement*); + virtual PassRefPtr<RenderStyle> styleForElement(); + void setVisible(bool); + bool isVisible() { return m_isVisible; } + void setPosition(int x, int y); + bool hitTest(const IntPoint& absPoint); + +private: + bool m_isVisible; + int m_x, m_y; +}; + +// ---------------------------- + class MediaControlStatusDisplayElement : public MediaControlElement { public: MediaControlStatusDisplayElement(Document*, HTMLMediaElement*); @@ -118,7 +147,7 @@ private: class MediaControlInputElement : public HTMLInputElement { public: - MediaControlInputElement(Document*, PseudoId, const String& type, HTMLMediaElement*, MediaControlElementType); + MediaControlInputElement(Document*, PseudoId, const String& type, HTMLMediaElement*); virtual void attach(); virtual bool rendererIsNeeded(RenderStyle*); @@ -130,13 +159,16 @@ public: bool hitTest(const IntPoint& absPoint); MediaControlElementType displayType() const { return m_displayType; } + HTMLMediaElement* mediaElement() const { return m_mediaElement; } + virtual bool isMediaControlElement() const { return true; } + protected: virtual void updateDisplayType() { } void setDisplayType(MediaControlElementType); HTMLMediaElement* m_mediaElement; PseudoId m_pseudoStyleId; - MediaControlElementType m_displayType; // some elements can show multiple types (e.g. play/pause) + MediaControlElementType m_displayType; }; // ---------------------------- @@ -179,7 +211,6 @@ class MediaControlRewindButtonElement : public MediaControlInputElement { public: MediaControlRewindButtonElement(Document*, HTMLMediaElement*); virtual void defaultEventHandler(Event*); - virtual bool rendererIsNeeded(RenderStyle*); }; // ---------------------------- @@ -188,7 +219,15 @@ class MediaControlReturnToRealtimeButtonElement : public MediaControlInputElemen public: MediaControlReturnToRealtimeButtonElement(Document*, HTMLMediaElement*); virtual void defaultEventHandler(Event*); - virtual bool rendererIsNeeded(RenderStyle*); +}; + +// ---------------------------- + +class MediaControlToggleClosedCaptionsButtonElement : public MediaControlInputElement { +public: + MediaControlToggleClosedCaptionsButtonElement(Document*, HTMLMediaElement*); + virtual void defaultEventHandler(Event*); + virtual void updateDisplayType(); }; // ---------------------------- @@ -202,11 +241,19 @@ public: // ---------------------------- +class MediaControlVolumeSliderElement : public MediaControlInputElement { +public: + MediaControlVolumeSliderElement(Document*, HTMLMediaElement*); + virtual void defaultEventHandler(Event*); + virtual void update(); +}; + +// ---------------------------- + class MediaControlFullscreenButtonElement : public MediaControlInputElement { public: MediaControlFullscreenButtonElement(Document*, HTMLMediaElement*); virtual void defaultEventHandler(Event*); - virtual bool rendererIsNeeded(RenderStyle*); }; // ---------------------------- @@ -217,7 +264,11 @@ public: void setVisible(bool); virtual PassRefPtr<RenderStyle> styleForElement(); + void setCurrentValue(float); + float currentValue() const { return m_currentValue; } + private: + float m_currentValue; bool m_isVisible; }; diff --git a/WebCore/rendering/PointerEventsHitRules.cpp b/WebCore/rendering/PointerEventsHitRules.cpp index 214fb09..ababcfd 100644 --- a/WebCore/rendering/PointerEventsHitRules.cpp +++ b/WebCore/rendering/PointerEventsHitRules.cpp @@ -1,8 +1,6 @@ /* Copyright (C) 2007 Rob Buis <buis@kde.org> - This file is part of the KDE project - 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 diff --git a/WebCore/rendering/PointerEventsHitRules.h b/WebCore/rendering/PointerEventsHitRules.h index 3d8939a..c17c19c 100644 --- a/WebCore/rendering/PointerEventsHitRules.h +++ b/WebCore/rendering/PointerEventsHitRules.h @@ -1,8 +1,6 @@ /* Copyright (C) 2007 Rob Buis <buis@kde.org> - This file is part of the KDE project - 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 diff --git a/WebCore/rendering/RenderArena.cpp b/WebCore/rendering/RenderArena.cpp index 3ab4dff..41f33de 100644 --- a/WebCore/rendering/RenderArena.cpp +++ b/WebCore/rendering/RenderArena.cpp @@ -114,7 +114,7 @@ void RenderArena::free(size_t size, void* ptr) // Use standard free so that memory debugging tools work. RenderArenaDebugHeader* header = static_cast<RenderArenaDebugHeader*>(ptr) - 1; ASSERT(header->signature == signature); - ASSERT(header->size == size); + ASSERT_UNUSED(size, header->size == size); ASSERT(header->arena == this); header->signature = signatureDead; ::free(header); diff --git a/WebCore/rendering/RenderArena.h b/WebCore/rendering/RenderArena.h index ca35361..edf052a 100644 --- a/WebCore/rendering/RenderArena.h +++ b/WebCore/rendering/RenderArena.h @@ -36,12 +36,13 @@ #define RenderArena_h #include "Arena.h" +#include <wtf/Noncopyable.h> namespace WebCore { static const size_t gMaxRecycledSize = 400; -class RenderArena { +class RenderArena : public Noncopyable { public: RenderArena(unsigned arenaSize = 4096); ~RenderArena(); diff --git a/WebCore/rendering/RenderBR.cpp b/WebCore/rendering/RenderBR.cpp index f407099..340d6b7 100644 --- a/WebCore/rendering/RenderBR.cpp +++ b/WebCore/rendering/RenderBR.cpp @@ -1,6 +1,4 @@ /** - * This file is part of the DOM implementation for KDE. - * * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * Copyright (C) 2006 Apple Computer, Inc. * diff --git a/WebCore/rendering/RenderBR.h b/WebCore/rendering/RenderBR.h index 7eae8ea..8850d46 100644 --- a/WebCore/rendering/RenderBR.h +++ b/WebCore/rendering/RenderBR.h @@ -1,6 +1,4 @@ /* - * This file is part of the DOM implementation for KDE. - * * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * * This library is free software; you can redistribute it and/or diff --git a/WebCore/rendering/RenderBlock.cpp b/WebCore/rendering/RenderBlock.cpp index 73648d1..eabb054 100644 --- a/WebCore/rendering/RenderBlock.cpp +++ b/WebCore/rendering/RenderBlock.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -33,6 +33,7 @@ #include "HTMLNames.h" #include "HitTestResult.h" #include "InlineTextBox.h" +#include "RenderFlexibleBox.h" #include "RenderImage.h" #include "RenderInline.h" #include "RenderMarquee.h" @@ -43,6 +44,7 @@ #include "RenderView.h" #include "SelectionController.h" #include "Settings.h" +#include "TransformState.h" #include <wtf/StdLibExtras.h> #ifdef ANDROID_LAYOUT @@ -61,13 +63,7 @@ static 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 { +struct ColumnInfo : public Noncopyable { ColumnInfo() : m_desiredColumnWidth(0) , m_desiredColumnCount(1) @@ -118,8 +114,6 @@ RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int top, int bottom) m_posMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(true) : 0; m_negMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(false) : 0; - - m_selfCollapsingBlockClearedFloat = false; m_topQuirk = m_bottomQuirk = m_determinedTopQuirk = false; } @@ -132,10 +126,6 @@ RenderBlock::RenderBlock(Node* node) , 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); @@ -172,18 +162,18 @@ RenderBlock::~RenderBlock() void RenderBlock::destroy() { - // 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(); + // 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(); - // Destroy our continuation before anything other than anonymous children. - // The reason we don't destroy it before anonymous children is that they may - // have continuations of their own that are anonymous children of our continuation. - if (m_inlineContinuation) { + // Destroy our continuation before anything other than anonymous children. + // The reason we don't destroy it before anonymous children is that they may + // have continuations of their own that are anonymous children of our continuation. + if (m_inlineContinuation) { m_inlineContinuation->destroy(); - m_inlineContinuation = 0; + m_inlineContinuation = 0; } - + if (!documentBeingDestroyed()) { if (firstLineBox()) { // We can't wait for RenderBox::destroy to clear the selection, @@ -411,6 +401,44 @@ RootInlineBox* RenderBlock::createAndAppendRootInlineBox() m_lineBoxes.appendLineBox(rootBox); return rootBox; } + +void RenderBlock::moveChildTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* child) +{ + ASSERT(this == child->parent()); + toChildList->appendChildNode(to, children()->removeChildNode(this, child, false), false); +} + +void RenderBlock::moveChildTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* beforeChild, RenderObject* child) +{ + ASSERT(this == child->parent()); + ASSERT(!beforeChild || to == beforeChild->parent()); + toChildList->insertChildNode(to, children()->removeChildNode(this, child, false), beforeChild, false); +} + +void RenderBlock::moveAllChildrenTo(RenderObject* to, RenderObjectChildList* toChildList) +{ + RenderObject* nextChild = children()->firstChild(); + while (nextChild) { + RenderObject* child = nextChild; + nextChild = child->nextSibling(); + toChildList->appendChildNode(to, children()->removeChildNode(this, child, false), false); + } +} + +void RenderBlock::moveAllChildrenTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* beforeChild) +{ + ASSERT(!beforeChild || to == beforeChild->parent()); + if (!beforeChild) { + moveAllChildrenTo(to, toChildList); + return; + } + RenderObject* nextChild = children()->firstChild(); + while (nextChild) { + RenderObject* child = nextChild; + nextChild = child->nextSibling(); + toChildList->insertChildNode(to, children()->removeChildNode(this, child, false), beforeChild, false); + } +} void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) { @@ -448,9 +476,9 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) RenderObject* no = o; o = no->nextSibling(); - moveChild(block, block->children(), this, children(), no); + moveChildTo(block, block->children(), no); } - moveChild(block, block->children(), this, children(), inlineRunEnd); + moveChildTo(block, block->children(), inlineRunEnd); } #ifndef NDEBUG @@ -518,20 +546,12 @@ void RenderBlock::removeChild(RenderObject* oldChild) // Take all the children out of the |next| block and put them in // the |prev| block. prev->setNeedsLayoutAndPrefWidthsRecalc(); - RenderObject* o = next->firstChild(); - RenderBlock* nextBlock = toRenderBlock(next); RenderBlock* prevBlock = toRenderBlock(prev); - while (o) { - RenderObject* no = o; - o = no->nextSibling(); - moveChild(prevBlock, prevBlock->children(), nextBlock, nextBlock->children(), no); - } - + nextBlock->moveAllChildrenTo(prevBlock, prevBlock->children()); + // Delete the now-empty block's lines and nuke it. nextBlock->deleteLineBoxTree(); - - // Nuke the now-empty block. - next->destroy(); + nextBlock->destroy(); } RenderBox::removeChild(oldChild); @@ -544,114 +564,15 @@ void RenderBlock::removeChild(RenderObject* oldChild) setNeedsLayoutAndPrefWidthsRecalc(); RenderBlock* anonBlock = toRenderBlock(children()->removeChildNode(this, child, false)); setChildrenInline(true); - RenderObject* o = anonBlock->firstChild(); - while (o) { - RenderObject* no = o; - o = no->nextSibling(); - moveChild(this, children(), anonBlock, anonBlock->children(), no); - } - + anonBlock->moveAllChildrenTo(this, children()); // Delete the now-empty block's lines and nuke it. anonBlock->deleteLineBoxTree(); anonBlock->destroy(); } -} - -int RenderBlock::overflowHeight(bool includeInterior) const -{ - if (!includeInterior && hasOverflowClip()) { - int shadowTop; - int shadowBottom; - style()->getBoxShadowVerticalExtent(shadowTop, shadowBottom); - - int inflatedHeight = height() + shadowBottom; - if (hasReflection()) - inflatedHeight = max(inflatedHeight, reflectionBox().bottom()); - return inflatedHeight; - } - return m_overflowHeight; -} - -int RenderBlock::overflowWidth(bool includeInterior) const -{ - if (!includeInterior && hasOverflowClip()) { - int shadowLeft; - int shadowRight; - style()->getBoxShadowHorizontalExtent(shadowLeft, shadowRight); - - int inflatedWidth = width() + shadowRight; - if (hasReflection()) - inflatedWidth = max(inflatedWidth, reflectionBox().right()); - return inflatedWidth; - } - return m_overflowWidth; -} - -int RenderBlock::overflowLeft(bool includeInterior) const -{ - if (!includeInterior && hasOverflowClip()) { - int shadowLeft; - int shadowRight; - style()->getBoxShadowHorizontalExtent(shadowLeft, shadowRight); - - int left = shadowLeft; - if (hasReflection()) - left = min(left, reflectionBox().x()); - return left; - } - return m_overflowLeft; -} - -int RenderBlock::overflowTop(bool includeInterior) const -{ - if (!includeInterior && hasOverflowClip()) { - int shadowTop; - int shadowBottom; - style()->getBoxShadowVerticalExtent(shadowTop, shadowBottom); - - int top = shadowTop; - if (hasReflection()) - top = min(top, reflectionBox().y()); - return top; - } - return m_overflowTop; -} - -IntRect RenderBlock::overflowRect(bool includeInterior) const -{ - if (!includeInterior && hasOverflowClip()) { - IntRect box = borderBoxRect(); - - int shadowLeft; - int shadowRight; - int shadowTop; - int shadowBottom; - style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); - - box.move(shadowLeft, shadowTop); - box.setWidth(box.width() - shadowLeft + shadowRight); - box.setHeight(box.height() - shadowTop + shadowBottom); - - if (hasReflection()) { - IntRect reflection(reflectionBox()); - int reflectTop = min(box.y(), reflection.y()); - int reflectBottom = max(box.bottom(), reflection.bottom()); - box.setHeight(reflectBottom - reflectTop); - box.setY(reflectTop); - - int reflectLeft = min(box.x(), reflection.x()); - int reflectRight = max(box.right(), reflection.right()); - box.setWidth(reflectRight - reflectLeft); - box.setX(reflectLeft); - } - return box; - } - if (!includeInterior && hasOverflowClip()) - return borderBoxRect(); - int l = overflowLeft(includeInterior); - int t = overflowTop(includeInterior); - return IntRect(l, t, overflowWidth(includeInterior) - l, max(overflowHeight(includeInterior), height()) - t); + // If this was our last child be sure to clear out our line boxes. + if (childrenInline() && !firstChild()) + lineBoxes()->deleteLineBoxes(renderArena()); } bool RenderBlock::isSelfCollapsingBlock() const @@ -715,15 +636,15 @@ void RenderBlock::finishDelayUpdateScrollInfo() if (gDelayUpdateScrollInfo == 0) { ASSERT(gDelayedUpdateScrollInfoSet); - for (DelayedUpdateScrollInfoSet::iterator it = gDelayedUpdateScrollInfoSet->begin(); it != gDelayedUpdateScrollInfoSet->end(); ++it) { + OwnPtr<DelayedUpdateScrollInfoSet> infoSet(gDelayedUpdateScrollInfoSet); + gDelayedUpdateScrollInfoSet = 0; + + for (DelayedUpdateScrollInfoSet::iterator it = infoSet->begin(); it != infoSet->end(); ++it) { RenderBlock* block = *it; if (block->hasOverflowClip()) { block->layer()->updateScrollInfoAfterLayout(); } } - - delete gDelayedUpdateScrollInfoSet; - gDelayedUpdateScrollInfoSet = 0; } } @@ -747,13 +668,9 @@ void RenderBlock::layout() layoutBlock(false); // It's safe to check for control clip here, since controls can never be table cells. - if (hasControlClip()) { - // Because of the lightweight clip, there can never be any overflow from children. - m_overflowWidth = width(); - m_overflowHeight = height(); - m_overflowLeft = 0; - m_overflowTop = 0; - } + // If we have a lightweight clip, there can never be any overflow from children. + if (hasControlClip() && m_overflow) + clearLayoutOverflow(); } void RenderBlock::layoutBlock(bool relayoutChildren) @@ -779,8 +696,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren) calcWidth(); calcColumnWidth(); - m_overflowWidth = width(); - m_overflowLeft = 0; + m_overflow.clear(); if (oldWidth != width() || oldColumnWidth != desiredColumnWidth()) relayoutChildren = true; @@ -798,8 +714,6 @@ void RenderBlock::layoutBlock(bool relayoutChildren) int previousHeight = height(); setHeight(0); - m_overflowHeight = 0; - // We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg, to track // our current maximal positive and negative margins. These values are used when we // are collapsed with adjacent blocks, so for example, if you have block A and B @@ -864,50 +778,35 @@ void RenderBlock::layoutBlock(bool relayoutChildren) } } } + // We have to rebalance columns to the new height. layoutColumns(singleColumnBottom); - - // If the block got expanded in size, then increase our overflowheight to match. - if (m_overflowHeight > height()) - m_overflowHeight -= toAdd; - if (m_overflowHeight < height()) - m_overflowHeight = height(); } + if (previousHeight != height()) relayoutChildren = true; - if ((isCell || isInline() || isFloatingOrPositioned() || isRoot()) && !hasOverflowClip() && !hasControlClip()) - addVisualOverflow(floatRect()); + // It's weird that we're treating float information as normal flow overflow, but we do this because floatRect() isn't + // able to be propagated up the render tree yet. Overflow information is however. This check is designed to catch anyone + // who wasn't going to propagate float information up to the parent and yet could potentially be painted by its ancestor. + if (isRoot() || expandsToEncloseOverhangingFloats()) + addOverflowFromFloats(); - layoutPositionedObjects(relayoutChildren || isRoot()); + // Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway). + if (!hasColumns()) { + if (childrenInline()) + addOverflowFromInlineChildren(); + else + addOverflowFromBlockChildren(); + } - positionListMarker(); + // Add visual overflow from box-shadow and reflections. + addShadowOverflow(); - // Always ensure our overflow width/height are at least as large as our width/height. - m_overflowWidth = max(m_overflowWidth, width()); - m_overflowHeight = max(m_overflowHeight, height()); - - if (!hasOverflowClip()) { - int shadowLeft; - int shadowRight; - int shadowTop; - int shadowBottom; - style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); - - m_overflowLeft = min(m_overflowLeft, shadowLeft); - m_overflowWidth = max(m_overflowWidth, width() + shadowRight); - m_overflowTop = min(m_overflowTop, shadowTop); - m_overflowHeight = max(m_overflowHeight, height() + shadowBottom); - - if (hasReflection()) { - IntRect reflection(reflectionBox()); - m_overflowLeft = min(m_overflowLeft, reflection.x()); - m_overflowWidth = max(m_overflowWidth, reflection.right()); - m_overflowTop = min(m_overflowTop, reflection.y()); - m_overflowHeight = max(m_overflowHeight, reflection.bottom()); - } - } + layoutPositionedObjects(relayoutChildren || isRoot()); + positionListMarker(); + statePusher.pop(); // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if @@ -917,7 +816,9 @@ void RenderBlock::layoutBlock(bool relayoutChildren) // Repaint with our new bounds if they are different from our old bounds. bool didFullRepaint = repainter.repaintAfterLayout(); if (!didFullRepaint && repaintTop != repaintBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) { - IntRect repaintRect(m_overflowLeft, repaintTop, m_overflowWidth - m_overflowLeft, repaintBottom - repaintTop); + int repaintLeft = min(leftVisualOverflow(), leftLayoutOverflow()); + int repaintRight = max(rightVisualOverflow(), rightLayoutOverflow()); + IntRect repaintRect(repaintLeft, repaintTop, repaintRight - repaintLeft, repaintBottom - repaintTop); // FIXME: Deal with multiple column repainting. We have to split the repaint // rect up into multiple rects if it spans columns. @@ -946,6 +847,28 @@ void RenderBlock::layoutBlock(bool relayoutChildren) setNeedsLayout(false); } +void RenderBlock::addOverflowFromBlockChildren() +{ + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (!child->isFloatingOrPositioned()) + addOverflowFromChild(child); + } +} + +void RenderBlock::addOverflowFromFloats() +{ + IntRect result; + if (!m_floatingObjects) + return; + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for (; (r = it.current()); ++it) { + if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) + addOverflowFromChild(r->m_renderer, IntSize(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop())); + } + return; +} + bool RenderBlock::expandsToEncloseOverhangingFloats() const { return isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBox()) || hasColumns() || isTableCell() || isFieldset(); @@ -976,7 +899,11 @@ void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marg } y += (collapsedTopPos - collapsedTopNeg) - marginTop; } - child->layer()->setStaticY(y); + RenderLayer* childLayer = child->layer(); + if (childLayer->staticY() != y) { + child->layer()->setStaticY(y); + child->setChildNeedsLayout(true, false); + } } } @@ -1035,7 +962,10 @@ bool RenderBlock::handleRunInChild(RenderBox* child) // 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 (!child->isRunIn() || !child->childrenInline() && !child->isReplaced()) + if (!child->isRunIn() || !child->childrenInline()) + return false; + // FIXME: We don't handle non-block elements with run-in for now. + if (!child->isRenderBlock()) return false; // Get the next non-positioned/non-floating RenderBlock. @@ -1062,7 +992,7 @@ bool RenderBlock::handleRunInChild(RenderBox* child) // 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 (runInIsGenerated || runInChild->style()->styleType() != BEFORE && runInChild->style()->styleType() != AFTER) { + if (runInIsGenerated || (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. } @@ -1169,8 +1099,6 @@ int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) if (marginInfo.margin()) marginInfo.setBottomQuirk(child->isBottomMarginQuirk() || style()->marginBottomCollapse() == MDISCARD); - - marginInfo.setSelfCollapsingBlockClearedFloat(false); } return ypos; @@ -1186,16 +1114,26 @@ int RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, i // For self-collapsing blocks that clear, they can still collapse their // margins with following siblings. Reset the current margins to represent // the self-collapsing block's margins only. - marginInfo.setPosMargin(max(child->maxTopMargin(true), child->maxBottomMargin(true))); - marginInfo.setNegMargin(max(child->maxTopMargin(false), child->maxBottomMargin(false))); + // CSS2.1 states: + // "An element that has had clearance applied to it never collapses its top margin with its parent block's bottom margin. + // Therefore if we are at the bottom of the block, let's go ahead and reset margins to only include the + // self-collapsing block's bottom margin. + bool atBottomOfBlock = true; + for (RenderBox* curr = child->nextSiblingBox(); curr && atBottomOfBlock; curr = curr->nextSiblingBox()) { + if (!curr->isFloatingOrPositioned()) + atBottomOfBlock = false; + } + if (atBottomOfBlock) { + marginInfo.setPosMargin(child->maxBottomMargin(true)); + marginInfo.setNegMargin(child->maxBottomMargin(false)); + } else { + marginInfo.setPosMargin(max(child->maxTopMargin(true), child->maxBottomMargin(true))); + marginInfo.setNegMargin(max(child->maxTopMargin(false), child->maxBottomMargin(false))); + } - // Adjust our height such that we are ready to be collapsed with subsequent siblings. + // Adjust our height such that we are ready to be collapsed with subsequent siblings (or the bottom + // of the parent block). setHeight(child->y() - max(0, marginInfo.margin())); - - // Set a flag that we cleared a float so that we know both to increase the height of the block - // to compensate for the clear and to avoid collapsing our margins with the parent block's - // bottom margin. - marginInfo.setSelfCollapsingBlockClearedFloat(true); } else // Increase our height by the amount we had to clear. setHeight(height() + heightIncrease); @@ -1299,17 +1237,7 @@ void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) void RenderBlock::handleBottomOfBlock(int top, int bottom, MarginInfo& marginInfo) { - // If our last flow was a self-collapsing block that cleared a float, then we don't - // collapse it with the bottom of the block. - if (!marginInfo.selfCollapsingBlockClearedFloat()) - marginInfo.setAtBottomOfBlock(true); - else { - // We have to special case the negative margin situation (where the collapsed - // margin of the self-collapsing block is negative), since there's no need - // to make an adjustment in that case. - if (marginInfo.margin() < 0) - marginInfo.clearMargin(); - } + marginInfo.setAtBottomOfBlock(true); // If we can't collapse with children then go ahead and add in the bottom margin. if (!marginInfo.canCollapseWithBottom() && !marginInfo.canCollapseWithTop() @@ -1323,9 +1251,6 @@ void RenderBlock::handleBottomOfBlock(int top, int bottom, MarginInfo& marginInf // If this happens, ensure that the computed height is increased to the minimal height. setHeight(max(height(), top + bottom)); - // Always make sure our overflow height is at least our height. - m_overflowHeight = max(height(), m_overflowHeight); - // Update our bottom collapsed margin info. setCollapsedBottomMargin(marginInfo); } @@ -1353,8 +1278,7 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom int top = borderTop() + paddingTop(); int bottom = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); - m_overflowHeight = top; - setHeight(m_overflowHeight); + setHeight(top); // The margin struct caches all our current margin collapsing state. The compact struct caches state when we encounter compacts, MarginInfo marginInfo(this, top, bottom); @@ -1375,9 +1299,6 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom if (legend == child) continue; // Skip the legend, since it has already been positioned up in the fieldset's border. - int oldTopPosMargin = maxTopPosMargin(); - int oldTopNegMargin = maxTopNegMargin(); - // Make sure we layout children if they need it. // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into // an auto value. Add a method to determine this, so that we can avoid the relayout. @@ -1393,127 +1314,127 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom if (handleSpecialChild(child, marginInfo)) continue; - // The child is a normal flow object. Compute its vertical margins now. - child->calcVerticalMargins(); + // Lay out the child. + layoutBlockChild(child, marginInfo, previousFloatBottom, maxFloatBottom); + } + + // Now do the handling of the bottom of the block, adding in our bottom border/padding and + // determining the correct collapsed bottom margin information. + handleBottomOfBlock(top, bottom, marginInfo); +} - // Do not allow a collapse if the margin top collapse style is set to SEPARATE. - if (child->style()->marginTopCollapse() == MSEPARATE) { - marginInfo.setAtTopOfBlock(false); - marginInfo.clearMargin(); - } +void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int& previousFloatBottom, int& maxFloatBottom) +{ + int oldTopPosMargin = maxTopPosMargin(); + int oldTopNegMargin = maxTopNegMargin(); - // Try to guess our correct y position. In most cases this guess will - // be correct. Only if we're wrong (when we compute the real y position) - // will we have to potentially relayout. - int yPosEstimate = estimateVerticalPosition(child, marginInfo); + // The child is a normal flow object. Compute its vertical margins now. + child->calcVerticalMargins(); - // Cache our old rect so that we can dirty the proper repaint rects if the child moves. - IntRect oldRect(child->x(), child->y() , child->width(), child->height()); + // Do not allow a collapse if the margin top collapse style is set to SEPARATE. + if (child->style()->marginTopCollapse() == MSEPARATE) { + marginInfo.setAtTopOfBlock(false); + marginInfo.clearMargin(); + } + + // Try to guess our correct y position. In most cases this guess will + // be correct. Only if we're wrong (when we compute the real y position) + // will we have to potentially relayout. + int yPosEstimate = estimateVerticalPosition(child, marginInfo); + + // Cache our old rect so that we can dirty the proper repaint rects if the child moves. + IntRect oldRect(child->x(), child->y() , child->width(), child->height()); #ifndef NDEBUG - IntSize oldLayoutDelta = view()->layoutDelta(); + IntSize oldLayoutDelta = view()->layoutDelta(); #endif - // Go ahead and position the child as though it didn't collapse with the top. - view()->addLayoutDelta(IntSize(0, child->y() - yPosEstimate)); - child->setLocation(child->x(), yPosEstimate); - - bool markDescendantsWithFloats = false; - if (yPosEstimate != oldRect.y() && !child->avoidsFloats() && child->isBlockFlow() && toRenderBlock(child)->containsFloats()) + // Go ahead and position the child as though it didn't collapse with the top. + view()->addLayoutDelta(IntSize(0, child->y() - yPosEstimate)); + child->setLocation(child->x(), yPosEstimate); + + bool markDescendantsWithFloats = false; + 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 > yPosEstimate) markDescendantsWithFloats = true; - else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) { - // If an element might be affected by the presence of floats, then always mark it for - // layout. - int fb = max(previousFloatBottom, floatBottom()); - if (fb > yPosEstimate) - markDescendantsWithFloats = true; - } + } - if (child->isRenderBlock()) { - if (markDescendantsWithFloats) - toRenderBlock(child)->markAllDescendantsWithFloatsForLayout(); + if (child->isRenderBlock()) { + if (markDescendantsWithFloats) + toRenderBlock(child)->markAllDescendantsWithFloatsForLayout(); - previousFloatBottom = max(previousFloatBottom, oldRect.y() + toRenderBlock(child)->floatBottom()); - } + previousFloatBottom = max(previousFloatBottom, oldRect.y() + toRenderBlock(child)->floatBottom()); + } - bool childHadLayout = child->m_everHadLayout; - bool childNeededLayout = child->needsLayout(); - if (childNeededLayout) - child->layout(); + bool childHadLayout = child->m_everHadLayout; + bool childNeededLayout = child->needsLayout(); + if (childNeededLayout) + child->layout(); - // Now determine the correct ypos based off examination of collapsing margin - // values. - int yBeforeClear = collapseMargins(child, marginInfo); + // Now determine the correct ypos based off examination of collapsing margin + // values. + int yBeforeClear = collapseMargins(child, marginInfo); - // Now check for clear. - int yAfterClear = clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin, yBeforeClear); - - view()->addLayoutDelta(IntSize(0, yPosEstimate - yAfterClear)); - child->setLocation(child->x(), yAfterClear); + // Now check for clear. + int yAfterClear = clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin, yBeforeClear); - // 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(); + 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. - if (marginInfo.atTopOfBlock() && !child->isSelfCollapsingBlock()) - marginInfo.setAtTopOfBlock(false); - - // Now place the child in the correct horizontal position - determineHorizontalPosition(child); + // 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. + if (marginInfo.atTopOfBlock() && !child->isSelfCollapsingBlock()) + marginInfo.setAtTopOfBlock(false); - // Update our height now that the child has been placed in the correct position. - setHeight(height() + child->height()); - if (child->style()->marginBottomCollapse() == MSEPARATE) { - setHeight(height() + child->marginBottom()); - marginInfo.clearMargin(); - } - // If the child has overhanging floats that intrude into following siblings (or possibly out - // of this block), then the parent gets notified of the floats now. - if (child->isBlockFlow() && toRenderBlock(child)->containsFloats()) - maxFloatBottom = max(maxFloatBottom, addOverhangingFloats(toRenderBlock(child), -child->x(), -child->y(), !childNeededLayout)); - - // Update our visual overflow in case the child spills out the block, but only if we were going to paint - // the child block ourselves. - if (!child->hasSelfPaintingLayer()) { - m_overflowTop = min(m_overflowTop, child->y() + child->overflowTop(false)); - m_overflowHeight = max(m_overflowHeight, height() + child->overflowHeight(false) - child->height()); - m_overflowWidth = max(child->x() + child->overflowWidth(false), m_overflowWidth); - m_overflowLeft = min(child->x() + child->overflowLeft(false), m_overflowLeft); - } + // Now place the child in the correct horizontal position + determineHorizontalPosition(child); - IntSize childOffset(child->x() - oldRect.x(), child->y() - oldRect.y()); - if (childOffset.width() || childOffset.height()) { - view()->addLayoutDelta(childOffset); + // Update our height now that the child has been placed in the correct position. + setHeight(height() + child->height()); + if (child->style()->marginBottomCollapse() == MSEPARATE) { + setHeight(height() + child->marginBottom()); + marginInfo.clearMargin(); + } + // If the child has overhanging floats that intrude into following siblings (or possibly out + // of this block), then the parent gets notified of the floats now. + if (child->isBlockFlow() && toRenderBlock(child)->containsFloats()) + maxFloatBottom = max(maxFloatBottom, addOverhangingFloats(toRenderBlock(child), -child->x(), -child->y(), !childNeededLayout)); - // If the child moved, we have to repaint it as well as any floating/positioned - // descendants. An exception is if we need a layout. In this case, we know we're going to - // repaint ourselves (and the child) anyway. - if (childHadLayout && !selfNeedsLayout() && child->checkForRepaintDuringLayout()) - child->repaintDuringLayoutIfMoved(oldRect); - } + IntSize childOffset(child->x() - oldRect.x(), child->y() - oldRect.y()); + if (childOffset.width() || childOffset.height()) { + view()->addLayoutDelta(childOffset); - if (!childHadLayout && child->checkForRepaintDuringLayout()) { - child->repaint(); - child->repaintOverhangingFloats(true); - } + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (childHadLayout && !selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldRect); + } - ASSERT(oldLayoutDelta == view()->layoutDelta()); + if (!childHadLayout && child->checkForRepaintDuringLayout()) { + child->repaint(); + child->repaintOverhangingFloats(true); } - // Now do the handling of the bottom of the block, adding in our bottom border/padding and - // determining the correct collapsed bottom margin information. - handleBottomOfBlock(top, bottom, marginInfo); + ASSERT(oldLayoutDelta == view()->layoutDelta()); } bool RenderBlock::layoutOnlyPositionedObjects() @@ -1537,7 +1458,7 @@ bool RenderBlock::layoutOnlyPositionedObjects() updateScrollInfoAfterLayout(); #ifdef ANDROID_FIX - // iframe flatten will call FrameView::layout() which calls performPostLayoutTasks, + // iframe flatten will call FrameView::layout() which calls performPostLayoutTasks, // which may make us need to layout again if (!posChildNeedsLayout() || normalChildNeedsLayout() || selfNeedsLayout()) return false; @@ -1626,7 +1547,7 @@ void RenderBlock::paint(PaintInfo& paintInfo, int tx, int ty) // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView // paints the root's background. if (!isRoot()) { - IntRect overflowBox = overflowRect(false); + IntRect overflowBox = visibleOverflowRect(); overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); overflowBox.move(tx, ty); if (!overflowBox.intersects(paintInfo.rect)) @@ -1641,7 +1562,7 @@ void RenderBlock::paint(PaintInfo& paintInfo, int tx, int 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. - if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground)) + if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && shouldPaintWithinRoot(paintInfo)) layer()->paintOverflowControls(paintInfo.context, tx, ty, paintInfo.rect); } @@ -1792,7 +1713,7 @@ void RenderBlock::paintCaret(PaintInfo& paintInfo, int tx, int ty, CaretType typ offsetForContents(tx, ty); if (type == CursorCaret) - document()->frame()->paintCaret(paintInfo.context, tx, ty, paintInfo.rect); + document()->frame()->selection()->paintCaret(paintInfo.context, tx, ty, paintInfo.rect); else document()->frame()->paintDragCaret(paintInfo.context, tx, ty, paintInfo.rect); } @@ -2025,23 +1946,28 @@ bool RenderBlock::isSelectionRoot() const return false; } -GapRects RenderBlock::selectionGapRectsForRepaint(RenderBoxModelObject* /*repaintContainer*/) +GapRects RenderBlock::selectionGapRectsForRepaint(RenderBoxModelObject* repaintContainer) { ASSERT(!needsLayout()); if (!shouldPaintSelectionGaps()) return GapRects(); - // FIXME: this is broken with transforms and a non-null repaintContainer - FloatPoint absContentPoint = localToAbsolute(FloatPoint()); + // FIXME: this is broken with transforms + TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint()); + mapLocalToContainer(repaintContainer, false, false, transformState); + FloatPoint offsetFromRepaintContainer = transformState.mappedPoint(); + int x = offsetFromRepaintContainer.x(); + int y = offsetFromRepaintContainer.y(); + if (hasOverflowClip()) - absContentPoint -= layer()->scrolledContentOffset(); + layer()->subtractScrolledContentOffset(x, y); int lastTop = 0; int lastLeft = leftSelectionOffset(this, lastTop); int lastRight = rightSelectionOffset(this, lastTop); - return fillSelectionGaps(this, absContentPoint.x(), absContentPoint.y(), absContentPoint.x(), absContentPoint.y(), lastTop, lastLeft, lastRight); + return fillSelectionGaps(this, x, y, x, y, lastTop, lastLeft, lastRight); } void RenderBlock::paintSelection(PaintInfo& paintInfo, int tx, int ty) @@ -2051,7 +1977,17 @@ void RenderBlock::paintSelection(PaintInfo& paintInfo, int tx, int ty) int lastLeft = leftSelectionOffset(this, lastTop); int lastRight = rightSelectionOffset(this, lastTop); paintInfo.context->save(); - fillSelectionGaps(this, tx, ty, tx, ty, lastTop, lastLeft, lastRight, &paintInfo); + IntRect gapRectsBounds = fillSelectionGaps(this, tx, ty, tx, ty, lastTop, lastLeft, lastRight, &paintInfo); + if (!gapRectsBounds.isEmpty()) { + if (RenderLayer* layer = enclosingLayer()) { + gapRectsBounds.move(IntSize(-tx, -ty)); + if (!hasLayer()) { + FloatRect localBounds(gapRectsBounds); + gapRectsBounds = localToContainerQuad(localBounds, layer->renderer()).enclosingBoundingBox(); + } + layer->addBlockSelectionGapsBounds(gapRectsBounds); + } + } paintInfo.context->restore(); } } @@ -2163,9 +2099,9 @@ GapRects RenderBlock::fillInlineSelectionGaps(RenderBlock* rootBlock, int blockX if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) { // Go ahead and update our lastY to be the bottom of the last selected line. - lastTop = (ty - blockY) + lastSelectedLine->bottomOverflow(); - lastLeft = leftSelectionOffset(rootBlock, lastSelectedLine->bottomOverflow()); - lastRight = rightSelectionOffset(rootBlock, lastSelectedLine->bottomOverflow()); + lastTop = (ty - blockY) + lastSelectedLine->selectionBottom(); + lastLeft = leftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); + lastRight = rightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); } return result; } @@ -2238,7 +2174,7 @@ IntRect RenderBlock::fillHorizontalSelectionGap(RenderObject* selObj, int xPos, return IntRect(); IntRect gapRect(xPos, yPos, width, height); if (paintInfo && selObj->style()->visibility() == VISIBLE) - paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor()); + paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor(), selObj->style()->colorSpace()); return gapRect; } @@ -2259,7 +2195,7 @@ IntRect RenderBlock::fillVerticalSelectionGap(int lastTop, int lastLeft, int las IntRect gapRect(left, top, width, height); if (paintInfo) - paintInfo->context->fillRect(gapRect, selectionBackgroundColor()); + paintInfo->context->fillRect(gapRect, selectionBackgroundColor(), style()->colorSpace()); return gapRect; } @@ -2275,7 +2211,7 @@ IntRect RenderBlock::fillLeftSelectionGap(RenderObject* selObj, int xPos, int yP IntRect gapRect(left, top, width, height); if (paintInfo) - paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor()); + paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor(), selObj->style()->colorSpace()); return gapRect; } @@ -2291,7 +2227,7 @@ IntRect RenderBlock::fillRightSelectionGap(RenderObject* selObj, int xPos, int y IntRect gapRect(left, top, width, height); if (paintInfo) - paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor()); + paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor(), selObj->style()->colorSpace()); return gapRect; } @@ -2716,13 +2652,13 @@ RenderBlock::floatBottom() const IntRect RenderBlock::floatRect() const { IntRect result; - if (!m_floatingObjects || hasOverflowClip()) + if (!m_floatingObjects || hasOverflowClip() || hasColumns()) return result; FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for (; (r = it.current()); ++it) { if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) { - IntRect childRect = r->m_renderer->overflowRect(false); + IntRect childRect = r->m_renderer->visibleOverflowRect(); childRect.move(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop()); result.unite(childRect); } @@ -2733,10 +2669,14 @@ IntRect RenderBlock::floatRect() const int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) const { + int bottom = includeSelf && width() > 0 ? height() : 0; + if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) - return includeSelf && width() > 0 ? overflowHeight(false) : 0; + return bottom; + + if (!firstChild() && (!width() || !height())) + return bottom; - int bottom = includeSelf && width() > 0 ? height() : 0; if (!hasColumns()) { // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. // For now, we have to descend into all the children, since we may have a huge abs div inside @@ -2760,7 +2700,7 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) int relativeOffset = includeSelf && isRelPositioned() ? relativePositionOffsetY() : 0; if (includeSelf) - bottom = max(bottom, m_overflowHeight + relativeOffset); + bottom = max(bottom, bottomLayoutOverflow() + relativeOffset); if (m_positionedObjects) { RenderBox* r; @@ -2802,8 +2742,8 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) if (!includeSelf) { bottom = max(bottom, borderTop() + paddingTop() + paddingBottom() + relativeOffset); if (childrenInline()) { - if (lastLineBox()) { - int childBottomEdge = lastLineBox()->y() + lastLineBox()->height(); + if (lastRootBox()) { + int childBottomEdge = lastRootBox()->selectionBottom(); bottom = max(bottom, childBottomEdge + paddingBottom() + relativeOffset); } } else { @@ -2823,10 +2763,13 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const { + int right = includeSelf && height() > 0 ? width() : 0; + if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) - return includeSelf && height() > 0 ? overflowWidth(false) : 0; + return right; - int right = includeSelf && height() > 0 ? width() : 0; + if (!firstChild() && (!width() || !height())) + return right; if (!hasColumns()) { // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. @@ -2850,7 +2793,7 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel int relativeOffset = includeSelf && isRelPositioned() ? relativePositionOffsetX() : 0; if (includeSelf) - right = max(right, m_overflowWidth + relativeOffset); + right = max(right, rightLayoutOverflow() + relativeOffset); if (m_positionedObjects) { RenderBox* r; @@ -2917,10 +2860,14 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const { + int left = includeSelf && height() > 0 ? 0 : width(); + if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) - return includeSelf && height() > 0 ? overflowLeft(false) : width(); + return left; + + if (!firstChild() && (!width() || !height())) + return left; - int left = includeSelf && height() > 0 ? 0 : width(); if (!hasColumns()) { // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. // For now, we have to descend into all the children, since we may have a huge abs div inside @@ -2943,7 +2890,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf int relativeOffset = includeSelf && isRelPositioned() ? relativePositionOffsetX() : 0; if (includeSelf) - left = min(left, m_overflowLeft + relativeOffset); + left = min(left, leftLayoutOverflow() + relativeOffset); if (m_positionedObjects) { RenderBox* r; @@ -3018,7 +2965,7 @@ RenderBlock::rightBottom() return bottom; } -void RenderBlock::markLinesDirtyInVerticalRange(int top, int bottom) +void RenderBlock::markLinesDirtyInVerticalRange(int top, int bottom, RootInlineBox* highest) { if (top >= bottom) return; @@ -3030,7 +2977,7 @@ void RenderBlock::markLinesDirtyInVerticalRange(int top, int bottom) lowestDirtyLine = lowestDirtyLine->prevRootBox(); } - while (afterLowest && afterLowest->blockHeight() >= top) { + while (afterLowest && afterLowest != highest && afterLowest->blockHeight() >= top) { afterLowest->markDirty(); afterLowest = afterLowest->prevRootBox(); } @@ -3137,9 +3084,8 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bo int lowestFloatBottom = 0; - // Floats that will remain the child's responsiblity to paint should factor into its - // visual overflow. - IntRect floatsOverflowRect; + // Floats that will remain the child's responsibility to paint should factor into its + // overflow. DeprecatedPtrListIterator<FloatingObject> it(*child->m_floatingObjects); for (FloatingObject* r; (r = it.current()); ++it) { int bottom = child->y() + r->m_bottom; @@ -3180,13 +3126,9 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bo // it should paint. r->m_shouldPaint = true; - 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); - } + if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) + child->addOverflowFromChild(r->m_renderer, IntSize(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop())); } - child->addVisualOverflow(floatsOverflowRect); return lowestFloatBottom; } @@ -3299,28 +3241,39 @@ int RenderBlock::getClearDelta(RenderBox* child, int yPos) } // We also clear floats if we are too big to sit on the same line as a float (and wish to avoid floats by default). - // FIXME: Note that the remaining space checks aren't quite accurate, since you should be able to clear only some floats (the minimum # needed - // to fit) and not all (we should be using nextFloatBottomBelow and looping). - // 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 - yPos) : 0; - if (!result && child->avoidsFloats() && child->style()->width().isFixed() && - child->minPrefWidth() > lineWidth(yPos, false) && child->minPrefWidth() <= availableWidth() && - document()->inStrictMode()) - result = max(0, floatBottom() - yPos); + if (!result && child->avoidsFloats()) { + int availableWidth = this->availableWidth(); + if (child->minPrefWidth() > availableWidth) + return 0; + + int y = yPos; + while (true) { + int widthAtY = lineWidth(y, false); + if (widthAtY == availableWidth) + return y - yPos; + + int oldChildY = child->y(); + int oldChildWidth = child->width(); + child->setY(y); + child->calcWidth(); + int childWidthAtY = child->width(); + child->setY(oldChildY); + child->setWidth(oldChildWidth); + + if (childWidthAtY <= widthAtY) + return y - yPos; + + y = nextFloatBottomBelow(y); + ASSERT(y >= yPos); + if (y < yPos) + break; + } + ASSERT_NOT_REACHED(); + } return result; } -void RenderBlock::addVisualOverflow(const IntRect& r) -{ - if (r.isEmpty()) - return; - m_overflowLeft = min(m_overflowLeft, r.x()); - m_overflowWidth = max(m_overflowWidth, r.right()); - m_overflowTop = min(m_overflowTop, r.y()); - m_overflowHeight = max(m_overflowHeight, r.bottom()); -} - bool RenderBlock::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty) { if (!scrollsOverflow()) @@ -3336,7 +3289,7 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu if (!isRenderView()) { // Check if we need to do anything at all. - IntRect overflowBox = overflowRect(false); + IntRect overflowBox = visibleOverflowRect(); overflowBox.move(tx, ty); if (!overflowBox.contains(_x, _y)) return false; @@ -3533,7 +3486,7 @@ VisiblePosition RenderBlock::positionForPointWithInlineChildren(const IntPoint& RootInlineBox* firstRootBoxWithChildren = 0; RootInlineBox* lastRootBoxWithChildren = 0; for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { - if (!root->firstChild()) + if (!root->firstLeafChild()) continue; if (!firstRootBoxWithChildren) firstRootBoxWithChildren = root; @@ -3545,9 +3498,9 @@ VisiblePosition RenderBlock::positionForPointWithInlineChildren(const IntPoint& if (root->nextRootBox()) { // FIXME: We would prefer to 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(); + bottom = root->nextRootBox()->lineTop(); } else - bottom = root->bottomOverflow() + verticalLineClickFudgeFactor; + bottom = root->lineBottom() + verticalLineClickFudgeFactor; // check if this root line box is located at this y coordinate if (pointInContents.y() < bottom) { @@ -3566,7 +3519,7 @@ VisiblePosition RenderBlock::positionForPointWithInlineChildren(const IntPoint& } if (closestBox) { - if (!useWindowsBehavior && pointInContents.y() < firstRootBoxWithChildren->topOverflow() - verticalLineClickFudgeFactor) { + if (!useWindowsBehavior && pointInContents.y() < firstRootBoxWithChildren->lineTop() - verticalLineClickFudgeFactor) { // y coordinate is above first root line box, so return the start of the first return VisiblePosition(positionForBox(firstRootBoxWithChildren->firstLeafChild(), true), DOWNSTREAM); } @@ -3680,11 +3633,15 @@ void RenderBlock::calcColumnWidth() desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; } else if (colGap < availWidth) { desiredColumnCount = availWidth / colGap; + if (desiredColumnCount < 1) + desiredColumnCount = 1; desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; } } else if (style()->hasAutoColumnCount()) { if (colWidth < availWidth) { desiredColumnCount = (availWidth + colGap) / (colWidth + colGap); + if (desiredColumnCount < 1) + desiredColumnCount = 1; desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; } } else { @@ -3694,6 +3651,8 @@ void RenderBlock::calcColumnWidth() desiredColumnWidth = colWidth; } else if (colWidth < availWidth) { desiredColumnCount = (availWidth + colGap) / (colWidth + colGap); + if (desiredColumnCount < 1) + desiredColumnCount = 1; desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; } } @@ -3702,7 +3661,7 @@ void RenderBlock::calcColumnWidth() void RenderBlock::setDesiredColumnCountAndWidth(int count, int width) { - if (count == 1) { + if (count == 1 && style()->hasAutoColumnWidth()) { if (hasColumns()) { delete gColumnInfoMap->take(this); setHasColumns(false); @@ -3744,7 +3703,7 @@ Vector<IntRect>* RenderBlock::columnRects() const return &gColumnInfoMap->get(this)->m_columnRects; } -int RenderBlock::layoutColumns(int endOfContent) +int RenderBlock::layoutColumns(int endOfContent, int requestedColumnHeight) { // Don't do anything if we have no columns if (!hasColumns()) @@ -3757,17 +3716,20 @@ int RenderBlock::layoutColumns(int endOfContent) bool computeIntrinsicHeight = (endOfContent == -1); - // Fill the columns in to the available height. Attempt to balance the height of the columns - int availableHeight = contentHeight(); - int colHeight = computeIntrinsicHeight ? availableHeight / desiredColumnCount : availableHeight; - + // Fill the columns in to the available height. Attempt to balance the height of the columns. // Add in half our line-height to help with best-guess initial balancing. int columnSlop = lineHeight(false) / 2; int remainingSlopSpace = columnSlop * desiredColumnCount; + int availableHeight = contentHeight(); + int colHeight; + if (computeIntrinsicHeight && requestedColumnHeight >= 0) + colHeight = requestedColumnHeight; + else if (computeIntrinsicHeight) + colHeight = availableHeight / desiredColumnCount + columnSlop; + else + colHeight = availableHeight; + int originalColHeight = colHeight; - if (computeIntrinsicHeight) - colHeight += columnSlop; - int colGap = columnGap(); // Compute a collection of column rects. @@ -3783,7 +3745,8 @@ int RenderBlock::layoutColumns(int endOfContent) int currY = top; unsigned colCount = desiredColumnCount; int maxColBottom = borderTop() + paddingTop(); - int contentBottom = top + availableHeight; + int contentBottom = top + availableHeight; + int minimumColumnHeight = -1; for (unsigned i = 0; i < colCount; i++) { // If we aren't constrained, then the last column can just get all the remaining space. if (computeIntrinsicHeight && i == colCount - 1) @@ -3803,6 +3766,11 @@ int RenderBlock::layoutColumns(int endOfContent) paintObject(paintInfo, 0, 0); setHasColumns(true); + if (computeIntrinsicHeight && v->minimumColumnHeight() > originalColHeight) { + // The initial column height was too small to contain one line of text. + minimumColumnHeight = max(minimumColumnHeight, v->minimumColumnHeight()); + } + int adjustedBottom = v->bestTruncatedAt(); if (adjustedBottom <= currY) adjustedBottom = currY + colHeight; @@ -3839,14 +3807,21 @@ int RenderBlock::layoutColumns(int endOfContent) colCount++; } - m_overflowWidth = max(width(), currX - colGap); - m_overflowLeft = min(0, currX + desiredColumnWidth + colGap); + if (minimumColumnHeight >= 0) { + // If originalColHeight was too small, we need to try to layout again. + return layoutColumns(endOfContent, minimumColumnHeight); + } - m_overflowHeight = maxColBottom; + int overflowRight = max(width(), currX - colGap); + int overflowLeft = min(0, currX + desiredColumnWidth + colGap); + int overflowHeight = maxColBottom; int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); if (computeIntrinsicHeight) - setHeight(m_overflowHeight + toAdd); + setHeight(maxColBottom + toAdd); + + m_overflow.clear(); + addLayoutOverflow(IntRect(overflowLeft, 0, overflowRight - overflowLeft, overflowHeight)); v->setPrintRect(IntRect()); v->setTruncatedAt(0); @@ -4317,6 +4292,10 @@ void RenderBlock::calcInlinePrefWidths() } else inlineMax += childMax; } + + // Ignore spaces after a list marker. + if (child->isListMarker()) + stripFrontSpaces = true; } else { m_minPrefWidth = max(inlineMin, m_minPrefWidth); m_maxPrefWidth = max(inlineMax, m_maxPrefWidth); @@ -4766,7 +4745,7 @@ static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, if (block->childrenInline()) { for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) { if (++count == l) - return box->bottomOverflow() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0); + return box->lineBottom() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0); } } else { @@ -5079,7 +5058,7 @@ IntRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, int* return IntRect(x, y, caretWidth, height); } -void RenderBlock::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) { // For blocks inside inlines, we go ahead and include margins so that we run right up to the // inline boxes above and below us (thus getting merged with them to form a single irregular @@ -5091,14 +5070,20 @@ void RenderBlock::addFocusRingRects(GraphicsContext* graphicsContext, int tx, in bool prevInlineHasLineBox = toRenderInline(inlineContinuation()->node()->renderer())->firstLineBox(); int topMargin = prevInlineHasLineBox ? collapsedMarginTop() : 0; int bottomMargin = nextInlineHasLineBox ? collapsedMarginBottom() : 0; - graphicsContext->addFocusRingRect(IntRect(tx, ty - topMargin, - width(), height() + topMargin + bottomMargin)); - } else - graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); + IntRect rect(tx, ty - topMargin, width(), height() + topMargin + bottomMargin); + if (!rect.isEmpty()) + rects.append(rect); + } else if (width() && height()) + rects.append(IntRect(tx, ty, width(), height())); if (!hasOverflowClip() && !hasControlClip()) { - for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) - graphicsContext->addFocusRingRect(IntRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())); + for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { + int top = max(curr->lineTop(), curr->y()); + int bottom = min(curr->lineBottom(), curr->y() + curr->height()); + IntRect rect(tx + curr->x(), ty + top, curr->width(), bottom - top); + if (!rect.isEmpty()) + rects.append(rect); + } for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { if (!curr->isText() && !curr->isListMarker() && curr->isBox()) { @@ -5109,24 +5094,31 @@ void RenderBlock::addFocusRingRects(GraphicsContext* graphicsContext, int tx, in pos = curr->localToAbsolute(); else pos = FloatPoint(tx + box->x(), ty + box->y()); - box->addFocusRingRects(graphicsContext, pos.x(), pos.y()); + box->addFocusRingRects(rects, pos.x(), pos.y()); } } } if (inlineContinuation()) - inlineContinuation()->addFocusRingRects(graphicsContext, + inlineContinuation()->addFocusRingRects(rects, tx - x() + inlineContinuation()->containingBlock()->x(), ty - y() + inlineContinuation()->containingBlock()->y()); } -RenderBlock* RenderBlock::createAnonymousBlock() const +RenderBlock* RenderBlock::createAnonymousBlock(bool isFlexibleBox) const { RefPtr<RenderStyle> newStyle = RenderStyle::create(); newStyle->inheritFrom(style()); - newStyle->setDisplay(BLOCK); - RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + RenderBlock* newBox = 0; + if (isFlexibleBox) { + newStyle->setDisplay(BOX); + newBox = new (renderArena()) RenderFlexibleBox(document() /* anonymous box */); + } else { + newStyle->setDisplay(BLOCK); + newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + } + newBox->setStyle(newStyle.release()); return newBox; } diff --git a/WebCore/rendering/RenderBlock.h b/WebCore/rendering/RenderBlock.h index 839be16..d1d105a 100644 --- a/WebCore/rendering/RenderBlock.h +++ b/WebCore/rendering/RenderBlock.h @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -34,7 +34,6 @@ namespace WebCore { class InlineIterator; class RenderInline; -class RootInlineBox; struct BidiRun; @@ -67,17 +66,6 @@ public: void deleteLineBoxTree(); - // 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 - // it would have an overflow height of borderTop() + paddingTop() + 100px. - virtual int overflowHeight(bool includeInterior = true) const; - virtual int overflowWidth(bool includeInterior = true) const; - virtual int overflowLeft(bool includeInterior = true) const; - virtual int overflowTop(bool includeInterior = true) const; - virtual IntRect overflowRect(bool includeInterior = true) const; - - void addVisualOverflow(const IntRect&); - virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); virtual void removeChild(RenderObject*); @@ -104,6 +92,7 @@ public: IntRect floatRect() 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; @@ -147,14 +136,16 @@ public: // This function is a convenience helper for creating an anonymous block that inherits its // style from this RenderBlock. - RenderBlock* createAnonymousBlock() const; + RenderBlock* createAnonymousBlock(bool isFlexibleBox = false) const; - Vector<IntRect>* columnRects() const; - int columnGap() const; + static void appendRunsForObject(int start, int end, RenderObject*, InlineBidiResolver&); + static bool requiresLineBox(const InlineIterator&, bool isLineEmpty = true, bool previousLineBrokeCleanly = true); protected: - virtual void setOverflowHeight(int h) { m_overflowHeight = h; } - virtual void setOverflowWidth(int w) { m_overflowWidth = w; } + void moveChildTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* child); + void moveChildTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* beforeChild, RenderObject* child); + void moveAllChildrenTo(RenderObject* to, RenderObjectChildList* toChildList); + void moveAllChildrenTo(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* beforeChild); int maxTopPosMargin() const { return m_maxMargin ? m_maxMargin->m_topPos : MaxMargin::topPosDefault(this); } int maxTopNegMargin() const { return m_maxMargin ? m_maxMargin->m_topNeg : MaxMargin::topNegDefault(this); } @@ -207,7 +198,7 @@ protected: virtual bool hasLineIfEmpty() const; bool layoutOnlyPositionedObjects(); - + private: virtual RenderObjectChildList* virtualChildren() { return children(); } virtual const RenderObjectChildList* virtualChildren() const { return children(); } @@ -275,11 +266,14 @@ private: InlineFlowBox* createLineBoxes(RenderObject*, bool firstLine); void computeHorizontalPositionsForLine(RootInlineBox*, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd); void computeVerticalPositionsForLine(RootInlineBox*, BidiRun*); - void checkLinesForOverflow(); void deleteEllipsisLineBoxes(); void checkLinesForTextOverflow(); + void addOverflowFromInlineChildren(); // End of functions defined in RenderBlockLineLayout.cpp. + void addOverflowFromBlockChildren(); + void addOverflowFromFloats(); + void paintFloats(PaintInfo&, int tx, int ty, bool preservePhase = false); void paintContents(PaintInfo&, int tx, int ty); void paintColumnContents(PaintInfo&, int tx, int ty, bool paintFloats = false); @@ -353,18 +347,32 @@ private: int desiredColumnWidth() const; unsigned desiredColumnCount() const; +#if PLATFORM(ANDROID) +public: +#endif + Vector<IntRect>* columnRects() const; +#if PLATFORM(ANDROID) +private: +#endif void setDesiredColumnCountAndWidth(int count, int width); +#if PLATFORM(ANDROID) +public: +#endif + int columnGap() const; +#if PLATFORM(ANDROID) +private: +#endif void paintContinuationOutlines(PaintInfo&, int tx, int ty); virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); void adjustPointToColumnContents(IntPoint&) const; void adjustForBorderFit(int x, int& left, int& right) const; // Helper function for borderFitAdjust - void markLinesDirtyInVerticalRange(int top, int bottom); + void markLinesDirtyInVerticalRange(int top, int bottom, RootInlineBox* highest = 0); void newLine(EClear); @@ -376,13 +384,13 @@ private: void offsetForContents(int& tx, int& ty) const; void calcColumnWidth(); - int layoutColumns(int endOfContent = -1); + int layoutColumns(int endOfContent = -1, int requestedColumnHeight = -1); bool expandsToEncloseOverhangingFloats() const; void updateScrollInfoAfterLayout(); - struct FloatingObject { + struct FloatingObject : Noncopyable { enum Type { FloatLeft, FloatRight @@ -432,10 +440,6 @@ private: // This flag is set when we know we're examining bottom margins and we know we're at the bottom of the block. bool m_atBottomOfBlock : 1; - // If our last normal flow child was a self-collapsing block that cleared a float, - // we track it in this variable. - bool m_selfCollapsingBlockClearedFloat : 1; - // These variables are used to detect quirky margins that we need to collapse away (in table cells // and in the body element). bool m_topQuirk : 1; @@ -452,7 +456,6 @@ private: void setAtTopOfBlock(bool b) { m_atTopOfBlock = b; } void setAtBottomOfBlock(bool b) { m_atBottomOfBlock = b; } void clearMargin() { m_posMargin = m_negMargin = 0; } - void setSelfCollapsingBlockClearedFloat(bool b) { m_selfCollapsingBlockClearedFloat = b; } void setTopQuirk(bool b) { m_topQuirk = b; } void setBottomQuirk(bool b) { m_bottomQuirk = b; } void setDeterminedTopQuirk(bool b) { m_determinedTopQuirk = b; } @@ -468,7 +471,6 @@ private: bool canCollapseWithBottom() const { return m_atBottomOfBlock && m_canCollapseBottomWithChildren; } bool canCollapseTopWithChildren() const { return m_canCollapseTopWithChildren; } bool canCollapseBottomWithChildren() const { return m_canCollapseBottomWithChildren; } - bool selfCollapsingBlockClearedFloat() const { return m_selfCollapsingBlockClearedFloat; } bool quirkContainer() const { return m_quirkContainer; } bool determinedTopQuirk() const { return m_determinedTopQuirk; } bool topQuirk() const { return m_topQuirk; } @@ -478,6 +480,7 @@ private: int margin() const { return m_posMargin - m_negMargin; } }; + void layoutBlockChild(RenderBox* child, MarginInfo&, int& previousFloatBottom, int& maxFloatBottom); void adjustPositionedBlock(RenderBox* child, const MarginInfo&); void adjustFloatingBlock(const MarginInfo&); bool handleSpecialChild(RenderBox* child, const MarginInfo&); @@ -503,7 +506,7 @@ private: RenderInline* m_inlineContinuation; // Allocated only when some of these fields have non-default values - struct MaxMargin { + struct MaxMargin : Noncopyable { MaxMargin(const RenderBlock* o) : m_topPos(topPosDefault(o)) , m_topNeg(topNegDefault(o)) @@ -528,15 +531,11 @@ private: 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>. -protected: - // How much content overflows out of our block vertically or horizontally. - int m_overflowHeight; - int m_overflowWidth; - int m_overflowLeft; - int m_overflowTop; - -private: mutable int m_lineHeight; + + // RenderRubyBase objects need to be able to split and merge, moving their children around + // (calling moveChildTo, moveAllChildrenTo, and makeChildrenNonInline). + friend class RenderRubyBase; }; inline RenderBlock* toRenderBlock(RenderObject* object) diff --git a/WebCore/rendering/RenderBlockLineLayout.cpp b/WebCore/rendering/RenderBlockLineLayout.cpp index 615a369..a7f3553 100644 --- a/WebCore/rendering/RenderBlockLineLayout.cpp +++ b/WebCore/rendering/RenderBlockLineLayout.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2000 Lars Knoll (knoll@kde.org) - * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009 Apple Inc. All right reserved. + * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. All right reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,12 +24,14 @@ #include "BidiResolver.h" #include "CharacterNames.h" +#include "InlineIterator.h" #include "InlineTextBox.h" #include "Logging.h" #include "RenderArena.h" #include "RenderInline.h" #include "RenderListMarker.h" #include "RenderView.h" +#include "TrailingFloatsRootInlineBox.h" #include "break_lines.h" #include <wtf/AlwaysInline.h> #include <wtf/RefCountedLeakCounter.h> @@ -51,36 +54,6 @@ namespace WebCore { // We don't let our line box tree for a single line get any deeper than this. const unsigned cMaxLineDepth = 200; -class InlineIterator { -public: - InlineIterator() - : block(0) - , obj(0) - , pos(0) - , nextBreakablePosition(-1) - { - } - - InlineIterator(RenderBlock* b, RenderObject* o, unsigned p) - : block(b) - , obj(o) - , pos(p) - , nextBreakablePosition(-1) - { - } - - void increment(InlineBidiResolver* resolver = 0); - bool atEnd() const; - - UChar current() const; - Direction direction() const; - - RenderBlock* block; - RenderObject* obj; - unsigned pos; - int nextBreakablePosition; -}; - static int getBorderPaddingMargin(RenderBoxModelObject* child, bool endOfInline) { bool leftSide = (child->style()->direction() == LTR) ? !endOfInline : endOfInline; @@ -105,247 +78,6 @@ static int inlineWidth(RenderObject* child, bool start = true, bool end = true) return extraWidth; } -struct BidiRun : BidiCharacterRun { - BidiRun(int start, int stop, RenderObject* object, BidiContext* context, Direction dir) - : BidiCharacterRun(start, stop, context, dir) - , m_object(object) - , m_box(0) - { - } - - void destroy(); - - // Overloaded new operator. - void* operator new(size_t, RenderArena*) throw(); - - // Overridden to prevent the normal delete from being called. - void operator delete(void*, size_t); - - BidiRun* next() { return static_cast<BidiRun*>(m_next); } - -private: - // The normal operator new is disallowed. - void* operator new(size_t) throw(); - -public: - RenderObject* m_object; - InlineBox* m_box; -}; - -#ifndef NDEBUG -static RefCountedLeakCounter bidiRunCounter("BidiRun"); - -static bool inBidiRunDestroy; -#endif - -void BidiRun::destroy() -{ -#ifndef NDEBUG - inBidiRunDestroy = true; -#endif - RenderArena* renderArena = m_object->renderArena(); - delete this; -#ifndef NDEBUG - inBidiRunDestroy = false; -#endif - - // Recover the size left there for us by operator delete and free the memory. - renderArena->free(*reinterpret_cast<size_t*>(this), this); -} - -void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw() -{ -#ifndef NDEBUG - bidiRunCounter.increment(); -#endif - return renderArena->allocate(sz); -} - -void BidiRun::operator delete(void* ptr, size_t sz) -{ -#ifndef NDEBUG - bidiRunCounter.decrement(); -#endif - ASSERT(inBidiRunDestroy); - - // Stash size where destroy() can find it. - *(size_t*)ptr = sz; -} - -// --------------------------------------------------------------------- - -inline bool operator==(const InlineIterator& it1, const InlineIterator& it2) -{ - return it1.pos == it2.pos && it1.obj == it2.obj; -} - -inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2) -{ - return it1.pos != it2.pos || it1.obj != it2.obj; -} - -static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0) -{ - RenderObject* next = 0; - bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false; - bool endOfInline = false; - - while (current) { - next = 0; - if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned() && !current->isText()) { - next = current->firstChild(); - if (next && resolver && next->isRenderInline()) { - EUnicodeBidi ub = next->style()->unicodeBidi(); - if (ub != UBNormal) { - TextDirection dir = next->style()->direction(); - Direction d = (ub == Embed - ? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding) - : (dir == RTL ? RightToLeftOverride : LeftToRightOverride)); - resolver->embed(d); - } - } - } - - if (!next) { - if (!skipInlines && !oldEndOfInline && current->isRenderInline()) { - next = current; - endOfInline = true; - break; - } - - while (current && current != block) { - if (resolver && current->isRenderInline() && current->style()->unicodeBidi() != UBNormal) - resolver->embed(PopDirectionalFormat); - - next = current->nextSibling(); - if (next) { - if (resolver && next->isRenderInline()) { - EUnicodeBidi ub = next->style()->unicodeBidi(); - if (ub != UBNormal) { - TextDirection dir = next->style()->direction(); - Direction d = (ub == Embed - ? (dir == RTL ? RightToLeftEmbedding: LeftToRightEmbedding) - : (dir == RTL ? RightToLeftOverride : LeftToRightOverride)); - resolver->embed(d); - } - } - break; - } - - current = current->parent(); - if (!skipInlines && current && current != block && current->isRenderInline()) { - next = current; - endOfInline = true; - break; - } - } - } - - if (!next) - break; - - if (next->isText() || next->isFloating() || next->isReplaced() || next->isPositioned() - || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines. - && next->isRenderInline())) - break; - current = next; - } - - if (endOfInlinePtr) - *endOfInlinePtr = endOfInline; - - return next; -} - -static RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* resolver, bool skipInlines = true) -{ - if (!block->firstChild()) - return 0; - - RenderObject* o = block->firstChild(); - if (o->isRenderInline()) { - if (resolver) { - EUnicodeBidi ub = o->style()->unicodeBidi(); - if (ub != UBNormal) { - TextDirection dir = o->style()->direction(); - Direction d = (ub == Embed - ? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding) - : (dir == RTL ? RightToLeftOverride : LeftToRightOverride)); - resolver->embed(d); - } - } - if (skipInlines && o->firstChild()) - o = bidiNext(block, o, resolver, skipInlines); - else { - // Never skip empty inlines. - if (resolver) - resolver->commitExplicitEmbedding(); - return o; - } - } - - if (o && !o->isText() && !o->isReplaced() && !o->isFloating() && !o->isPositioned()) - o = bidiNext(block, o, resolver, skipInlines); - - if (resolver) - resolver->commitExplicitEmbedding(); - return o; -} - -inline void InlineIterator::increment(InlineBidiResolver* resolver) -{ - if (!obj) - return; - if (obj->isText()) { - pos++; - if (pos >= toRenderText(obj)->textLength()) { - obj = bidiNext(block, obj, resolver); - pos = 0; - nextBreakablePosition = -1; - } - } else { - obj = bidiNext(block, obj, resolver); - pos = 0; - nextBreakablePosition = -1; - } -} - -template<> -inline void InlineBidiResolver::increment() -{ - current.increment(this); -} - -inline bool InlineIterator::atEnd() const -{ - return !obj; -} - -inline UChar InlineIterator::current() const -{ - if (!obj || !obj->isText()) - return 0; - - RenderText* text = toRenderText(obj); - if (pos >= text->textLength()) - return 0; - - return text->characters()[pos]; -} - -ALWAYS_INLINE Direction InlineIterator::direction() const -{ - if (UChar c = current()) - return Unicode::direction(c); - - if (obj && obj->isListMarker()) - return obj->style()->direction() == LTR ? LeftToRight : RightToLeft; - - return OtherNeutral; -} - -// ------------------------------------------------------------------------------------------------- - static void chopMidpointsAt(LineMidpointState& lineMidpointState, RenderObject* obj, unsigned pos) { if (!lineMidpointState.numMidpoints) @@ -403,7 +135,7 @@ static void addMidpoint(LineMidpointState& lineMidpointState, const InlineIterat midpoints[lineMidpointState.numMidpoints++] = midpoint; } -static void appendRunsForObject(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) +void RenderBlock::appendRunsForObject(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) { if (start > end || obj->isFloating() || (obj->isPositioned() && !obj->style()->hasStaticX() && !obj->style()->hasStaticY() && !obj->container()->isRenderInline())) @@ -446,36 +178,6 @@ static void appendRunsForObject(int start, int end, RenderObject* obj, InlineBid } } -template <> -void InlineBidiResolver::appendRun() -{ - if (!emptyRun && !eor.atEnd()) { - int start = sor.pos; - RenderObject *obj = sor.obj; - while (obj && obj != eor.obj && obj != endOfLine.obj) { - appendRunsForObject(start, obj->length(), obj, *this); - start = 0; - obj = bidiNext(sor.block, obj); - } - if (obj) { - unsigned pos = obj == eor.obj ? eor.pos : UINT_MAX; - if (obj == endOfLine.obj && endOfLine.pos <= pos) { - reachedEndOfLine = true; - pos = endOfLine.pos; - } - // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be - int end = obj->length() ? pos+1 : 0; - appendRunsForObject(start, end, obj, *this); - } - - eor.increment(); - sor = eor; - } - - m_direction = OtherNeutral; - m_status.eor = OtherNeutral; -} - static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false) { if (isRootLineBox) @@ -770,11 +472,8 @@ void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, bool // The widths of all runs are now known. We can now place every inline box (and // compute accurate widths for the inline flow boxes). - int leftPosition = x; - int rightPosition = x; needsWordSpacing = false; - lineBox->placeBoxesHorizontally(x, leftPosition, rightPosition, needsWordSpacing); - lineBox->setHorizontalOverflowPositions(leftPosition, rightPosition); + lineBox->placeBoxesHorizontally(x, needsWordSpacing); } void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun) @@ -782,11 +481,6 @@ void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRu setHeight(lineBox->verticallyAlignBoxes(height())); lineBox->setBlockHeight(height()); - // See if the line spilled out. If so set overflow height accordingly. - int bottomOfLine = lineBox->bottomOverflow(); - if (bottomOfLine > height() && bottomOfLine > m_overflowHeight) - m_overflowHeight = bottomOfLine; - // Now make sure we place replaced render objects correctly. for (BidiRun* r = firstRun; r; r = r->next()) { ASSERT(r->m_box); @@ -831,14 +525,14 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i { bool useRepaintBounds = false; - m_overflowHeight = 0; + m_overflow.clear(); setHeight(borderTop() + paddingTop()); int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); // Figure out if we should clear out our line boxes. // FIXME: Handle resize eventually! - bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren; + bool fullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren; if (fullLayout) lineBoxes()->deleteLineBoxes(renderArena()); @@ -855,14 +549,17 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i if (firstChild()) { #ifdef ANDROID_LAYOUT - // if we are in fitColumnToScreen mode and viewport width is not device-width, + // if we are in fitColumnToScreen mode // and the current object is not float:right in LTR or not float:left in RTL, // and text align is auto, or justify or left in LTR, or right in RTL, we // will wrap text around screen width so that it doesn't need to scroll // horizontally when reading a paragraph. + // In case the line height is less than the font size, we skip + // the text wrapping since this will cause text overlapping. + // If a text has background image, we ignore text wrapping, + // otherwise the background will be potentially messed up. const Settings* settings = document()->settings(); - bool doTextWrap = settings && settings->viewportWidth() != 0 && - settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen; + bool doTextWrap = settings && settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen; if (doTextWrap) { int ta = style()->textAlign(); int dir = style()->direction(); @@ -871,9 +568,12 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // width as it may cause text to overlap. bool positioned = isPositioned(); EFloat cssfloat = style()->floating(); + const int lineHeight = style()->computedLineHeight(); + const int fontSize = style()->fontSize(); doTextWrap = autowrap && !positioned && + (fontSize <= lineHeight) && !style()->hasBackgroundImage() && (((dir == LTR && cssfloat != FRIGHT) || - (dir == RTL && cssfloat != FLEFT)) && + (dir == RTL && cssfloat != FLEFT)) && ((ta == TAAUTO) || (ta == JUSTIFY) || ((ta == LEFT || ta == WEBKIT_LEFT) && (dir == LTR)) || ((ta == RIGHT || ta == WEBKIT_RIGHT) && (dir == RTL)))); @@ -884,6 +584,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i bool endOfInline = false; RenderObject* o = bidiFirst(this, 0, false); Vector<FloatWithRect> floats; + bool hasInlineChild = false; while (o) { if (o->isReplaced() || o->isFloating() || o->isPositioned()) { RenderBox* box = toRenderBox(o); @@ -911,6 +612,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i o->layoutIfNeeded(); } } else if (o->isText() || (o->isRenderInline() && !endOfInline)) { + hasInlineChild = true; if (fullLayout || o->selfNeedsLayout()) dirtyLineBoxesForRenderer(o, fullLayout); o->setNeedsLayout(false); @@ -956,7 +658,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i break; } if (obj->isFloating() || obj->isPositioned()) { - // floating and absolute or fixed positioning are done out + // floating and absolute or fixed positioning are done out // of normal flow. Don't need to worry about height any more. break; } @@ -972,7 +674,13 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i setWidth(min(width(), maxWidth)); m_minPrefWidth = min(m_minPrefWidth, maxWidth); m_maxPrefWidth = min(m_maxPrefWidth, maxWidth); - m_overflowWidth = min(m_overflowWidth, maxWidth); + + IntRect overflow = layoutOverflowRect(); + if (overflow.width() > maxWidth) { + overflow.setWidth(maxWidth); + clearLayoutOverflow(); + addLayoutOverflow(overflow); + } } } } @@ -984,7 +692,7 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i bool previousLineBrokeCleanly = true; RootInlineBox* startLine = determineStartPosition(firstLine, fullLayout, previousLineBrokeCleanly, resolver, floats, floatIndex); - if (fullLayout && !selfNeedsLayout()) { + if (fullLayout && hasInlineChild && !selfNeedsLayout()) { setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like // we're supposed to. RenderView* v = view(); @@ -1016,8 +724,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i RenderArena* arena = renderArena(); RootInlineBox* box = startLine; while (box) { - repaintTop = min(repaintTop, box->topOverflow()); - repaintBottom = max(repaintBottom, box->bottomOverflow()); + repaintTop = min(repaintTop, box->topVisibleOverflow()); + repaintBottom = max(repaintBottom, box->bottomVisibleOverflow()); RootInlineBox* next = box->nextRootBox(); box->deleteLine(arena); box = next; @@ -1153,8 +861,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i if (lineBox) { lineBox->setLineBreakInfo(end.obj, end.pos, resolver.status()); if (useRepaintBounds) { - repaintTop = min(repaintTop, lineBox->topOverflow()); - repaintBottom = max(repaintBottom, lineBox->bottomOverflow()); + repaintTop = min(repaintTop, lineBox->topVisibleOverflow()); + repaintBottom = max(repaintBottom, lineBox->bottomVisibleOverflow()); } } @@ -1193,8 +901,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i for (RootInlineBox* line = endLine; line; line = line->nextRootBox()) { line->attachLine(); if (delta) { - repaintTop = min(repaintTop, line->topOverflow() + min(delta, 0)); - repaintBottom = max(repaintBottom, line->bottomOverflow() + max(delta, 0)); + repaintTop = min(repaintTop, line->topVisibleOverflow() + min(delta, 0)); + repaintBottom = max(repaintBottom, line->bottomVisibleOverflow() + max(delta, 0)); line->adjustPosition(0, delta); } if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) { @@ -1210,12 +918,12 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i setHeight(lastRootBox()->blockHeight()); } else { // Delete all the remaining lines. - InlineRunBox* line = endLine; + RootInlineBox* line = endLine; RenderArena* arena = renderArena(); while (line) { - repaintTop = min(repaintTop, line->topOverflow()); - repaintBottom = max(repaintBottom, line->bottomOverflow()); - InlineRunBox* next = line->nextLineBox(); + repaintTop = min(repaintTop, line->topVisibleOverflow()); + repaintBottom = max(repaintBottom, line->bottomVisibleOverflow()); + RootInlineBox* next = line->nextRootBox(); line->deleteLine(arena); line = next; } @@ -1225,6 +933,16 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // In case we have a float on the last line, it might not be positioned up to now. // This has to be done before adding in the bottom border/padding, or the float will // include the padding incorrectly. -dwh + if (checkForFloatsFromLastLine) { + int bottomVisualOverflow = lastRootBox()->bottomVisualOverflow(); + int bottomLayoutOverflow = lastRootBox()->bottomLayoutOverflow(); + TrailingFloatsRootInlineBox* trailingFloatsLineBox = new (renderArena()) TrailingFloatsRootInlineBox(this); + m_lineBoxes.appendLineBox(trailingFloatsLineBox); + trailingFloatsLineBox->setConstructed(); + trailingFloatsLineBox->verticallyAlignBoxes(height()); + trailingFloatsLineBox->setVerticalOverflowPositions(height(), bottomLayoutOverflow, height(), bottomVisualOverflow, 0); + trailingFloatsLineBox->setBlockHeight(height()); + } if (lastFloat) { for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) { } @@ -1253,12 +971,6 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i // Now add in the bottom border/padding. setHeight(height() + toAdd); - // Always make sure this is at least our height. - m_overflowHeight = max(height(), m_overflowHeight); - - // See if any lines spill out of the block. If so, we need to update our overflow width. - checkLinesForOverflow(); - if (!firstLineBox() && hasLineIfEmpty()) setHeight(height() + lineHeight(true, true)); @@ -1293,7 +1005,7 @@ RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLa if (floats[floatIndex].rect.size() != newSize) { int floatTop = floats[floatIndex].rect.y(); curr->markDirty(); - markLinesDirtyInVerticalRange(curr->blockHeight(), floatTop + max(floats[floatIndex].rect.height(), newSize.height())); + markLinesDirtyInVerticalRange(curr->blockHeight(), floatTop + max(floats[floatIndex].rect.height(), newSize.height()), curr); floats[floatIndex].rect.setSize(newSize); dirtiedByFloat = true; } @@ -1485,8 +1197,8 @@ bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const Inlin RootInlineBox* boxToDelete = endLine; RenderArena* arena = renderArena(); while (boxToDelete && boxToDelete != result) { - repaintTop = min(repaintTop, boxToDelete->topOverflow()); - repaintBottom = max(repaintBottom, boxToDelete->bottomOverflow()); + repaintTop = min(repaintTop, boxToDelete->topVisibleOverflow()); + repaintBottom = max(repaintBottom, boxToDelete->bottomVisibleOverflow()); RootInlineBox* next = boxToDelete->nextRootBox(); boxToDelete->deleteLine(arena); boxToDelete = next; @@ -1539,7 +1251,7 @@ static bool inlineFlowRequiresLineBox(RenderInline* flow) return !flow->firstChild() && flow->hasHorizontalBordersPaddingOrMargin(); } -static inline bool requiresLineBox(const InlineIterator& it, bool isLineEmpty, bool previousLineBrokeCleanly) +bool RenderBlock::requiresLineBox(const InlineIterator& it, bool isLineEmpty, bool previousLineBrokeCleanly) { if (it.obj->isFloatingOrPositioned()) return false; @@ -1878,10 +1590,10 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool currentCharacterIsSpace = false; currentCharacterIsWS = false; trailingSpaceObject = 0; - + // Optimize for a common case. If we can't find whitespace after the list // item, then this is all moot. -dwh - if (o->isListMarker() && !toRenderListMarker(o)->isInside()) { + if (o->isListMarker()) { if (style()->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(this, o, lineMidpointState)) { // Like with inline flows, we start ignoring spaces to make sure that any // additional spaces we see will be discarded. @@ -1889,6 +1601,8 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool currentCharacterIsWS = true; ignoringSpaces = true; } + if (toRenderListMarker(o)->isInside()) + tmpW += replacedBox->width() + replacedBox->marginLeft() + replacedBox->marginRight() + inlineWidth(o); } else tmpW += replacedBox->width() + replacedBox->marginLeft() + replacedBox->marginRight() + inlineWidth(o); } else if (o->isText()) { @@ -1908,6 +1622,10 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool int wordSpacing = o->style()->wordSpacing(); int lastSpaceWordSpacing = 0; + // Non-zero only when kerning is enabled, in which case we measure words with their trailing + // space, then subtract its width. + int wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.spaceWidth() + wordSpacing : 0; + int wrapW = tmpW + inlineWidth(o, !appliedStartWidth, true); int charWidth = 0; bool breakNBSP = autoWrap && o->style()->nbspMode() == SPACE; @@ -2002,7 +1720,11 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } } - int additionalTmpW = textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + int additionalTmpW; + if (wordTrailingSpaceWidth && currentCharacterIsSpace) + additionalTmpW = textWidth(t, lastSpace, pos + 1 - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) - wordTrailingSpaceWidth + lastSpaceWordSpacing; + else + additionalTmpW = textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; tmpW += additionalTmpW; if (!appliedStartWidth) { tmpW += inlineWidth(o, true, false); @@ -2034,7 +1756,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } } if (lineWasTooWide || w + tmpW > width) { - if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && !toRenderText(lBreak.obj)->isWordBreak() && toRenderText(lBreak.obj)->characters()[lBreak.pos] == '\n') { + if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && toRenderText(lBreak.obj)->textLength() && !toRenderText(lBreak.obj)->isWordBreak() && toRenderText(lBreak.obj)->characters()[lBreak.pos] == '\n') { if (!stoppedIgnoringSpaces && pos > 0) { // We need to stop right before the newline and then start up again. addMidpoint(lineMidpointState, InlineIterator(0, o, pos - 1)); // Stop @@ -2243,19 +1965,12 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool lBreak.nextBreakablePosition = -1; } } else if (lBreak.obj) { - if (last != o && !last->isListMarker()) { - // better to break between object boundaries than in the middle of a word (except for list markers) - lBreak.obj = o; - lBreak.pos = 0; - lBreak.nextBreakablePosition = -1; - } else { - // Don't ever break in the middle of a word if we can help it. - // There's no room at all. We just have to be on this line, - // even though we'll spill out. - lBreak.obj = o; - lBreak.pos = pos; - lBreak.nextBreakablePosition = -1; - } + // Don't ever break in the middle of a word if we can help it. + // There's no room at all. We just have to be on this line, + // even though we'll spill out. + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = -1; } } @@ -2306,14 +2021,12 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool return lBreak; } -void RenderBlock::checkLinesForOverflow() +void RenderBlock::addOverflowFromInlineChildren() { - m_overflowWidth = width(); for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { - m_overflowLeft = min(curr->leftOverflow(), m_overflowLeft); - m_overflowTop = min(curr->topOverflow(), m_overflowTop); - m_overflowWidth = max(curr->rightOverflow(), m_overflowWidth); - m_overflowHeight = max(curr->bottomOverflow(), m_overflowHeight); + addLayoutOverflow(curr->layoutOverflowRect()); + if (!hasOverflowClip()) + addVisualOverflow(curr->visualOverflowRect()); } } diff --git a/WebCore/rendering/RenderBox.cpp b/WebCore/rendering/RenderBox.cpp index 4c2bff0..92c3d99 100644 --- a/WebCore/rendering/RenderBox.cpp +++ b/WebCore/rendering/RenderBox.cpp @@ -26,6 +26,7 @@ #include "RenderBox.h" #include "CachedImage.h" +#include "Chrome.h" #include "ChromeClient.h" #include "Document.h" #include "FrameView.h" @@ -145,6 +146,8 @@ void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyl markContainingBlocksForLayout(); if (style()->position() == StaticPosition) repaint(); + else if (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition) + parent()->setChildNeedsLayout(true); if (isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition)) removeFloatingOrPositionedChildFromBlockLists(); } @@ -333,12 +336,12 @@ IntRect RenderBox::outlineBoundsForRepaint(RenderBoxModelObject* repaintContaine return box; } -void RenderBox::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +void RenderBox::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) { - graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); + if (width() && height()) + rects.append(IntRect(tx, ty, width(), height())); } - IntRect RenderBox::reflectionBox() const { IntRect result; @@ -406,16 +409,28 @@ int RenderBox::horizontalScrollbarHeight() const return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0; } -bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) +bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) { RenderLayer* l = layer(); - if (l && l->scroll(direction, granularity, multiplier)) + if (l && l->scroll(direction, granularity, multiplier)) { + if (stopNode) + *stopNode = node(); + return true; + } + + if (stopNode && *stopNode && *stopNode == node()) return true; + RenderBlock* b = containingBlock(); if (b && !b->isRenderView()) - return b->scroll(direction, granularity, multiplier); + return b->scroll(direction, granularity, multiplier, stopNode); return false; } + +bool RenderBox::canBeScrolledAndHasScrollableArea() const +{ + return canBeProgramaticallyScrolled(false) && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth()); +} bool RenderBox::canBeProgramaticallyScrolled(bool) const { @@ -554,13 +569,14 @@ void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty) { const FillLayer* bgLayer = style()->backgroundLayers(); Color bgColor = style()->backgroundColor(); + RenderObject* bodyObject = 0; if (!style()->hasBackground() && node() && node()->hasTagName(HTMLNames::htmlTag)) { // Locate the <body> element using the DOM. This is easier than trying // to crawl around a render tree with potential :before/:after content and // anonymous blocks created by inline <body> tags etc. We can locate the <body> // render object very easily via the DOM. HTMLElement* body = document()->body(); - RenderObject* bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; + bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; if (bodyObject) { bgLayer = bodyObject->style()->backgroundLayers(); bgColor = bodyObject->style()->backgroundColor(); @@ -588,7 +604,7 @@ void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty) int bw = max(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw); int bh = max(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh); - paintFillLayers(paintInfo, bgColor, bgLayer, bx, by, bw, bh); + paintFillLayers(paintInfo, bgColor, bgLayer, bx, by, bw, bh, CompositeSourceOver, bodyObject); if (style()->hasBorder() && style()->display() != INLINE) paintBorder(paintInfo.context, tx, ty, w, h, style()); @@ -653,35 +669,58 @@ void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int { // Figure out if we need to push a transparency layer to render our mask. bool pushTransparencyLayer = false; - StyleImage* maskBoxImage = style()->maskBoxImage().image(); - if (maskBoxImage && style()->maskLayers()->hasImage()) { - pushTransparencyLayer = true; - } else { - // We have to use an extra image buffer to hold the mask. Multiple mask images need - // to composite together using source-over so that they can then combine into a single unified mask that - // can be composited with the content using destination-in. SVG images need to be able to set compositing modes - // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer - // and composite that buffer as the mask. - // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering - // before pushing the transparency layer. - for (const FillLayer* fillLayer = style()->maskLayers()->next(); fillLayer; fillLayer = fillLayer->next()) { - if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) { - pushTransparencyLayer = true; - // We found one image that can be used in rendering, exit the loop - break; + bool compositedMask = hasLayer() && layer()->hasCompositedMask(); + CompositeOperator compositeOp = CompositeSourceOver; + + bool allMaskImagesLoaded = true; + + if (!compositedMask) { + StyleImage* maskBoxImage = style()->maskBoxImage().image(); + const FillLayer* maskLayers = style()->maskLayers(); + + // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content. + if (maskBoxImage) + allMaskImagesLoaded &= maskBoxImage->isLoaded(); + + if (maskLayers) + allMaskImagesLoaded &= maskLayers->imagesAreLoaded(); + + // Before all images have loaded, just use an empty transparency layer as the mask. + if (!allMaskImagesLoaded) + pushTransparencyLayer = true; + + if (maskBoxImage && maskLayers->hasImage()) { + // We have a mask-box-image and mask-image, so need to composite them together before using the result as a mask. + pushTransparencyLayer = true; + } else { + // We have to use an extra image buffer to hold the mask. Multiple mask images need + // to composite together using source-over so that they can then combine into a single unified mask that + // can be composited with the content using destination-in. SVG images need to be able to set compositing modes + // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer + // and composite that buffer as the mask. + // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering + // before pushing the transparency layer. + for (const FillLayer* fillLayer = maskLayers->next(); fillLayer; fillLayer = fillLayer->next()) { + if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) { + pushTransparencyLayer = true; + // We found one image that can be used in rendering, exit the loop + break; + } } } - } - - CompositeOperator compositeOp = CompositeDestinationIn; - if (pushTransparencyLayer) { - paintInfo.context->setCompositeOperation(CompositeDestinationIn); - paintInfo.context->beginTransparencyLayer(1.0f); - compositeOp = CompositeSourceOver; + + compositeOp = CompositeDestinationIn; + if (pushTransparencyLayer) { + paintInfo.context->setCompositeOperation(CompositeDestinationIn); + paintInfo.context->beginTransparencyLayer(1.0f); + compositeOp = CompositeSourceOver; + } } - paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp); - paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp); + if (allMaskImagesLoaded) { + paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp); + paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp); + } if (pushTransparencyLayer) paintInfo.context->endTransparencyLayer(); @@ -706,18 +745,18 @@ IntRect RenderBox::maskClipRect() return result; } -void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op) +void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) { if (!fillLayer) return; - paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op); - paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op); + paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op, backgroundObject); + paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op, backgroundObject); } -void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op) +void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) { - paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, op); + paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, op, backgroundObject); } void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*) @@ -927,17 +966,20 @@ void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool } } - if (style()->position() == FixedPosition) - fixed = true; - - RenderObject* o = container(); + bool containerSkipped; + RenderObject* o = container(repaintContainer, &containerSkipped); if (!o) return; + bool isFixedPos = style()->position() == FixedPosition; bool hasTransform = hasLayer() && layer()->transform(); - if (hasTransform) - fixed = false; // Elements with transforms act as a containing block for fixed position descendants - + if (hasTransform) { + // If this box has a transform, it acts as a fixed position container for fixed descendants, + // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. + fixed &= isFixedPos; + } else + fixed |= isFixedPos; + IntSize containerOffset = offsetFromContainer(o); bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); @@ -948,6 +990,14 @@ void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool } else transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + if (containerSkipped) { + // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe + // to just subtract the delta between the repaintContainer and o. + IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); + transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + return; + } + o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); } @@ -956,12 +1006,14 @@ void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, Transfor // We don't expect absoluteToLocal() to be called during layout (yet) ASSERT(!view() || !view()->layoutStateEnabled()); - if (style()->position() == FixedPosition) - fixed = true; - + bool isFixedPos = style()->position() == FixedPosition; bool hasTransform = hasLayer() && layer()->transform(); - if (hasTransform) - fixed = false; // Elements with transforms act as a containing block for fixed position descendants + if (hasTransform) { + // If this box has a transform, it acts as a fixed position container for fixed descendants, + // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. + fixed &= isFixedPos; + } else + fixed |= isFixedPos; RenderObject* o = container(); if (!o) @@ -1068,7 +1120,7 @@ IntRect RenderBox::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintCo if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) return IntRect(); - IntRect r = overflowRect(false); + IntRect r = visibleOverflowRect(); RenderView* v = view(); if (v) { @@ -1120,7 +1172,8 @@ void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, In if (repaintContainer == this) return; - RenderObject* o = container(); + bool containerSkipped; + RenderObject* o = container(repaintContainer, &containerSkipped); if (!o) return; @@ -1177,6 +1230,13 @@ void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, In return; } else rect.setLocation(topLeft); + + if (containerSkipped) { + // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates. + IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); + rect.move(-containerOffset); + return; + } o->computeRectForRepaint(repaintContainer, rect, fixed); } @@ -1210,7 +1270,7 @@ void RenderBox::calcWidth() } } #endif - + if (isPositioned()) { calcAbsoluteHorizontal(); return; @@ -1250,10 +1310,11 @@ void RenderBox::calcWidth() if (treatAsReplaced) #endif setWidth(max(w.value() + borderLeft() + borderRight() + paddingLeft() + paddingRight(), minPrefWidth())); + #ifdef ANDROID_LAYOUT // in SSR mode with replaced box, if the box width is wider than the container width, // it will be shrinked to fit to the container. - if (containerWidth && (width() + m_marginLeft + m_marginRight) > containerWidth && + if (containerWidth && (width() + m_marginLeft + m_marginRight) > containerWidth && document()->frame()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { m_marginLeft = m_marginRight = 0; setWidth(containerWidth); @@ -1304,8 +1365,8 @@ void RenderBox::calcWidth() } #ifdef ANDROID_LAYOUT // in SSR mode with non-replaced box, we use ANDROID_SSR_MARGIN_PADDING for left/right margin. - // If the box width is wider than the container width, it will be shrinked to fit to the container. - if (containerWidth && !treatAsReplaced && + // If the box width is wider than the container width, it will be shrinked to fit to the container. + if (containerWidth && !treatAsReplaced && document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { setWidth(width() + m_marginLeft + m_marginRight); m_marginLeft = m_marginLeft > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginLeft; @@ -1486,7 +1547,7 @@ void RenderBox::calcHeight() // height since we don't set a height in RenderView when we're printing. So without this quirk, the // height has nothing to be a percentage of, and it ends up being 0. That is bad. bool printingNeedsBaseHeight = document()->printing() && h.isPercent() - && (isRoot() || isBody() && document()->documentElement()->renderer()->style()->height().isPercent()); + && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->height().isPercent())); if (stretchesToViewHeight() || printingNeedsBaseHeight) { int margins = collapsedMarginTop() + collapsedMarginBottom(); int visHeight = document()->printing() ? view()->frameView()->visibleHeight() : view()->viewHeight(); @@ -1520,7 +1581,7 @@ int RenderBox::calcHeightUsing(const Length& h) int RenderBox::calcPercentageHeight(const Length& height) { int result = -1; - bool includeBorderPadding = isTable(); + bool skippedAutoHeightContainingBlock = false; RenderBlock* cb = containingBlock(); if (style()->htmlHacks()) { // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing @@ -1528,6 +1589,7 @@ int RenderBox::calcPercentageHeight(const Length& height) // specification, which states that percentage heights just revert to auto if the containing // block has an auto height. while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->height().isAuto()) { + skippedAutoHeightContainingBlock = true; cb = cb->containingBlock(); cb->addPercentHeightDescendant(this); } @@ -1537,25 +1599,29 @@ int RenderBox::calcPercentageHeight(const Length& height) // explicitly specified that can be used for any percentage computations. bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->height().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto())); + bool includeBorderPadding = isTable(); + // Table cells violate what the CSS spec says to do with heights. Basically we // don't care if the cell specified a height or not. We just always make ourselves // be a percentage of the cell's current content height. if (cb->isTableCell()) { - result = cb->overrideSize(); - if (result == -1) { - // Normally we would let the cell size intrinsically, but scrolling overflow has to be - // treated differently, since WinIE lets scrolled overflow regions shrink as needed. - // While we can't get all cases right, we can at least detect when the cell has a specified - // height or when the table has a specified height. In these cases we want to initially have - // no size and allow the flexing of the table or the cell to its specified height to cause us - // to grow to fill the space. This could end up being wrong in some cases, but it is - // preferable to the alternative (sizing intrinsically and making the row end up too big). - RenderTableCell* cell = toRenderTableCell(cb); - if (scrollsOverflowY() && (!cell->style()->height().isAuto() || !cell->table()->style()->height().isAuto())) - return 0; - return -1; + if (!skippedAutoHeightContainingBlock) { + result = cb->overrideSize(); + if (result == -1) { + // Normally we would let the cell size intrinsically, but scrolling overflow has to be + // treated differently, since WinIE lets scrolled overflow regions shrink as needed. + // While we can't get all cases right, we can at least detect when the cell has a specified + // height or when the table has a specified height. In these cases we want to initially have + // no size and allow the flexing of the table or the cell to its specified height to cause us + // to grow to fill the space. This could end up being wrong in some cases, but it is + // preferable to the alternative (sizing intrinsically and making the row end up too big). + RenderTableCell* cell = toRenderTableCell(cb); + if (scrollsOverflowY() && (!cell->style()->height().isAuto() || !cell->table()->style()->height().isAuto())) + return 0; + return -1; + } + includeBorderPadding = true; } - includeBorderPadding = true; } // Otherwise we only use our percentage height if our containing block had a specified // height. @@ -1926,7 +1992,7 @@ void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBoxModelO int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos) { // 'left' and 'right' cannot both be 'auto' because one would of been - // converted to the static postion already + // converted to the static position already ASSERT(!(left.isAuto() && right.isAuto())); int leftValue = 0; @@ -1960,7 +2026,7 @@ void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBoxModelO if (marginLeft.isAuto() && marginRight.isAuto()) { // Both margins auto, solve for equality if (availableSpace >= 0) { - marginLeftValue = availableSpace / 2; // split the diference + marginLeftValue = availableSpace / 2; // split the difference marginRightValue = availableSpace - marginLeftValue; // account for odd valued differences } else { // see FIXME 1 @@ -2245,7 +2311,7 @@ void RenderBox::calcAbsoluteVerticalValues(Length h, const RenderBoxModelObject* if (marginTop.isAuto() && marginBottom.isAuto()) { // Both margins auto, solve for equality // NOTE: This may result in negative values. - marginTopValue = availableSpace / 2; // split the diference + marginTopValue = availableSpace / 2; // split the difference marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences } else if (marginTop.isAuto()) { // Solve for top margin @@ -2323,7 +2389,7 @@ void RenderBox::calcAbsoluteVerticalValues(Length h, const RenderBoxModelObject* void RenderBox::calcAbsoluteHorizontalReplaced() { // The following is based off of the W3C Working Draft from April 11, 2006 of - // CSS 2.1: Section 10.3.8 "Absolutly positioned, replaced elements" + // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> // (block-style-comments in this function correspond to text from the spec and // the numbers correspond to numbers in spec) @@ -2414,7 +2480,7 @@ void RenderBox::calcAbsoluteHorizontalReplaced() int difference = availableSpace - (leftValue + rightValue); if (difference > 0) { - m_marginLeft = difference / 2; // split the diference + m_marginLeft = difference / 2; // split the difference m_marginRight = difference - m_marginLeft; // account for odd valued differences } else { // see FIXME 1 @@ -2485,7 +2551,7 @@ void RenderBox::calcAbsoluteHorizontalReplaced() // positioned, inline containing block because right now, it is using the xPos // of the first line box when really it should use the last line box. When // this is fixed elsewhere, this block should be removed. - if (containerBlock->isInline() && containerBlock->style()->direction() == RTL) { + if (containerBlock->isRenderInline() && containerBlock->style()->direction() == RTL) { const RenderInline* flow = toRenderInline(containerBlock); InlineFlowBox* firstLine = flow->firstLineBox(); InlineFlowBox* lastLine = flow->lastLineBox(); @@ -2501,7 +2567,7 @@ void RenderBox::calcAbsoluteHorizontalReplaced() void RenderBox::calcAbsoluteVerticalReplaced() { // The following is based off of the W3C Working Draft from April 11, 2006 of - // CSS 2.1: Section 10.6.5 "Absolutly positioned, replaced elements" + // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> // (block-style-comments in this function correspond to text from the spec and // the numbers correspond to numbers in spec) @@ -2565,7 +2631,7 @@ void RenderBox::calcAbsoluteVerticalReplaced() int bottomValue = 0; if (marginTop.isAuto() && marginBottom.isAuto()) { - // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combinded. + // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. ASSERT(!(top.isAuto() || bottom.isAuto())); topValue = top.calcValue(containerHeight); @@ -2646,9 +2712,9 @@ IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWid if (box) { RootInlineBox* rootBox = box->root(); - int top = rootBox->topOverflow(); + int top = rootBox->lineTop(); rect.setY(top); - rect.setHeight(rootBox->bottomOverflow() - top); + rect.setHeight(rootBox->lineBottom() - top); } // If height of box is smaller than font height, use the latter one, @@ -2701,16 +2767,11 @@ 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())); + return createVisiblePosition(node() ? firstDeepEditingPositionForNode(node()) : Position(0, 0)); int xPos = point.x(); int yPos = point.y(); @@ -2813,11 +2874,86 @@ bool RenderBox::avoidsFloats() const return isReplaced() || hasOverflowClip() || isHR(); } +void RenderBox::addShadowOverflow() +{ + int shadowLeft; + int shadowRight; + int shadowTop; + int shadowBottom; + style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); + IntRect borderBox = borderBoxRect(); + int overflowLeft = borderBox.x() + shadowLeft; + int overflowRight = borderBox.right() + shadowRight; + int overflowTop = borderBox.y() + shadowTop; + int overflowBottom = borderBox.bottom() + shadowBottom; + addVisualOverflow(IntRect(overflowLeft, overflowTop, overflowRight - overflowLeft, overflowBottom - overflowTop)); +} + +void RenderBox::addOverflowFromChild(RenderBox* child, const IntSize& delta) +{ + // Update our overflow in case the child spills out the block, but only if we were going to paint + // the child block ourselves. + if (child->hasSelfPaintingLayer()) + return; + + // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then + // its overflow is internal to it, and we don't care about it. + IntRect childLayoutOverflowRect = child->hasOverflowClip() ? child->borderBoxRect() : child->layoutOverflowRect(); + childLayoutOverflowRect.move(delta); + addLayoutOverflow(childLayoutOverflowRect); + + // Add in visual overflow from the child. Even if the child clips its overflow, it may still + // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this + // overflow if we are clipping our own overflow. + if (hasOverflowClip()) + return; + IntRect childVisualOverflowRect = child->visualOverflowRect(); + childVisualOverflowRect.move(delta); + addVisualOverflow(childVisualOverflowRect); +} + +void RenderBox::addLayoutOverflow(const IntRect& rect) +{ + IntRect borderBox = borderBoxRect(); + if (borderBox.contains(rect)) + return; + + if (!m_overflow) + m_overflow.set(new RenderOverflow(borderBox)); + + m_overflow->addLayoutOverflow(rect); +} + +void RenderBox::addVisualOverflow(const IntRect& rect) +{ + IntRect borderBox = borderBoxRect(); + if (borderBox.contains(rect)) + return; + + if (!m_overflow) + m_overflow.set(new RenderOverflow(borderBox)); + + m_overflow->addVisualOverflow(rect); +} + +void RenderBox::clearLayoutOverflow() +{ + if (!m_overflow) + return; + + if (visualOverflowRect() == borderBoxRect()) { + m_overflow.clear(); + return; + } + + m_overflow->resetLayoutOverflow(borderBoxRect()); +} + #if ENABLE(SVG) -TransformationMatrix RenderBox::localTransform() const +AffineTransform RenderBox::localTransform() const { - return TransformationMatrix(1, 0, 0, 1, x(), y()); + return AffineTransform(1, 0, 0, 1, x(), y()); } #endif diff --git a/WebCore/rendering/RenderBox.h b/WebCore/rendering/RenderBox.h index cb2297b..401a46d 100644 --- a/WebCore/rendering/RenderBox.h +++ b/WebCore/rendering/RenderBox.h @@ -24,6 +24,7 @@ #define RenderBox_h #include "RenderBoxModelObject.h" +#include "RenderOverflow.h" #include "ScrollTypes.h" namespace WebCore { @@ -73,23 +74,38 @@ public: // Bounds of the outline box in absolute coords. Respects transforms virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/) const; - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); // Use this with caution! No type checking is done! RenderBox* previousSiblingBox() const; RenderBox* nextSiblingBox() const; RenderBox* parentBox() const; - // The height of a block when you include normal flow overflow spillage out of the bottom - // of the block (e.g., a <div style="height:25px"> that has a 100px tall image inside - // it would have an overflow height of borderTop() + paddingTop() + 100px. - virtual int overflowHeight(bool /*includeInterior*/ = true) const { return height(); } - virtual int overflowWidth(bool /*includeInterior*/ = true) const { return width(); } - virtual void setOverflowHeight(int) { } - virtual void setOverflowWidth(int) { } - virtual int overflowLeft(bool /*includeInterior*/ = true) const { return 0; } - virtual int overflowTop(bool /*includeInterior*/ = true) const { return 0; } - virtual IntRect overflowRect(bool /*includeInterior*/ = true) const { return borderBoxRect(); } + IntRect visibleOverflowRect() const { return hasOverflowClip() ? visualOverflowRect() : (m_overflow ? m_overflow->visibleOverflowRect() : borderBoxRect()); } + int topVisibleOverflow() const { return hasOverflowClip() ? topVisualOverflow() : std::min(topLayoutOverflow(), topVisualOverflow()); } + int bottomVisibleOverflow() const { return hasOverflowClip() ? bottomVisualOverflow() : std::max(bottomLayoutOverflow(), bottomVisualOverflow()); } + int leftVisibleOverflow() const { return hasOverflowClip() ? leftVisualOverflow() : std::min(leftLayoutOverflow(), leftVisualOverflow()); } + int rightVisibleOverflow() const { return hasOverflowClip() ? rightVisualOverflow() : std::max(rightLayoutOverflow(), rightVisualOverflow()); } + + IntRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : borderBoxRect(); } + int topLayoutOverflow() const { return m_overflow? m_overflow->topLayoutOverflow() : 0; } + int bottomLayoutOverflow() const { return m_overflow ? m_overflow->bottomLayoutOverflow() : height(); } + int leftLayoutOverflow() const { return m_overflow ? m_overflow->leftLayoutOverflow() : 0; } + int rightLayoutOverflow() const { return m_overflow ? m_overflow->rightLayoutOverflow() : width(); } + + IntRect visualOverflowRect() const { return m_overflow ? m_overflow->visualOverflowRect() : borderBoxRect(); } + int topVisualOverflow() const { return m_overflow? m_overflow->topVisualOverflow() : 0; } + int bottomVisualOverflow() const { return m_overflow ? m_overflow->bottomVisualOverflow() : height(); } + int leftVisualOverflow() const { return m_overflow ? m_overflow->leftVisualOverflow() : 0; } + int rightVisualOverflow() const { return m_overflow ? m_overflow->rightVisualOverflow() : width(); } + + void addLayoutOverflow(const IntRect&); + void addVisualOverflow(const IntRect&); + + void addShadowOverflow(); + void addOverflowFromChild(RenderBox* child) { addOverflowFromChild(child, IntSize(child->x(), child->y())); } + void addOverflowFromChild(RenderBox* child, const IntSize& delta); + void clearLayoutOverflow(); int contentWidth() const { return clientWidth() - paddingLeft() - paddingRight(); } int contentHeight() const { return clientHeight() - paddingTop() - paddingBottom(); } @@ -229,7 +245,8 @@ public: virtual int verticalScrollbarWidth() const; int horizontalScrollbarHeight() const; - virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); + virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f, Node** stopNode = 0); + bool canBeScrolledAndHasScrollableArea() const; virtual bool canBeProgramaticallyScrolled(bool) const; virtual void autoscroll(); virtual void stopAutoscroll() { } @@ -280,7 +297,7 @@ public: virtual bool avoidsFloats() const; #if ENABLE(SVG) - virtual TransformationMatrix localTransform() const; + virtual AffineTransform localTransform() const; #endif protected: @@ -288,8 +305,8 @@ protected: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); virtual void updateBoxModelInfoFromStyle(); - void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver); - void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver); + void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject); + void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver, RenderObject* backgroundObject = 0); void paintMaskImages(const PaintInfo&, int tx, int ty, int width, int height); @@ -301,7 +318,7 @@ protected: virtual bool shouldCalculateSizeAsReplaced() const { return isReplaced() && !isInlineBlockOrInlineTable(); } - virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState&) const; virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const; private: @@ -333,9 +350,6 @@ private: // These include tables, positioned objects, floats and flexible boxes. 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). IntRect m_frameRect; @@ -360,6 +374,9 @@ protected: // For inline replaced elements, the inline box that owns us. InlineBox* m_inlineBoxWrapper; + // Our overflow information. + OwnPtr<RenderOverflow> m_overflow; + private: // Used to store state between styleWillChange and styleDidChange static bool s_hadOverflowClip; diff --git a/WebCore/rendering/RenderBoxModelObject.cpp b/WebCore/rendering/RenderBoxModelObject.cpp index 3a7ff71..f8bf05e 100644 --- a/WebCore/rendering/RenderBoxModelObject.cpp +++ b/WebCore/rendering/RenderBoxModelObject.cpp @@ -33,6 +33,7 @@ #include "RenderInline.h" #include "RenderLayer.h" #include "RenderView.h" +#include <wtf/CurrentTime.h> using namespace std; @@ -44,6 +45,140 @@ bool RenderBoxModelObject::s_wasFloating = false; bool RenderBoxModelObject::s_hadLayer = false; bool RenderBoxModelObject::s_layerWasSelfPainting = false; +static const double cInterpolationCutoff = 800. * 800.; +static const double cLowQualityTimeThreshold = 0.500; // 500 ms + +class RenderBoxModelScaleData : public Noncopyable { +public: + RenderBoxModelScaleData(RenderBoxModelObject* object, const IntSize& size, const AffineTransform& transform, double time, bool lowQualityScale) + : m_size(size) + , m_transform(transform) + , m_lastPaintTime(time) + , m_lowQualityScale(lowQualityScale) + , m_highQualityRepaintTimer(object, &RenderBoxModelObject::highQualityRepaintTimerFired) + { + } + + ~RenderBoxModelScaleData() + { + m_highQualityRepaintTimer.stop(); + } + + Timer<RenderBoxModelObject>& hiqhQualityRepaintTimer() { return m_highQualityRepaintTimer; } + + const IntSize& size() const { return m_size; } + void setSize(const IntSize& s) { m_size = s; } + double lastPaintTime() const { return m_lastPaintTime; } + void setLastPaintTime(double t) { m_lastPaintTime = t; } + bool useLowQualityScale() const { return m_lowQualityScale; } + const AffineTransform& transform() const { return m_transform; } + void setTransform(const AffineTransform& transform) { m_transform = transform; } + void setUseLowQualityScale(bool b) + { + m_highQualityRepaintTimer.stop(); + m_lowQualityScale = b; + if (b) + m_highQualityRepaintTimer.startOneShot(cLowQualityTimeThreshold); + } + +private: + IntSize m_size; + AffineTransform m_transform; + double m_lastPaintTime; + bool m_lowQualityScale; + Timer<RenderBoxModelObject> m_highQualityRepaintTimer; +}; + +class RenderBoxModelScaleObserver { +public: + static bool shouldPaintBackgroundAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const IntSize&); + + static void boxModelObjectDestroyed(RenderBoxModelObject* object) + { + if (gBoxModelObjects) { + RenderBoxModelScaleData* data = gBoxModelObjects->take(object); + delete data; + if (!gBoxModelObjects->size()) { + delete gBoxModelObjects; + gBoxModelObjects = 0; + } + } + } + + static void highQualityRepaintTimerFired(RenderBoxModelObject* object) + { + RenderBoxModelScaleObserver::boxModelObjectDestroyed(object); + object->repaint(); + } + + static HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>* gBoxModelObjects; +}; + +bool RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const IntSize& size) +{ + // If the image is not a bitmap image, then none of this is relevant and we just paint at high + // quality. + if (!image || !image->isBitmapImage()) + return false; + + // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image + // is actually being scaled. + IntSize imageSize(image->width(), image->height()); + + // Look ourselves up in the hashtable. + RenderBoxModelScaleData* data = 0; + if (gBoxModelObjects) + data = gBoxModelObjects->get(object); + + const AffineTransform& currentTransform = context->getCTM(); + bool contextIsScaled = !currentTransform.isIdentityOrTranslation(); + if (!contextIsScaled && imageSize == size) { + // There is no scale in effect. If we had a scale in effect before, we can just delete this data. + if (data) { + gBoxModelObjects->remove(object); + delete data; + } + return false; + } + + // There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case. + if (object->document()->page()->inLowQualityImageInterpolationMode()) { + double totalPixels = static_cast<double>(image->width()) * static_cast<double>(image->height()); + if (totalPixels > cInterpolationCutoff) + return true; + } + + // If there is no data yet, we will paint the first scale at high quality and record the paint time in case a second scale happens + // very soon. + if (!data) { + data = new RenderBoxModelScaleData(object, size, currentTransform, currentTime(), false); + if (!gBoxModelObjects) + gBoxModelObjects = new HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>; + gBoxModelObjects->set(object, data); + return false; + } + + // We are scaled, but we painted already at this size, so just keep using whatever mode we last painted with. + if ((!contextIsScaled || data->transform() == currentTransform) && data->size() == size) + return data->useLowQualityScale(); + + // We have data and our size just changed. If this change happened quickly, go into low quality mode and then set a repaint + // timer to paint in high quality mode. Otherwise it is ok to just paint in high quality mode. + double newTime = currentTime(); + data->setUseLowQualityScale(newTime - data->lastPaintTime() < cLowQualityTimeThreshold); + data->setLastPaintTime(newTime); + data->setTransform(currentTransform); + data->setSize(size); + return data->useLowQualityScale(); +} + +HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>* RenderBoxModelScaleObserver::gBoxModelObjects = 0; + +void RenderBoxModelObject::highQualityRepaintTimerFired(Timer<RenderBoxModelObject>*) +{ + RenderBoxModelScaleObserver::highQualityRepaintTimerFired(this); +} + RenderBoxModelObject::RenderBoxModelObject(Node* node) : RenderObject(node) , m_layer(0) @@ -55,6 +190,7 @@ RenderBoxModelObject::~RenderBoxModelObject() // Our layer should have been destroyed and cleared by now ASSERT(!hasLayer()); ASSERT(!m_layer); + RenderBoxModelScaleObserver::boxModelObjectDestroyed(this); } void RenderBoxModelObject::destroyLayer() @@ -304,9 +440,12 @@ int RenderBoxModelObject::paddingRight(bool) const } -void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op) +void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op, RenderObject* backgroundObject) { GraphicsContext* context = paintInfo.context; + if (context->paintingDisabled()) + return; + bool includeLeftEdge = box ? box->includeLeftEdge() : true; bool includeRightEdge = box ? box->includeRightEdge() : true; int bLeft = includeLeftEdge ? borderLeft() : 0; @@ -316,10 +455,14 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co bool clippedToBorderRadius = false; if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) { + IntRect borderRect(tx, ty, w, h); + + if (borderRect.isEmpty()) + return; + context->save(); IntSize topLeft, topRight, bottomLeft, bottomRight; - IntRect borderRect(tx, ty, w, h); style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); context->addRoundedRectClip(borderRect, includeLeftEdge ? topLeft : IntSize(), @@ -437,18 +580,14 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co if (baseColor.alpha() > 0) { context->save(); context->setCompositeOperation(CompositeCopy); - context->fillRect(rect, baseColor); + context->fillRect(rect, baseColor, style()->colorSpace()); context->restore(); -#ifdef ANDROID_ALLOW_TRANSPARENT_BACKGROUNDS - } -#else } else context->clearRect(rect); -#endif } if (bgColor.isValid() && bgColor.alpha() > 0) - context->fillRect(rect, bgColor); + context->fillRect(rect, bgColor, style()->colorSpace()); } // no progressive loading of the background image @@ -463,21 +602,10 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co if (!destRect.isEmpty()) { phase += destRect.location() - destOrigin; CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; - RenderObject* clientForBackgroundImage = this; - // Check if this is the root element painting a background layer propagated from <body>, - // and pass the body's renderer as the client in that case. - if (isRoot && !style()->hasBackground()) { - ASSERT(node()->hasTagName(htmlTag)); - HTMLElement* body = document()->body(); - ASSERT(body); - ASSERT(body->hasLocalName(bodyTag)); - ASSERT(body->renderer()); - if (body) { - if (RenderObject* bodyRenderer = body->renderer()) - clientForBackgroundImage = bodyRenderer; - } - } - context->drawTiledImage(bg->image(clientForBackgroundImage, tileSize), destRect, phase, tileSize, compositeOp); + RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this; + Image* image = bg->image(clientForBackgroundImage, tileSize); + bool useLowQualityScaling = RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality(context, this, image, destRect.size()); + context->drawTiledImage(image, style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling); } } @@ -493,153 +621,141 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co context->restore(); } -IntSize RenderBoxModelObject::calculateBackgroundSize(const FillLayer* bgLayer, int scaledWidth, int scaledHeight) const +IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, IntSize positioningAreaSize) 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(); + StyleImage* image = fillLayer->image(); + image->setImageContainerSize(positioningAreaSize); // Use the box established by background-origin. + + EFillSizeType type = fillLayer->size().type; + + switch (type) { + case SizeLength: { + int w = positioningAreaSize.width(); + int h = positioningAreaSize.height(); + Length layerWidth = fillLayer->size().size.width(); + Length layerHeight = fillLayer->size().size.height(); + + if (layerWidth.isFixed()) + w = layerWidth.value(); + else if (layerWidth.isPercent()) + w = layerWidth.calcValue(positioningAreaSize.width()); + + if (layerHeight.isFixed()) + h = layerHeight.value(); + else if (layerHeight.isPercent()) + h = layerHeight.calcValue(positioningAreaSize.height()); + + // If one of the values is auto we have to use the appropriate + // scale to maintain our aspect ratio. + if (layerWidth.isAuto() && !layerHeight.isAuto()) + w = image->imageSize(this, style()->effectiveZoom()).width() * h / image->imageSize(this, style()->effectiveZoom()).height(); + else if (!layerWidth.isAuto() && layerHeight.isAuto()) + h = image->imageSize(this, style()->effectiveZoom()).height() * w / image->imageSize(this, style()->effectiveZoom()).width(); + else if (layerWidth.isAuto() && layerHeight.isAuto()) { + // If both width and height are auto, we just want to use the image's + // intrinsic size. + w = image->imageSize(this, style()->effectiveZoom()).width(); + h = image->imageSize(this, style()->effectiveZoom()).height(); + } + + return IntSize(max(1, w), max(1, h)); } - - return IntSize(max(1, w), max(1, h)); - } else - return bg->imageSize(this, style()->effectiveZoom()); + case Contain: + case Cover: { + IntSize imageIntrinsicSize = image->imageSize(this, 1); + float horizontalScaleFactor = static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSize.width(); + float verticalScaleFactor = static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height(); + float scaleFactor = type == Contain ? min(horizontalScaleFactor, verticalScaleFactor) : max(horizontalScaleFactor, verticalScaleFactor); + + return IntSize(max<int>(1, imageIntrinsicSize.width() * scaleFactor), max<int>(1, imageIntrinsicSize.height() * scaleFactor)); + } + case SizeNone: + break; + } + return image->imageSize(this, style()->effectiveZoom()); } -void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* bgLayer, int tx, int ty, int w, int h, +void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fillLayer, 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 - bool fixedAttachment = bgLayer->attachment() == FixedBackgroundAttachment; + IntSize positioningAreaSize; + + // Determine the background positioning area and set destRect to the background painting area. + // destRect will be adjusted later if the background is non-repeating. + bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment; + +#if ENABLE(FAST_MOBILE_SCROLLING) + if (view()->frameView() && view()->frameView()->canBlitOnScroll()) { + // As a side effect of an optimization to blit on scroll, we do not honor the CSS + // property "background-attachment: fixed" because it may result in rendering + // artifacts. Note, these artifacts only appear if we are blitting on scroll of + // a page that has fixed background images. + fixedAttachment = false; + } +#endif + if (!fixedAttachment) { - // Scroll and Local - if (bgLayer->origin() != BorderFillBox) { + destRect = IntRect(tx, ty, w, h); + + int right = 0; + int bottom = 0; + // Scroll and Local. + if (fillLayer->origin() != BorderFillBox) { left = borderLeft(); right = borderRight(); top = borderTop(); bottom = borderBottom(); - if (bgLayer->origin() == ContentFillBox) { + if (fillLayer->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. + // its margins. Since those were added in already, we have to factor them out when computing + // the background positioning area. if (isRoot()) { - rw = toRenderBox(this)->width() - left - right; - rh = toRenderBox(this)->height() - top - bottom; + positioningAreaSize = IntSize(toRenderBox(this)->width() - left - right, 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 + positioningAreaSize = IntSize(w - left - right, h - top - bottom); } else { - // Fixed background attachment. - IntRect vr = viewRect(); - cx = vr.x(); - cy = vr.y(); - pw = vr.width(); - ph = vr.height(); + destRect = viewRect(); + positioningAreaSize = destRect.size(); } - int sx = 0; - int sy = 0; - int cw; - int ch; + tileSize = calculateFillTileSize(fillLayer, positioningAreaSize); - IntSize scaledImageSize; - if (isRoot() && !fixedAttachment) - scaledImageSize = calculateBackgroundSize(bgLayer, rw, rh); - else - scaledImageSize = calculateBackgroundSize(bgLayer, pw, ph); - - int scaledImageWidth = scaledImageSize.width(); - int scaledImageHeight = scaledImageSize.height(); + EFillRepeat backgroundRepeatX = fillLayer->repeatX(); + EFillRepeat backgroundRepeatY = fillLayer->repeatY(); - EFillRepeat backgroundRepeat = bgLayer->repeat(); - - int xPosition; - if (isRoot() && !fixedAttachment) - 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() && !fixedAttachment) - 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); + int xPosition = fillLayer->xPosition().calcMinValue(positioningAreaSize.width() - tileSize.width(), true); + if (backgroundRepeatX == RepeatFill) + phase.setX(tileSize.width() ? tileSize.width() - (xPosition + left) % tileSize.width() : 0); + else { + destRect.move(max(xPosition + left, 0), 0); + phase.setX(-min(xPosition + left, 0)); + destRect.setWidth(tileSize.width() + min(xPosition + left, 0)); } - if (fixedAttachment) { - sx += max(tx - cx, 0); - sy += max(ty - cy, 0); + int yPosition = fillLayer->yPosition().calcMinValue(positioningAreaSize.height() - tileSize.height(), true); + if (backgroundRepeatY == RepeatFill) + phase.setY(tileSize.height() ? tileSize.height() - (yPosition + top) % tileSize.height() : 0); + else { + destRect.move(0, max(yPosition + top, 0)); + phase.setY(-min(yPosition + top, 0)); + destRect.setHeight(tileSize.height() + min(yPosition + top, 0)); } - destRect = IntRect(cx, cy, cw, ch); + if (fixedAttachment) + phase.move(max(tx - destRect.x(), 0), max(ty - destRect.y(), 0)); + destRect.intersect(IntRect(tx, ty, w, h)); - phase = IntPoint(sx, sy); - tileSize = IntSize(scaledImageWidth, scaledImageHeight); } int RenderBoxModelObject::verticalPosition(bool firstLine) const @@ -729,6 +845,7 @@ bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0; Image* image = styleImage->image(this, imageSize); + ColorSpace colorSpace = style->colorSpace(); if (drawLeft) { // Paint the top and bottom left corners. @@ -736,18 +853,18 @@ bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, // 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), + graphicsContext->drawImage(image, colorSpace, 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), + graphicsContext->drawImage(image, colorSpace, 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, + graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx, ty + topWidth, leftWidth, h - topWidth - bottomWidth), IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice), Image::StretchTile, (Image::TileRule)vRule, op); @@ -758,17 +875,17 @@ bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, // 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), + graphicsContext->drawImage(image, colorSpace, 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), + graphicsContext->drawImage(image, colorSpace, 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, + graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth, h - topWidth - bottomWidth), IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice), Image::StretchTile, (Image::TileRule)vRule, op); @@ -776,20 +893,20 @@ bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, // Paint the top edge. if (drawTop) - graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth), + graphicsContext->drawTiledImage(image, colorSpace, 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, + graphicsContext->drawTiledImage(image, colorSpace, 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, + graphicsContext->drawTiledImage(image, colorSpace, 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); @@ -1221,7 +1338,7 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int shadowOffset -= extraOffset; fillRect.move(extraOffset); - context->setShadow(shadowOffset, shadowBlur, shadowColor); + context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); if (hasBorderRadius) { IntRect rectToClipOut = rect; IntSize topLeftToClipOut = topLeft; @@ -1264,7 +1381,7 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int if (!rectToClipOut.isEmpty()) context->clipOutRoundedRect(rectToClipOut, topLeftToClipOut, topRightToClipOut, bottomLeftToClipOut, bottomRightToClipOut); - context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black); + context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black, s->colorSpace()); } else { IntRect rectToClipOut = rect; @@ -1273,7 +1390,7 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path // by one pixel. if (hasOpaqueBackground) { - TransformationMatrix currentTransformation = context->getCTM(); + AffineTransform currentTransformation = context->getCTM(); if (currentTransformation.a() != 1 || (currentTransformation.d() != 1 && currentTransformation.d() != -1) || currentTransformation.b() || currentTransformation.c()) rectToClipOut.inflate(-1); @@ -1281,7 +1398,7 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int if (!rectToClipOut.isEmpty()) context->clipOut(rectToClipOut); - context->fillRect(fillRect, Color::black); + context->fillRect(fillRect, Color::black, s->colorSpace()); } context->restore(); @@ -1292,9 +1409,9 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int if (holeRect.isEmpty()) { if (hasBorderRadius) - context->fillRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight, shadowColor); + context->fillRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight, shadowColor, s->colorSpace()); else - context->fillRect(rect, shadowColor); + context->fillRect(rect, shadowColor, s->colorSpace()); continue; } if (!begin) { @@ -1343,8 +1460,8 @@ void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int context->addPath(Path::createRectangle(holeRect)); context->setFillRule(RULE_EVENODD); - context->setFillColor(fillColor); - context->setShadow(shadowOffset, shadowBlur, shadowColor); + context->setFillColor(fillColor, s->colorSpace()); + context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); context->fillPath(); context->restore(); diff --git a/WebCore/rendering/RenderBoxModelObject.h b/WebCore/rendering/RenderBoxModelObject.h index baa5ecb..db7538d 100644 --- a/WebCore/rendering/RenderBoxModelObject.h +++ b/WebCore/rendering/RenderBoxModelObject.h @@ -90,7 +90,7 @@ public: void paintBorder(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, bool begin = true, bool end = true); bool paintNinePieceImage(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, const NinePieceImage&, CompositeOperator = CompositeSourceOver); void paintBoxShadow(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, ShadowStyle, bool begin = true, bool end = true); - void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver); + void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver, RenderObject* backgroundObject = 0); // The difference between this inline's baseline position and the line's baseline position. int verticalPosition(bool firstLine) const; @@ -98,12 +98,16 @@ public: // Called by RenderObject::destroy() (and RenderWidget::destroy()) and is the only way layers should ever be destroyed void destroyLayer(); + void highQualityRepaintTimerFired(Timer<RenderBoxModelObject>*); + protected: void calculateBackgroundImageGeometry(const FillLayer*, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize); - IntSize calculateBackgroundSize(const FillLayer*, int scaledWidth, int scaledHeight) const; private: virtual bool isBoxModelObject() const { return true; } + + IntSize calculateFillTileSize(const FillLayer*, IntSize scaledSize) const; + friend class RenderView; RenderLayer* m_layer; diff --git a/WebCore/rendering/RenderButton.cpp b/WebCore/rendering/RenderButton.cpp index 6d36a0f..3ecd382 100644 --- a/WebCore/rendering/RenderButton.cpp +++ b/WebCore/rendering/RenderButton.cpp @@ -1,6 +1,4 @@ /** - * This file is part of the html renderer for KDE. - * * Copyright (C) 2005 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or @@ -52,7 +50,8 @@ void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild) if (!m_inner) { // Create an anonymous block. ASSERT(!firstChild()); - m_inner = createAnonymousBlock(); + bool isFlexibleBox = style()->display() == BOX || style()->display() == INLINE_BOX; + m_inner = createAnonymousBlock(isFlexibleBox); setupInnerStyle(m_inner->style()); RenderFlexibleBox::addChild(m_inner); } @@ -108,6 +107,7 @@ void RenderButton::setupInnerStyle(RenderStyle* innerStyle) // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is // safe to modify. innerStyle->setBoxFlex(1.0f); + innerStyle->setBoxOrient(style()->boxOrient()); innerStyle->setPaddingTop(Length(theme()->buttonInternalPaddingTop(), Fixed)); innerStyle->setPaddingRight(Length(theme()->buttonInternalPaddingRight(), Fixed)); diff --git a/WebCore/rendering/RenderButton.h b/WebCore/rendering/RenderButton.h index 3a74589..7fd6ab0 100644 --- a/WebCore/rendering/RenderButton.h +++ b/WebCore/rendering/RenderButton.h @@ -1,6 +1,4 @@ /* - * This file is part of the html renderer for KDE. - * * Copyright (C) 2005 Apple Computer * * This library is free software; you can redistribute it and/or diff --git a/WebCore/rendering/RenderCounter.cpp b/WebCore/rendering/RenderCounter.cpp index 67e5cba..3cb9a07 100644 --- a/WebCore/rendering/RenderCounter.cpp +++ b/WebCore/rendering/RenderCounter.cpp @@ -38,7 +38,7 @@ using namespace HTMLNames; typedef HashMap<RefPtr<AtomicStringImpl>, CounterNode*> CounterMap; typedef HashMap<const RenderObject*, CounterMap*> CounterMaps; -static CounterNode* counter(RenderObject*, const AtomicString& counterName, bool alwaysCreateCounter); +static CounterNode* makeCounterNode(RenderObject*, const AtomicString& identifier, bool alwaysCreateCounter); static CounterMaps& counterMaps() { @@ -53,31 +53,7 @@ static inline RenderObject* previousSiblingOrParent(RenderObject* object) return object->parent(); } -static CounterNode* lastDescendant(CounterNode* node) -{ - CounterNode* last = node->lastChild(); - if (!last) - return 0; - - while (CounterNode* lastChild = last->lastChild()) - last = lastChild; - - return last; -} - -static CounterNode* previousInPreOrder(CounterNode* node) -{ - CounterNode* previous = node->previousSibling(); - if (!previous) - return node->parent(); - - while (CounterNode* lastChild = previous->lastChild()) - previous = lastChild; - - return previous; -} - -static bool planCounter(RenderObject* object, const AtomicString& counterName, bool& isReset, int& value) +static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value) { ASSERT(object); @@ -90,7 +66,7 @@ static bool planCounter(RenderObject* object, const AtomicString& counterName, b ASSERT(style); if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) { - CounterDirectives directives = directivesMap->get(counterName.impl()); + CounterDirectives directives = directivesMap->get(identifier.impl()); if (directives.m_reset) { value = directives.m_resetValue; if (directives.m_increment) @@ -105,7 +81,7 @@ static bool planCounter(RenderObject* object, const AtomicString& counterName, b } } - if (counterName == "list-item") { + if (identifier == "list-item") { if (object->isListItem()) { if (toRenderListItem(object)->hasExplicitValue()) { value = toRenderListItem(object)->explicitValue(); @@ -133,68 +109,142 @@ static bool planCounter(RenderObject* object, const AtomicString& counterName, b return false; } -static bool findPlaceForCounter(RenderObject* object, const AtomicString& counterName, - bool isReset, CounterNode*& parent, CounterNode*& previousSibling) +// - Finds the insertion point for the counter described by counterOwner, isReset and +// identifier in the CounterNode tree for identifier and sets parent and +// previousSibling accordingly. +// - The function returns true if the counter whose insertion point is searched is NOT +// the root of the tree. +// - The root of the tree is a counter reference that is not in the scope of any other +// counter with the same identifier. +// - All the counter references with the same identifier as this one that are in +// children or subsequent siblings of the renderer that owns the root of the tree +// form the rest of of the nodes of the tree. +// - The root of the tree is always a reset type reference. +// - A subtree rooted at any reset node in the tree is equivalent to all counter +// references that are in the scope of the counter or nested counter defined by that +// reset node. +// - Non-reset CounterNodes cannot have descendants. + +static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& identifier, bool isReset, CounterNode*& parent, CounterNode*& previousSibling) { - // Find the appropriate previous sibling for insertion into the parent node - // by searching in render tree order for a child of the counter. - parent = 0; + // We cannot stop searching for counters with the same identifier before we also + // check this renderer, because it may affect the positioning in the tree of our counter. + RenderObject* searchEndRenderer = previousSiblingOrParent(counterOwner); + // We check renderers in preOrder from the renderer that our counter is attached to + // towards the begining of the document for counters with the same identifier as the one + // we are trying to find a place for. This is the next renderer to be checked. + RenderObject* currentRenderer = counterOwner->previousInPreOrder(); previousSibling = 0; - RenderObject* resetCandidate = isReset ? object->parent() : previousSiblingOrParent(object); - RenderObject* prevCounterCandidate = object; - CounterNode* candidateCounter = 0; - while ((prevCounterCandidate = prevCounterCandidate->previousInPreOrder())) { - CounterNode* c = counter(prevCounterCandidate, counterName, false); - if (prevCounterCandidate == resetCandidate) { - if (!candidateCounter) - candidateCounter = c; - if (candidateCounter) { - if (candidateCounter->isReset()) { - parent = candidateCounter; - previousSibling = 0; - } else { - parent = candidateCounter->parent(); - previousSibling = candidateCounter; + while (currentRenderer) { + CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false); + if (searchEndRenderer == currentRenderer) { + // We may be at the end of our search. + if (currentCounter) { + // We have a suitable counter on the EndSearchRenderer. + if (previousSibling) { // But we already found another counter that we come after. + if (currentCounter->actsAsReset()) { + // We found a reset counter that is on a renderer that is a sibling of ours or a parent. + if (isReset && currentRenderer->parent() == counterOwner->parent()) { + // We are also a reset counter and the previous reset was on a sibling renderer + // hence we are the next sibling of that counter if that reset is not a root or + // we are a root node if that reset is a root. + parent = currentCounter->parent(); + previousSibling = parent ? currentCounter : 0; + return parent; + } + // We are not a reset node or the previous reset must be on an ancestor of our renderer + // hence we must be a child of that reset counter. + parent = currentCounter; + ASSERT(previousSibling->parent() == currentCounter); + return true; + } + // CurrentCounter, the counter at the EndSearchRenderer, is not reset. + if (!isReset || currentRenderer->parent() != counterOwner->parent()) { + // If the node we are placing is not reset or we have found a counter that is attached + // to an ancestor of the placed counter's renderer we know we are a sibling of that node. + ASSERT(currentCounter->parent() == previousSibling->parent()); + parent = currentCounter->parent(); + return true; + } + } else { + // We are at the potential end of the search, but we had no previous sibling candidate + // In this case we follow pretty much the same logic as above but no ASSERTs about + // previousSibling, and when we are a sibling of the end counter we must set previousSibling + // to currentCounter. + if (currentCounter->actsAsReset()) { + if (isReset && currentRenderer->parent() == counterOwner->parent()) { + parent = currentCounter->parent(); + previousSibling = currentCounter; + return parent; + } + parent = currentCounter; + return true; + } + if (!isReset || currentRenderer->parent() != counterOwner->parent()) { + parent = currentCounter->parent(); + previousSibling = currentCounter; + return true; + } + previousSibling = currentCounter; } - return true; } - resetCandidate = previousSiblingOrParent(resetCandidate); - } else if (c) { - if (c->isReset()) - candidateCounter = 0; - else if (!candidateCounter) - candidateCounter = c; + // We come here if the previous sibling or parent of our renderer had no + // good counter, or we are a reset node and the counter on the previous sibling + // of our renderer was not a reset counter. + // Set a new goal for the end of the search. + searchEndRenderer = previousSiblingOrParent(currentRenderer); + } else { + // We are searching descendants of a previous sibling of the renderer that the + // counter being placed is attached to. + if (currentCounter) { + // We found a suitable counter. + if (previousSibling) { + // Since we had a suitable previous counter before, we should only consider this one as our + // previousSibling if it is a reset counter and hence the current previousSibling is its child. + if (currentCounter->actsAsReset()) { + previousSibling = currentCounter; + // We are no longer interested in previous siblings of the currentRenderer or their children + // as counters they may have attached cannot be the previous sibling of the counter we are placing. + currentRenderer = currentRenderer->parent(); + continue; + } + } else + previousSibling = currentCounter; + currentRenderer = previousSiblingOrParent(currentRenderer); + continue; + } } + // This function is designed so that the same test is not done twice in an iteration, except for this one + // which may be done twice in some cases. Rearranging the decision points though, to accommodate this + // performance improvement would create more code duplication than is worthwhile in my oppinion and may further + // impede the readability of this already complex algorithm. + if (previousSibling) + currentRenderer = previousSiblingOrParent(currentRenderer); + else + currentRenderer = currentRenderer->previousInPreOrder(); } - return false; } -static CounterNode* counter(RenderObject* object, const AtomicString& counterName, bool alwaysCreateCounter) +static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& identifier, bool alwaysCreateCounter) { ASSERT(object); if (object->m_hasCounterNodeMap) if (CounterMap* nodeMap = counterMaps().get(object)) - if (CounterNode* node = nodeMap->get(counterName.impl())) + if (CounterNode* node = nodeMap->get(identifier.impl())) return node; bool isReset = false; int value = 0; - if (!planCounter(object, counterName, isReset, value) && !alwaysCreateCounter) + if (!planCounter(object, identifier, isReset, value) && !alwaysCreateCounter) return 0; CounterNode* newParent = 0; CounterNode* newPreviousSibling = 0; - CounterNode* newNode; - if (findPlaceForCounter(object, counterName, isReset, newParent, newPreviousSibling)) { - newNode = new CounterNode(object, isReset, value); - newParent->insertAfter(newNode, newPreviousSibling); - } else { - // Make a reset node for counters that aren't inside an existing reset node. - newNode = new CounterNode(object, true, value); - } - + CounterNode* newNode = new CounterNode(object, isReset, value); + if (findPlaceForCounter(object, identifier, isReset, newParent, newPreviousSibling)) + newParent->insertAfter(newNode, newPreviousSibling, identifier); CounterMap* nodeMap; if (object->m_hasCounterNodeMap) nodeMap = counterMaps().get(object); @@ -203,8 +253,30 @@ static CounterNode* counter(RenderObject* object, const AtomicString& counterNam counterMaps().set(object, nodeMap); object->m_hasCounterNodeMap = true; } - nodeMap->set(counterName.impl(), newNode); - + nodeMap->set(identifier.impl(), newNode); + if (newNode->parent() || !object->nextInPreOrder(object->parent())) + return newNode; + // Checking if some nodes that were previously counter tree root nodes + // should become children of this node now. + CounterMaps& maps = counterMaps(); + RenderObject* stayWithin = object->parent(); + for (RenderObject* currentRenderer = object->nextInPreOrder(stayWithin); currentRenderer; currentRenderer = currentRenderer->nextInPreOrder(stayWithin)) { + if (!currentRenderer->m_hasCounterNodeMap) + continue; + CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()); + if (!currentCounter) + continue; + if (currentCounter->parent()) { + ASSERT(newNode->firstChild()); + if (currentRenderer->lastChild()) + currentRenderer = currentRenderer->lastChild(); + continue; + } + if (stayWithin != currentRenderer->parent() || !currentCounter->hasResetType()) + newNode->insertAfter(currentCounter, newNode->lastChild(), identifier); + if (currentRenderer->lastChild()) + currentRenderer = currentRenderer->lastChild(); + } return newNode; } @@ -231,15 +303,15 @@ PassRefPtr<StringImpl> RenderCounter::originalText() const return 0; if (!m_counterNode) - m_counterNode = counter(parent(), m_counter.identifier(), true); + m_counterNode = makeCounterNode(parent(), m_counter.identifier(), true); CounterNode* child = m_counterNode; - int value = child->isReset() ? child->value() : child->countInParent(); + int value = child->actsAsReset() ? child->value() : child->countInParent(); String text = listMarkerText(m_counter.listStyle(), value); if (!m_counter.separator().isNull()) { - if (!child->isReset()) + if (!child->actsAsReset()) child = child->parent(); while (CounterNode* parent = child->parent()) { text = listMarkerText(m_counter.listStyle(), child->countInParent()) @@ -257,47 +329,156 @@ void RenderCounter::calcPrefWidths(int lead) RenderText::calcPrefWidths(lead); } -void RenderCounter::invalidate() +void RenderCounter::invalidate(const AtomicString& identifier) { + if (m_counter.identifier() != identifier) + return; m_counterNode = 0; setNeedsLayoutAndPrefWidthsRecalc(); } -static void destroyCounterNodeChildren(AtomicStringImpl* identifier, CounterNode* node) +static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node) { CounterNode* previous; - for (CounterNode* child = lastDescendant(node); child && child != node; child = previous) { - previous = previousInPreOrder(child); - child->parent()->removeChild(child); - ASSERT(counterMaps().get(child->renderer())->get(identifier) == child); - counterMaps().get(child->renderer())->remove(identifier); + for (CounterNode* child = node->lastDescendant(); child && child != node; child = previous) { + previous = child->previousInPreOrder(); + child->parent()->removeChild(child, identifier); + ASSERT(counterMaps().get(child->renderer())->get(identifier.impl()) == child); + counterMaps().get(child->renderer())->remove(identifier.impl()); if (!child->renderer()->documentBeingDestroyed()) { RenderObjectChildList* children = child->renderer()->virtualChildren(); if (children) - children->invalidateCounters(child->renderer()); + children->invalidateCounters(child->renderer(), identifier); } delete child; } + RenderObject* renderer = node->renderer(); + if (!renderer->documentBeingDestroyed()) { + if (RenderObjectChildList* children = renderer->virtualChildren()) + children->invalidateCounters(renderer, identifier); + } + if (CounterNode* parent = node->parent()) + parent->removeChild(node, identifier); + delete node; } -void RenderCounter::destroyCounterNodes(RenderObject* object) +void RenderCounter::destroyCounterNodes(RenderObject* renderer) { CounterMaps& maps = counterMaps(); - CounterMap* map = maps.get(object); - if (!map) + CounterMaps::iterator mapsIterator = maps.find(renderer); + if (mapsIterator == maps.end()) return; - maps.remove(object); - + CounterMap* map = mapsIterator->second; CounterMap::const_iterator end = map->end(); for (CounterMap::const_iterator it = map->begin(); it != end; ++it) { - CounterNode* node = it->second; - destroyCounterNodeChildren(it->first.get(), node); - if (CounterNode* parent = node->parent()) - parent->removeChild(node); - delete node; + AtomicString identifier(it->first.get()); + destroyCounterNodeWithoutMapRemoval(identifier, it->second); } - + maps.remove(mapsIterator); delete map; + renderer->m_hasCounterNodeMap = false; +} + +void RenderCounter::destroyCounterNode(RenderObject* renderer, const AtomicString& identifier) +{ + CounterMap* map = counterMaps().get(renderer); + if (!map) + return; + CounterMap::iterator mapIterator = map->find(identifier.impl()); + if (mapIterator == map->end()) + return; + destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second); + map->remove(mapIterator); + // We do not delete "map" here even if empty because we expect to reuse + // it soon. In order for a renderer to lose all its counters permanently, + // a style change for the renderer involving removal of all counter + // directives must occur, in which case, RenderCounter::destroyCounterNodes() + // must be called. + // The destruction of the Renderer (possibly caused by the removal of its + // associated DOM node) is the other case that leads to the permanent + // destruction of all counters attached to a Renderer. In this case + // RenderCounter::destroyCounterNodes() must be and is now called, too. + // RenderCounter::destroyCounterNodes() handles destruction of the counter + // map associated with a renderer, so there is no risk in leaking the map. +} + +static void updateCounters(RenderObject* renderer) +{ + ASSERT(renderer->style()); + const CounterDirectiveMap* directiveMap = renderer->style()->counterDirectives(); + if (!directiveMap) + return; + CounterDirectiveMap::const_iterator end = directiveMap->end(); + if (!renderer->m_hasCounterNodeMap) { + for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) + makeCounterNode(renderer, AtomicString(it->first.get()), false); + return; + } + CounterMap* counterMap = counterMaps().get(renderer); + ASSERT(counterMap); + for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) { + CounterNode* node = counterMap->get(it->first.get()); + if (!node) { + makeCounterNode(renderer, AtomicString(it->first.get()), false); + continue; + } + CounterNode* newParent = 0; + CounterNode* newPreviousSibling; + findPlaceForCounter(renderer, AtomicString(it->first.get()), node->hasResetType(), newParent, newPreviousSibling); + CounterNode* parent = node->parent(); + if (newParent == parent && newPreviousSibling == node->previousSibling()) + continue; + if (parent) + parent->removeChild(node, it->first.get()); + if (newParent) + newParent->insertAfter(node, newPreviousSibling, it->first.get()); + } +} + +void RenderCounter::rendererSubtreeAttached(RenderObject* renderer) +{ + for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer)) + updateCounters(descendant); +} + +void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle) +{ + const CounterDirectiveMap* newCounterDirectives; + const CounterDirectiveMap* oldCounterDirectives; + if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) { + if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { + CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); + CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end(); + for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { + CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->first); + if (oldMapIt != oldMapEnd) { + if (oldMapIt->second == it->second) + continue; + RenderCounter::destroyCounterNode(renderer, it->first.get()); + } + // We must create this node here, because the changed node may be a node with no display such as + // as those created by the increment or reset directives and the re-layout that will happen will + // not catch the change if the node had no children. + makeCounterNode(renderer, it->first.get(), false); + } + // Destroying old counters that do not exist in the new counterDirective map. + for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) { + if (!newCounterDirectives->contains(it->first)) + RenderCounter::destroyCounterNode(renderer, it->first.get()); + } + } else { + if (renderer->m_hasCounterNodeMap) + RenderCounter::destroyCounterNodes(renderer); + } + } else if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { + CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); + for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { + // We must create this node here, because the added node may be a node with no display such as + // as those created by the increment or reset directives and the re-layout that will happen will + // not catch the change if the node had no children. + makeCounterNode(renderer, it->first.get(), false); + } + } } } // namespace WebCore diff --git a/WebCore/rendering/RenderCounter.h b/WebCore/rendering/RenderCounter.h index 961968e..10ba1dc 100644 --- a/WebCore/rendering/RenderCounter.h +++ b/WebCore/rendering/RenderCounter.h @@ -33,9 +33,16 @@ class RenderCounter : public RenderText { public: RenderCounter(Document*, const CounterContent&); - void invalidate(); + // Removes the reference to the CounterNode associated with this renderer + // if its identifier matches the argument. + // This is used to cause a counter display update when the CounterNode + // tree for identifier changes. + void invalidate(const AtomicString& identifier); static void destroyCounterNodes(RenderObject*); + static void destroyCounterNode(RenderObject*, const AtomicString& identifier); + static void rendererSubtreeAttached(RenderObject*); + static void rendererStyleChanged(RenderObject*, const RenderStyle* oldStyle, const RenderStyle* newStyle); private: virtual const char* renderName() const; diff --git a/WebCore/rendering/RenderEmbeddedObject.cpp b/WebCore/rendering/RenderEmbeddedObject.cpp new file mode 100644 index 0000000..db32808 --- /dev/null +++ b/WebCore/rendering/RenderEmbeddedObject.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderEmbeddedObject.h" + +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "HTMLEmbedElement.h" +#include "HTMLIFrameElement.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" +#include "HTMLParamElement.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "PluginWidget.h" +#include "RenderView.h" +#include "RenderWidgetProtector.h" +#include "Text.h" + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLVideoElement.h" +#endif + +#if USE(ACCELERATED_COMPOSITING) +#include "PluginWidget.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +RenderEmbeddedObject::RenderEmbeddedObject(Element* element) + : RenderPartObject(element) +{ + view()->frameView()->setIsVisuallyNonEmpty(); +} + +RenderEmbeddedObject::~RenderEmbeddedObject() +{ + if (frameView()) + frameView()->removeWidgetToUpdate(this); +} + +#if USE(ACCELERATED_COMPOSITING) +bool RenderEmbeddedObject::requiresLayer() const +{ + if (RenderPartObject::requiresLayer()) + return true; + + return allowsAcceleratedCompositing(); +} + +bool RenderEmbeddedObject::allowsAcceleratedCompositing() const +{ + return widget() && widget()->isPluginWidget() && static_cast<PluginWidget*>(widget())->platformLayer(); +} +#endif + +static bool isURLAllowed(Document* doc, const String& url) +{ + if (doc->frame()->page()->frameCount() >= 200) + return false; + + // We allow one level of self-reference because some sites depend on that. + // But we don't allow more than one. + KURL completeURL = doc->completeURL(url); + bool foundSelfReference = false; + for (Frame* frame = doc->frame(); frame; frame = frame->tree()->parent()) { + if (equalIgnoringFragmentIdentifier(frame->loader()->url(), completeURL)) { + if (foundSelfReference) + return false; + foundSelfReference = true; + } + } + return true; +} + +typedef HashMap<String, String, CaseFoldingHash> ClassIdToTypeMap; + +static ClassIdToTypeMap* createClassIdToTypeMap() +{ + ClassIdToTypeMap* map = new ClassIdToTypeMap; + map->add("clsid:D27CDB6E-AE6D-11CF-96B8-444553540000", "application/x-shockwave-flash"); + map->add("clsid:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA", "audio/x-pn-realaudio-plugin"); + map->add("clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B", "video/quicktime"); + map->add("clsid:166B1BCA-3F9C-11CF-8075-444553540000", "application/x-director"); + map->add("clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6", "application/x-mplayer2"); + map->add("clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95", "application/x-mplayer2"); + return map; +} + +static String serviceTypeForClassId(const String& classId) +{ + // Return early if classId is empty (since we won't do anything below). + // Furthermore, if classId is null, calling get() below will crash. + if (classId.isEmpty()) + return String(); + + static ClassIdToTypeMap* map = createClassIdToTypeMap(); + return map->get(classId); +} + +static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues) +{ + // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP + // require "src" attribute). + int srcIndex = -1, dataIndex = -1; + for (unsigned int i = 0; i < paramNames->size(); ++i) { + if (equalIgnoringCase((*paramNames)[i], "src")) + srcIndex = i; + else if (equalIgnoringCase((*paramNames)[i], "data")) + dataIndex = i; + } + + if (srcIndex == -1 && dataIndex != -1) { + paramNames->append("src"); + paramValues->append((*paramValues)[dataIndex]); + } +} + +void RenderEmbeddedObject::updateWidget(bool onlyCreateNonNetscapePlugins) +{ + String url; + String serviceType; + Vector<String> paramNames; + Vector<String> paramValues; + Frame* frame = frameView()->frame(); + + // The calls to FrameLoader::requestObject within this function can result in a plug-in being initialized. + // This can run cause arbitrary JavaScript to run and may result in this RenderObject being detached from + // the render tree and destroyed, causing a crash like <rdar://problem/6954546>. By extending our lifetime + // artifically to ensure that we remain alive for the duration of plug-in initialization. + RenderWidgetProtector protector(this); + + if (node()->hasTagName(objectTag)) { + HTMLObjectElement* objectElement = static_cast<HTMLObjectElement*>(node()); + + objectElement->setNeedWidgetUpdate(false); + if (!objectElement->isFinishedParsingChildren()) + return; + + // Check for a child EMBED tag. + HTMLEmbedElement* embed = 0; + for (Node* child = objectElement->firstChild(); child; ) { + if (child->hasTagName(embedTag)) { + embed = static_cast<HTMLEmbedElement*>(child); + break; + } + + if (child->hasTagName(objectTag)) + child = child->nextSibling(); // Don't descend into nested OBJECT tags + else + child = child->traverseNextNode(objectElement); // Otherwise descend (EMBEDs may be inside COMMENT tags) + } + + // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT. + HTMLElement* embedOrObject; + if (embed) { + embedOrObject = embed; + url = embed->url(); + serviceType = embed->serviceType(); + } else + embedOrObject = objectElement; + + // If there was no URL or type defined in EMBED, try the OBJECT tag. + if (url.isEmpty()) + url = objectElement->url(); + if (serviceType.isEmpty()) + serviceType = objectElement->serviceType(); + + HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; + + // Scan the PARAM children. + // Get the URL and type from the params if we don't already have them. + // Get the attributes from the params if there is no EMBED tag. + Node* child = objectElement->firstChild(); + while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) { + if (child->hasTagName(paramTag)) { + HTMLParamElement* p = static_cast<HTMLParamElement*>(child); + String name = p->name(); + if (url.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) + url = p->value(); + if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { + serviceType = p->value(); + int pos = serviceType.find(";"); + if (pos != -1) + serviceType = serviceType.left(pos); + } + if (!embed && !name.isEmpty()) { + uniqueParamNames.add(name.impl()); + paramNames.append(p->name()); + paramValues.append(p->value()); + } + } + child = child->nextSibling(); + } + + // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag + // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is + // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means + // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, + // else our Java plugin will misinterpret it. [4004531] + String codebase; + if (!embed && MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { + codebase = "codebase"; + uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already + } + + // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values. + NamedNodeMap* attributes = embedOrObject->attributes(); + if (attributes) { + for (unsigned i = 0; i < attributes->length(); ++i) { + Attribute* it = attributes->attributeItem(i); + const AtomicString& name = it->name().localName(); + if (embed || !uniqueParamNames.contains(name.impl())) { + paramNames.append(name.string()); + paramValues.append(it->value().string()); + } + } + } + + mapDataParamToSrc(¶mNames, ¶mValues); + + // If we still don't have a type, try to map from a specific CLASSID to a type. + if (serviceType.isEmpty()) + serviceType = serviceTypeForClassId(objectElement->classId()); + + if (!isURLAllowed(document(), url)) + return; + + // Find out if we support fallback content. + m_hasFallbackContent = false; + for (Node* child = objectElement->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) { + if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) // Discount <embed> and <param> + || (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace())) + m_hasFallbackContent = true; + } + + if (onlyCreateNonNetscapePlugins) { + KURL completedURL; + if (!url.isEmpty()) + completedURL = frame->loader()->completeURL(url); + + if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) + return; + } + + bool success = objectElement->dispatchBeforeLoadEvent(url) && frame->loader()->requestObject(this, url, objectElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); + if (!success && m_hasFallbackContent) + objectElement->renderFallbackContent(); + + } else if (node()->hasTagName(embedTag)) { + HTMLEmbedElement* embedElement = static_cast<HTMLEmbedElement*>(node()); + embedElement->setNeedWidgetUpdate(false); + url = embedElement->url(); + serviceType = embedElement->serviceType(); + + if (url.isEmpty() && serviceType.isEmpty()) + return; + if (!isURLAllowed(document(), url)) + return; + + // add all attributes set on the embed object + NamedNodeMap* attributes = embedElement->attributes(); + if (attributes) { + for (unsigned i = 0; i < attributes->length(); ++i) { + Attribute* it = attributes->attributeItem(i); + paramNames.append(it->name().localName().string()); + paramValues.append(it->value().string()); + } + } + + if (onlyCreateNonNetscapePlugins) { + KURL completedURL; + if (!url.isEmpty()) + completedURL = frame->loader()->completeURL(url); + + if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) + return; + } + + if (embedElement->dispatchBeforeLoadEvent(url)) + frame->loader()->requestObject(this, url, embedElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); + } +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + else if (node()->hasTagName(videoTag) || node()->hasTagName(audioTag)) { + HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node()); + + mediaElement->setNeedWidgetUpdate(false); + if (node()->hasTagName(videoTag)) { + HTMLVideoElement* vid = static_cast<HTMLVideoElement*>(node()); + String poster = vid->poster(); + if (!poster.isEmpty()) { + paramNames.append("_media_element_poster_"); + paramValues.append(poster); + } + } + + url = mediaElement->initialURL(); + if (!url.isEmpty()) { + paramNames.append("_media_element_src_"); + paramValues.append(url); + } + + serviceType = "application/x-media-element-proxy-plugin"; + + if (mediaElement->dispatchBeforeLoadEvent(url)) + frame->loader()->requestObject(this, url, nullAtom, serviceType, paramNames, paramValues); + } +#endif +} + +void RenderEmbeddedObject::layout() +{ + ASSERT(needsLayout()); + + calcWidth(); + calcHeight(); + + RenderPart::layout(); + + m_overflow.clear(); + addShadowOverflow(); + + if (!widget() && frameView()) + frameView()->addWidgetToUpdate(this); + + setNeedsLayout(false); +} + +} diff --git a/WebCore/rendering/RenderEmbeddedObject.h b/WebCore/rendering/RenderEmbeddedObject.h new file mode 100644 index 0000000..bdaea92 --- /dev/null +++ b/WebCore/rendering/RenderEmbeddedObject.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderEmbeddedObject_h +#define RenderEmbeddedObject_h + +#include "RenderPartObject.h" + +namespace WebCore { + +// Renderer for embeds and objects. +class RenderEmbeddedObject : public RenderPartObject { +public: + RenderEmbeddedObject(Element*); + virtual ~RenderEmbeddedObject(); + + void updateWidget(bool onlyCreateNonNetscapePlugins); + +#if USE(ACCELERATED_COMPOSITING) + virtual bool allowsAcceleratedCompositing() const; +#endif + +private: + virtual const char* renderName() const { return "RenderEmbeddedObject"; } + virtual bool isEmbeddedObject() const { return true; } + +#if USE(ACCELERATED_COMPOSITING) + virtual bool requiresLayer() const; +#endif + + virtual void layout(); +}; + +inline RenderEmbeddedObject* toRenderEmbeddedObject(RenderObject* object) +{ + ASSERT(!object || !strcmp(object->renderName(), "RenderEmbeddedObject")); + return static_cast<RenderEmbeddedObject*>(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderEmbeddedObject(const RenderEmbeddedObject*); + +} // namespace WebCore + +#endif // RenderEmbeddedObject_h diff --git a/WebCore/rendering/RenderFieldset.cpp b/WebCore/rendering/RenderFieldset.cpp index 437991a..889b0bc 100644 --- a/WebCore/rendering/RenderFieldset.cpp +++ b/WebCore/rendering/RenderFieldset.cpp @@ -1,6 +1,4 @@ /* - * 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) @@ -108,10 +106,11 @@ RenderBox* RenderFieldset::findLegend() const { for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) { if (!legend->isFloatingOrPositioned() && legend->node() && - legend->node()->hasTagName(legendTag) + (legend->node()->hasTagName(legendTag) #if ENABLE(WML) || legend->node()->hasTagName(WMLNames::insertedLegendTag) #endif + ) ) return toRenderBox(legend); } @@ -120,6 +119,9 @@ RenderBox* RenderFieldset::findLegend() const void RenderFieldset::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) { + if (!shouldPaintWithinRoot(paintInfo)) + return; + int w = width(); int h = height(); RenderBox* legend = findLegend(); diff --git a/WebCore/rendering/RenderFileUploadControl.cpp b/WebCore/rendering/RenderFileUploadControl.cpp index 9b5579c..59cbacf 100644 --- a/WebCore/rendering/RenderFileUploadControl.cpp +++ b/WebCore/rendering/RenderFileUploadControl.cpp @@ -21,6 +21,7 @@ #include "config.h" #include "RenderFileUploadControl.h" +#include "Chrome.h" #include "FileList.h" #include "Frame.h" #include "FrameView.h" @@ -63,8 +64,13 @@ private: RenderFileUploadControl::RenderFileUploadControl(HTMLInputElement* input) : RenderBlock(input) , m_button(0) - , m_fileChooser(FileChooser::create(this, input->value())) { + FileList* list = input->files(); + Vector<String> filenames; + unsigned length = list ? list->length() : 0; + for (unsigned i = 0; i < length; ++i) + filenames.append(list->item(i)->path()); + m_fileChooser = FileChooser::create(this, filenames); } RenderFileUploadControl::~RenderFileUploadControl() @@ -103,6 +109,11 @@ bool RenderFileUploadControl::allowsMultipleFiles() return !input->getAttribute(multipleAttr).isNull(); } +String RenderFileUploadControl::acceptTypes() +{ + return static_cast<HTMLInputElement*>(node())->accept(); +} + void RenderFileUploadControl::click() { Frame* frame = node()->document()->frame(); @@ -134,11 +145,7 @@ void RenderFileUploadControl::updateFromElement() addChild(renderer); } -#ifndef ANDROID_DISABLE_UPLOAD m_button->setDisabled(!theme()->isEnabled(this)); -#else - m_button->setDisabled(true); -#endif // This only supports clearing out the files, but that's OK because for // security reasons that's the only change the DOM is allowed to make. @@ -188,7 +195,7 @@ void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty) } if (paintInfo.phase == PaintPhaseForeground) { - const String& displayedFilename = m_fileChooser->basenameForWidth(style()->font(), maxFilenameWidth()); + const String& displayedFilename = fileTextValue(); unsigned length = displayedFilename.length(); const UChar* string = displayedFilename.characters(); TextRun textRun(string, length, false, 0, 0, style()->direction() == RTL, style()->unicodeBidi() == Override); @@ -208,7 +215,7 @@ void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty) + buttonRenderer->marginTop() + buttonRenderer->borderTop() + buttonRenderer->paddingTop() + buttonRenderer->baselinePosition(true, false); - paintInfo.context->setFillColor(style()->color()); + paintInfo.context->setFillColor(style()->color(), style()->colorSpace()); // Draw the filename paintInfo.context->drawBidiText(style()->font(), textRun, IntPoint(textX, textY)); @@ -288,7 +295,7 @@ String RenderFileUploadControl::buttonValue() return m_button->value(); } -String RenderFileUploadControl::fileTextValue() +String RenderFileUploadControl::fileTextValue() const { return m_fileChooser->basenameForWidth(style()->font(), maxFilenameWidth()); } diff --git a/WebCore/rendering/RenderFileUploadControl.h b/WebCore/rendering/RenderFileUploadControl.h index bd7d62a..dcdce4d 100644 --- a/WebCore/rendering/RenderFileUploadControl.h +++ b/WebCore/rendering/RenderFileUploadControl.h @@ -37,6 +37,8 @@ public: RenderFileUploadControl(HTMLInputElement*); virtual ~RenderFileUploadControl(); + virtual bool isFileUploadControl() const { return true; } + void click(); void valueChanged(); @@ -44,9 +46,10 @@ public: void receiveDroppedFiles(const Vector<String>&); String buttonValue(); - String fileTextValue(); + String fileTextValue() const; bool allowsMultipleFiles(); + String acceptTypes(); private: virtual const char* renderName() const { return "RenderFileUploadControl"; } @@ -66,10 +69,16 @@ private: inline RenderFileUploadControl* toRenderFileUploadControl(RenderObject* object) { - ASSERT(!object || !strcmp(object->renderName(), "RenderFileUploadControl")); + ASSERT(!object || object->isFileUploadControl()); return static_cast<RenderFileUploadControl*>(object); } +inline const RenderFileUploadControl* toRenderFileUploadControl(const RenderObject* object) +{ + ASSERT(!object || object->isFileUploadControl()); + return static_cast<const RenderFileUploadControl*>(object); +} + // This will catch anyone doing an unnecessary cast. void toRenderFileUploadControl(const RenderFileUploadControl*); diff --git a/WebCore/rendering/RenderFlexibleBox.cpp b/WebCore/rendering/RenderFlexibleBox.cpp index 6bbcc43..6882097 100644 --- a/WebCore/rendering/RenderFlexibleBox.cpp +++ b/WebCore/rendering/RenderFlexibleBox.cpp @@ -218,14 +218,15 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren) int previousWidth = width(); int previousHeight = height(); - + #ifdef ANDROID_LAYOUT int previousVisibleWidth = m_visibleWidth; #endif calcWidth(); calcHeight(); - m_overflowWidth = width(); + + m_overflow.clear(); if (previousWidth != width() || previousHeight != height() || (parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL && @@ -241,7 +242,6 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren) #endif setHeight(0); - m_overflowHeight = 0; m_flexingChildren = m_stretchingChildren = false; initMaxMarginValues(); @@ -259,15 +259,8 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren) else layoutVerticalBox(relayoutChildren); - int oldHeight = height(); calcHeight(); - if (oldHeight != height()) { - // If the block got expanded in size, then increase our overflowheight to match. - if (m_overflowHeight > height()) - m_overflowHeight -= (borderBottom() + paddingBottom() + horizontalScrollbarHeight()); - if (m_overflowHeight < height()) - m_overflowHeight = height(); - } + if (previousHeight != height()) relayoutChildren = true; @@ -290,31 +283,14 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren) setMaxTopMargins(pos, neg); setMaxBottomMargins(0, 0); } + + // Add in the overflow from children. + FlexBoxIterator iterator(this); + for (RenderBox* child = iterator.first(); child; child = iterator.next()) + addOverflowFromChild(child); - // Always ensure our overflow width is at least as large as our width. - if (m_overflowWidth < width()) - m_overflowWidth = width(); - - if (!hasOverflowClip()) { - int shadowLeft; - int shadowRight; - int shadowTop; - int shadowBottom; - style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); - - m_overflowLeft = min(m_overflowLeft, shadowLeft); - m_overflowWidth = max(m_overflowWidth, width() + shadowRight); - m_overflowTop = min(m_overflowTop, shadowTop); - m_overflowHeight = max(m_overflowHeight, height() + shadowBottom); - - if (hasReflection()) { - IntRect reflection(reflectionBox()); - m_overflowTop = min(m_overflowTop, reflection.y()); - m_overflowHeight = max(m_overflowHeight, reflection.bottom()); - m_overflowLeft = min(m_overflowLeft, reflection.x()); - m_overflowHeight = max(m_overflowWidth, reflection.right()); - } - } + // Add visual overflow from box-shadow and reflections. + addShadowOverflow(); statePusher.pop(); @@ -363,7 +339,7 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) int oldHeight = 0; int remainingSpace = 0; - m_overflowHeight = height(); + FlexBoxIterator iterator(this); unsigned int highestFlexGroup = 0; @@ -380,7 +356,7 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) do { // Reset our height. setHeight(yPos); - m_overflowHeight = height(); + xPos = borderLeft() + paddingLeft(); // Our first pass is done without flexing. We simply lay the children @@ -431,10 +407,6 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) setHeight(height() + lineHeight(true, true)); setHeight(height() + toAdd); - - // Always make sure our overflowheight is at least our height. - if (m_overflowHeight < height()) - m_overflowHeight = height(); oldHeight = height(); calcHeight(); @@ -454,8 +426,13 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) child->layer()->setStaticX(xPos); else child->layer()->setStaticX(width() - xPos); } - if (child->style()->hasStaticY()) - child->layer()->setStaticY(yPos); + if (child->style()->hasStaticY()) { + RenderLayer* childLayer = child->layer(); + if (childLayer->staticY() != yPos) { + child->layer()->setStaticY(yPos); + child->setChildNeedsLayout(true, false); + } + } child = iterator.next(); continue; } @@ -493,12 +470,6 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) } placeChild(child, xPos, childY); - - if (child->isRenderBlock()) - 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)); xPos += child->width() + child->marginRight(); @@ -662,22 +633,6 @@ void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) } } - child = iterator.first(); - while (child && child->isPositioned()) { - child = iterator.next(); - } - - if (child) { - m_overflowLeft = min(child->x() + child->overflowLeft(false), m_overflowLeft); - - RenderBox* lastChild = child; - while ((child = iterator.next())) { - if (!child->isPositioned()) - lastChild = child; - } - m_overflowWidth = max(lastChild->x() + lastChild->overflowWidth(false), m_overflowWidth); - } - // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of // a height change, we revert our height back to the intrinsic height before returning. if (heightSpecified) @@ -706,7 +661,7 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of // mainstream block layout); this is not really part of the XUL box model. - bool haveLineClamp = style()->lineClamp() >= 0 && style()->lineClamp() <= 100; + bool haveLineClamp = !style()->lineClamp().isNone(); if (haveLineClamp) { int maxLineCount = 0; child = iterator.first(); @@ -731,7 +686,8 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) // Get the # of lines and then alter all block flow children with auto height to use the // specified height. We always try to leave room for at least one line. - int numVisibleLines = max(1, static_cast<int>((maxLineCount + 1) * style()->lineClamp() / 100.0)); + LineClampValue lineClamp = style()->lineClamp(); + int numVisibleLines = lineClamp.isPercentage() ? max(1, (maxLineCount + 1) * lineClamp.value() / 100) : lineClamp.value(); if (numVisibleLines < maxLineCount) { for (child = iterator.first(); child; child = iterator.next()) { if (child->isPositioned() || !child->style()->height().isAuto() || !child->isBlockFlow()) @@ -762,28 +718,25 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) if (!lastLine) continue; - // See if the last item is an anchor - InlineBox* anchorBox = lastLine->lastChild(); - if (!anchorBox) - continue; - if (!anchorBox->renderer()->node()) - continue; - if (!anchorBox->renderer()->node()->isLink()) - continue; - RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines-1); if (!lastVisibleLine) continue; const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' }; DEFINE_STATIC_LOCAL(AtomicString, ellipsisAndSpaceStr, (ellipsisAndSpace, 2)); - + DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); const Font& font = style(numVisibleLines == 1)->font(); - int ellipsisAndSpaceWidth = font.width(TextRun(ellipsisAndSpace, 2)); - // Get ellipsis width + " " + anchor width - int totalWidth = ellipsisAndSpaceWidth + anchorBox->width(); - + // Get ellipsis width, and if the last child is an anchor, it will go after the ellipsis, so add in a space and the anchor width too + int totalWidth; + InlineBox* anchorBox = lastLine->lastChild(); + if (anchorBox && anchorBox->renderer()->node() && anchorBox->renderer()->node()->isLink()) + totalWidth = anchorBox->width() + font.width(TextRun(ellipsisAndSpace, 2)); + else { + anchorBox = 0; + totalWidth = font.width(TextRun(&horizontalEllipsis, 1)); + } + // See if this width can be accommodated on the last visible line RenderBlock* destBlock = toRenderBlock(lastVisibleLine->renderer()); RenderBlock* srcBlock = toRenderBlock(lastLine->renderer()); @@ -805,7 +758,7 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) continue; // Let the truncation code kick in. - lastVisibleLine->placeEllipsis(ellipsisAndSpaceStr, ltr, blockLeftEdge, blockRightEdge, totalWidth, anchorBox); + lastVisibleLine->placeEllipsis(anchorBox ? ellipsisAndSpaceStr : ellipsisStr, ltr, blockLeftEdge, blockRightEdge, totalWidth, anchorBox); destBlock->setHasMarkupTruncation(true); } } @@ -820,7 +773,6 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) do { setHeight(borderTop() + paddingTop()); int minHeight = height() + toAdd; - m_overflowHeight = height(); child = iterator.first(); while (child) { @@ -828,8 +780,7 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))) child->setChildNeedsLayout(true, false); - if (child->isPositioned()) - { + if (child->isPositioned()) { child->containingBlock()->insertPositionedObject(child); if (child->style()->hasStaticX()) { if (style()->direction() == LTR) @@ -837,8 +788,13 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) else child->layer()->setStaticX(borderRight()+paddingRight()); } - if (child->style()->hasStaticY()) - child->layer()->setStaticY(height()); + if (child->style()->hasStaticY()) { + RenderLayer* childLayer = child->layer(); + if (childLayer->staticY() != height()) { + child->layer()->setStaticY(height()); + child->setChildNeedsLayout(true, false); + } + } child = iterator.next(); continue; } @@ -876,14 +832,7 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) // Place the child. placeChild(child, childX, height()); setHeight(height() + child->height() + child->marginBottom()); - - if (child->isRenderBlock()) - 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); - m_overflowLeft = min(child->x() + child->overflowLeft(false), m_overflowLeft); - child = iterator.next(); } @@ -899,10 +848,6 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) if (height() < minHeight) setHeight(minHeight); - // Always make sure our overflowheight is at least our height. - if (m_overflowHeight < height()) - m_overflowHeight = height(); - // Now we have to calc our height, so we know how much space we have remaining. oldHeight = height(); calcHeight(); @@ -1060,22 +1005,6 @@ void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) } } } - - child = iterator.first(); - while (child && child->isPositioned()) { - child = iterator.next(); - } - - if (child) { - m_overflowTop = min(child->y() + child->overflowTop(false), m_overflowTop); - - RenderBox* lastChild = child; - while ((child = iterator.next())) { - if (!child->isPositioned()) - lastChild = child; - } - m_overflowHeight = max(lastChild->y() + lastChild->overflowHeight(false), m_overflowHeight); - } // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of // a height change, we revert our height back to the intrinsic height before returning. @@ -1161,6 +1090,8 @@ const char *RenderFlexibleBox::renderName() const return "RenderFlexibleBox (floating)"; if (isPositioned()) return "RenderFlexibleBox (positioned)"; + if (isAnonymous()) + return "RenderFlexibleBox (generated)"; if (isRelPositioned()) return "RenderFlexibleBox (relative positioned)"; return "RenderFlexibleBox"; diff --git a/WebCore/rendering/RenderForeignObject.cpp b/WebCore/rendering/RenderForeignObject.cpp index b15d55c..5bb4439 100644 --- a/WebCore/rendering/RenderForeignObject.cpp +++ b/WebCore/rendering/RenderForeignObject.cpp @@ -38,10 +38,10 @@ RenderForeignObject::RenderForeignObject(SVGForeignObjectElement* node) { } -TransformationMatrix RenderForeignObject::translationForAttributes() const +FloatPoint RenderForeignObject::translationForAttributes() const { SVGForeignObjectElement* foreign = static_cast<SVGForeignObjectElement*>(node()); - return TransformationMatrix().translate(foreign->x().value(foreign), foreign->y().value(foreign)); + return FloatPoint(foreign->x().value(foreign), foreign->y().value(foreign)); } void RenderForeignObject::paint(PaintInfo& paintInfo, int, int) @@ -84,12 +84,15 @@ FloatRect RenderForeignObject::repaintRectInLocalCoordinates() const void RenderForeignObject::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) { rect = localToParentTransform().mapRect(rect); + style()->svgStyle()->inflateForShadow(rect); RenderBlock::computeRectForRepaint(repaintContainer, rect, fixed); } -TransformationMatrix RenderForeignObject::localToParentTransform() const +const AffineTransform& RenderForeignObject::localToParentTransform() const { - return localTransform() * translationForAttributes(); + FloatPoint attributeTranslation(translationForAttributes()); + m_localToParentTransform = localTransform().translateRight(attributeTranslation.x(), attributeTranslation.y()); + return m_localToParentTransform; } void RenderForeignObject::layout() @@ -118,6 +121,11 @@ bool RenderForeignObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int return false; } +void RenderForeignObject::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const +{ + SVGRenderBase::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState); +} + } // namespace WebCore #endif // ENABLE(SVG) && ENABLE(SVG_FOREIGN_OBJECT) diff --git a/WebCore/rendering/RenderForeignObject.h b/WebCore/rendering/RenderForeignObject.h index 8fdb816..f32069c 100644 --- a/WebCore/rendering/RenderForeignObject.h +++ b/WebCore/rendering/RenderForeignObject.h @@ -23,7 +23,8 @@ #define RenderForeignObject_h #if ENABLE(SVG) && ENABLE(SVG_FOREIGN_OBJECT) -#include "TransformationMatrix.h" +#include "AffineTransform.h" +#include "FloatPoint.h" #include "RenderSVGBlock.h" namespace WebCore { @@ -38,24 +39,29 @@ public: virtual void paint(PaintInfo&, int parentX, int parentY); - virtual TransformationMatrix localToParentTransform() const; + virtual const AffineTransform& localToParentTransform() const; virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); virtual bool requiresLayer() const { return false; } virtual void layout(); virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const { return borderBoxRect(); } virtual FloatRect repaintRectInLocalCoordinates() const; virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + virtual bool isSVGForeignObject() const { return true; } + + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const; private: - TransformationMatrix translationForAttributes() const; + FloatPoint translationForAttributes() const; - virtual TransformationMatrix localTransform() const { return m_localTransform; } + virtual AffineTransform localTransform() const { return m_localTransform; } - TransformationMatrix m_localTransform; + AffineTransform m_localTransform; + mutable AffineTransform m_localToParentTransform; }; } // namespace WebCore diff --git a/WebCore/rendering/RenderFrame.cpp b/WebCore/rendering/RenderFrame.cpp index 54a73c7..ffe87c2 100644 --- a/WebCore/rendering/RenderFrame.cpp +++ b/WebCore/rendering/RenderFrame.cpp @@ -26,6 +26,7 @@ #include "FrameView.h" #include "HTMLFrameElement.h" +#include "RenderView.h" #ifdef FLATTEN_FRAMESET #include "Frame.h" @@ -70,7 +71,7 @@ void RenderFrame::layout() if (widget() && widget()->isFrameView()) { FrameView* view = static_cast<FrameView*>(widget()); RenderView* root = NULL; - if (view->frame() && view->frame()->document() && + if (view->frame() && view->frame()->document() && view->frame()->document()->renderer() && view->frame()->document()->renderer()->isRenderView()) root = static_cast<RenderView*>(view->frame()->document()->renderer()); @@ -92,5 +93,56 @@ void RenderFrame::layout() setNeedsLayout(false); } #endif +void RenderFrame::layoutWithFlattening(bool fixedWidth, bool fixedHeight) +{ + // NOTE: The width and height have been set at this point by + // RenderFrameSet::positionFramesWithFlattening() + + FrameView* childFrameView = static_cast<FrameView*>(widget()); + RenderView* childRoot = childFrameView ? static_cast<RenderView*>(childFrameView->frame()->contentRenderer()) : 0; + HTMLFrameElement* element = static_cast<HTMLFrameElement*>(node()); + + // Do not expand framesets which has zero width or height + if (!width() || !height() || !childRoot) { + updateWidgetPosition(); + if (childFrameView) + childFrameView->layout(); + setNeedsLayout(false); + return; + } + + // need to update to calculate min/max correctly + updateWidgetPosition(); + if (childRoot->prefWidthsDirty()) + childRoot->calcPrefWidths(); + + // if scrollbars are off, and the width or height are fixed + // we obey them and do not expand. With frame flattening + // no subframe much ever become scrollable. + + bool isScrollable = element->scrollingMode() != ScrollbarAlwaysOff; + + // make sure minimum preferred width is enforced + if (isScrollable || !fixedWidth || childRoot->isFrameSet()) + setWidth(max(width(), childRoot->minPrefWidth())); + + // update again to pass the width to the child frame + updateWidgetPosition(); + childFrameView->layout(); + + // expand the frame by setting frame height = content height + if (isScrollable || !fixedHeight || childRoot->isFrameSet()) + setHeight(max(height(), childFrameView->contentsHeight())); + if (isScrollable || !fixedWidth || childRoot->isFrameSet()) + setWidth(max(width(), childFrameView->contentsWidth())); + + updateWidgetPosition(); + + ASSERT(!childFrameView->layoutPending()); + ASSERT(!childRoot->needsLayout()); + ASSERT(!childRoot->firstChild() || !childRoot->firstChild()->firstChild() || !childRoot->firstChild()->firstChild()->needsLayout()); + + setNeedsLayout(false); +} } // namespace WebCore diff --git a/WebCore/rendering/RenderFrame.h b/WebCore/rendering/RenderFrame.h index 5b3b533..a66aa14 100644 --- a/WebCore/rendering/RenderFrame.h +++ b/WebCore/rendering/RenderFrame.h @@ -35,6 +35,7 @@ public: RenderFrame(HTMLFrameElement*); FrameEdgeInfo edgeInfo() const; + void layoutWithFlattening(bool fixedWidth, bool fixedHeight); private: virtual const char* renderName() const { return "RenderFrame"; } diff --git a/WebCore/rendering/RenderFrameSet.cpp b/WebCore/rendering/RenderFrameSet.cpp index 0838798..cf78b2b 100644 --- a/WebCore/rendering/RenderFrameSet.cpp +++ b/WebCore/rendering/RenderFrameSet.cpp @@ -1,6 +1,4 @@ /** - * This file is part of the KDE project. - * * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 2000 Simon Hausmann <hausmann@kde.org> * (C) 2000 Stefan Schimanski (1Stein@gmx.de) @@ -38,6 +36,7 @@ #include "MouseEvent.h" #include "RenderFrame.h" #include "RenderView.h" +#include "Settings.h" namespace WebCore { @@ -90,13 +89,14 @@ void RenderFrameSet::paintColumnBorder(const PaintInfo& paintInfo, const IntRect // Fill first. GraphicsContext* context = paintInfo.context; - context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->borderLeftColor() : borderFillColor()); + ColorSpace colorSpace = style()->colorSpace(); + context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->borderLeftColor() : borderFillColor(), colorSpace); // Now stroke the edges but only if we have enough room to paint both edges with a little // bit of the fill color showing through. if (borderRect.width() >= 3) { - context->fillRect(IntRect(borderRect.topLeft(), IntSize(1, height())), borderStartEdgeColor()); - context->fillRect(IntRect(borderRect.topRight(), IntSize(1, height())), borderEndEdgeColor()); + context->fillRect(IntRect(borderRect.topLeft(), IntSize(1, height())), borderStartEdgeColor(), colorSpace); + context->fillRect(IntRect(borderRect.topRight(), IntSize(1, height())), borderEndEdgeColor(), colorSpace); } } @@ -109,13 +109,14 @@ void RenderFrameSet::paintRowBorder(const PaintInfo& paintInfo, const IntRect& b // Fill first. GraphicsContext* context = paintInfo.context; - context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->borderLeftColor() : borderFillColor()); + ColorSpace colorSpace = style()->colorSpace(); + context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->borderLeftColor() : borderFillColor(), colorSpace); // Now stroke the edges but only if we have enough room to paint both edges with a little // bit of the fill color showing through. if (borderRect.height() >= 3) { - context->fillRect(IntRect(borderRect.topLeft(), IntSize(width(), 1)), borderStartEdgeColor()); - context->fillRect(IntRect(borderRect.bottomLeft(), IntSize(width(), 1)), borderEndEdgeColor()); + context->fillRect(IntRect(borderRect.topLeft(), IntSize(width(), 1)), borderStartEdgeColor(), colorSpace); + context->fillRect(IntRect(borderRect.bottomLeft(), IntSize(width(), 1)), borderEndEdgeColor(), colorSpace); } } @@ -496,7 +497,10 @@ void RenderFrameSet::layout() } #endif - positionFrames(); + if (flattenFrameSet()) + positionFramesWithFlattening(); + else + positionFrames(); RenderBox::layout(); @@ -520,7 +524,7 @@ void RenderFrameSet::positionFrames() int rows = frameSet()->totalRows(); int cols = frameSet()->totalCols(); - + int yPos = 0; int borderThickness = frameSet()->border(); #ifdef FLATTEN_FRAMESET @@ -602,7 +606,7 @@ void RenderFrameSet::positionFrames() child = (RenderBox*)firstChild(); yPos = 0; #endif // FLATTEN_FRAMESET - + for (int r = 0; r < rows; r++) { int xPos = 0; int height = m_rows.m_sizes[r]; @@ -635,6 +639,119 @@ void RenderFrameSet::positionFrames() } } +void RenderFrameSet::positionFramesWithFlattening() +{ + RenderBox* child = firstChildBox(); + if (!child) + return; + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + + int borderThickness = frameSet()->border(); + bool repaintNeeded = false; + + // calculate frameset height based on actual content height to eliminate scrolling + bool out = false; + for (int r = 0; r < rows && !out; r++) { + int extra = 0; + int height = m_rows.m_sizes[r]; + + for (int c = 0; c < cols; c++) { + IntRect oldFrameRect = child->frameRect(); + + int width = m_cols.m_sizes[c]; + + bool fixedWidth = frameSet()->colLengths() && frameSet()->colLengths()[c].isFixed(); + bool fixedHeight = frameSet()->rowLengths() && frameSet()->rowLengths()[r].isFixed(); + + // has to be resized and itself resize its contents + if (!fixedWidth) + child->setWidth(width ? width + extra / (cols - c) : 0); + else + child->setWidth(width); + child->setHeight(height); + + child->setNeedsLayout(true); + + if (child->isFrameSet()) + toRenderFrameSet(child)->layout(); + else + toRenderFrame(child)->layoutWithFlattening(fixedWidth, fixedHeight); + + if (child->height() > m_rows.m_sizes[r]) + m_rows.m_sizes[r] = child->height(); + if (child->width() > m_cols.m_sizes[c]) + m_cols.m_sizes[c] = child->width(); + + if (child->frameRect() != oldFrameRect) + repaintNeeded = true; + + // difference between calculated frame width and the width it actually decides to have + extra += width - m_cols.m_sizes[c]; + + child = child->nextSiblingBox(); + if (!child) { + out = true; + break; + } + } + } + + int xPos = 0; + int yPos = 0; + out = false; + child = firstChildBox(); + for (int r = 0; r < rows && !out; r++) { + xPos = 0; + for (int c = 0; c < cols; c++) { + // ensure the rows and columns are filled + IntRect oldRect = child->frameRect(); + + child->setLocation(xPos, yPos); + child->setHeight(m_rows.m_sizes[r]); + child->setWidth(m_cols.m_sizes[c]); + + if (child->frameRect() != oldRect) { + repaintNeeded = true; + + // update to final size + child->setNeedsLayout(true); + if (child->isFrameSet()) + toRenderFrameSet(child)->layout(); + else + toRenderFrame(child)->layoutWithFlattening(true, true); + } + + xPos += m_cols.m_sizes[c] + borderThickness; + child = child->nextSiblingBox(); + if (!child) { + out = true; + break; + } + } + yPos += m_rows.m_sizes[r] + borderThickness; + } + + setWidth(xPos - borderThickness); + setHeight(yPos - borderThickness); + + if (repaintNeeded) + repaint(); + + // all the remaining frames are hidden to avoid ugly spurious unflowed frames + for (; child; child = child->nextSiblingBox()) { + child->setWidth(0); + child->setHeight(0); + child->setNeedsLayout(false); + } +} + +bool RenderFrameSet::flattenFrameSet() const +{ + return document()->frame() && document()->frame()->settings()->frameSetFlatteningEnabled(); +} + void RenderFrameSet::startResizing(GridAxis& axis, int position) { int split = hitTestSplit(axis, position); @@ -663,6 +780,9 @@ void RenderFrameSet::continueResizing(GridAxis& axis, int position) bool RenderFrameSet::userResize(MouseEvent* evt) { + if (flattenFrameSet()) + return false; + if (!m_isResizing) { if (needsLayout()) return false; diff --git a/WebCore/rendering/RenderFrameSet.h b/WebCore/rendering/RenderFrameSet.h index aa3ac64..a4c8963 100644 --- a/WebCore/rendering/RenderFrameSet.h +++ b/WebCore/rendering/RenderFrameSet.h @@ -73,7 +73,8 @@ public: #ifdef FLATTEN_FRAMESET void setGridNeedsLayout() { m_gridCalculated = false; } #endif - + bool flattenFrameSet() const; + private: static const int noSplit = -1; @@ -108,6 +109,7 @@ private: void computeEdgeInfo(); void fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c); void positionFrames(); + void positionFramesWithFlattening(); int splitPosition(const GridAxis&, int split) const; int hitTestSplit(const GridAxis&, int position) const; diff --git a/WebCore/rendering/RenderHTMLCanvas.cpp b/WebCore/rendering/RenderHTMLCanvas.cpp index 1fc07f0..8c17a0e 100644 --- a/WebCore/rendering/RenderHTMLCanvas.cpp +++ b/WebCore/rendering/RenderHTMLCanvas.cpp @@ -43,6 +43,19 @@ RenderHTMLCanvas::RenderHTMLCanvas(HTMLCanvasElement* element) view()->frameView()->setIsVisuallyNonEmpty(); } +bool RenderHTMLCanvas::requiresLayer() const +{ + if (RenderReplaced::requiresLayer()) + return true; + +#if ENABLE(3D_CANVAS) + HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(node()); + return canvas && canvas->is3D(); +#else + return false; +#endif +} + void RenderHTMLCanvas::paintReplaced(PaintInfo& paintInfo, int tx, int ty) { IntRect rect = contentBoxRect(); @@ -55,10 +68,13 @@ void RenderHTMLCanvas::canvasSizeChanged() IntSize canvasSize = static_cast<HTMLCanvasElement*>(node())->size(); IntSize zoomedSize(canvasSize.width() * style()->effectiveZoom(), canvasSize.height() * style()->effectiveZoom()); - if (canvasSize == intrinsicSize()) + if (zoomedSize == intrinsicSize()) return; - setIntrinsicSize(canvasSize); + setIntrinsicSize(zoomedSize); + + if (!parent()) + return; if (!prefWidthsDirty()) setPrefWidthsDirty(true); diff --git a/WebCore/rendering/RenderHTMLCanvas.h b/WebCore/rendering/RenderHTMLCanvas.h index e82cf9a..473dad5 100644 --- a/WebCore/rendering/RenderHTMLCanvas.h +++ b/WebCore/rendering/RenderHTMLCanvas.h @@ -36,6 +36,9 @@ class RenderHTMLCanvas : public RenderReplaced { public: RenderHTMLCanvas(HTMLCanvasElement*); + virtual bool isCanvas() const { return true; } + virtual bool requiresLayer() const; + void canvasSizeChanged(); private: diff --git a/WebCore/rendering/RenderImage.cpp b/WebCore/rendering/RenderImage.cpp index 91200bc..881d0b4 100644 --- a/WebCore/rendering/RenderImage.cpp +++ b/WebCore/rendering/RenderImage.cpp @@ -4,7 +4,7 @@ * (C) 2000 Dirk Mueller (mueller@kde.org) * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,14 +26,19 @@ #include "config.h" #include "RenderImage.h" +#include "Frame.h" #include "GraphicsContext.h" +#include "HTMLAreaElement.h" +#include "HTMLCollection.h" #include "HTMLImageElement.h" #include "HTMLInputElement.h" #include "HTMLMapElement.h" #include "HTMLNames.h" #include "HitTestResult.h" #include "Page.h" +#include "RenderTheme.h" #include "RenderView.h" +#include "SelectionController.h" #include <wtf/CurrentTime.h> #include <wtf/UnusedParam.h> @@ -53,7 +58,7 @@ namespace WebCore { static const double cInterpolationCutoff = 800. * 800.; static const double cLowQualityTimeThreshold = 0.050; // 50 ms -class RenderImageScaleData { +class RenderImageScaleData : public Noncopyable { public: RenderImageScaleData(RenderImage* image, const IntSize& size, double time, bool lowQualityScale) : m_size(size) @@ -231,7 +236,7 @@ bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */) imageHeight = paddingHeight; } - if (newImage) { + if (newImage && newImage->image()) { // imageSize() returns 0 for the error image. We need the true size of the // error image, so we have to get it by grabbing image() directly. imageWidth += newImage->image()->width() * style()->effectiveZoom(); @@ -371,8 +376,8 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty) context->save(); #endif context->setStrokeStyle(SolidStroke); - context->setStrokeColor(Color::lightGray); - context->setFillColor(Color::transparent); + context->setStrokeColor(Color::lightGray, style()->colorSpace()); + context->setFillColor(Color::transparent, style()->colorSpace()); context->drawRect(IntRect(tx + leftBorder + leftPad, ty + topBorder + topPad, cWidth, cHeight)); #ifdef ANDROID_FIX // see http://b/issue?id=2052757 context->restore(); @@ -396,13 +401,13 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty) centerY = 0; imageX = leftBorder + leftPad + centerX + 1; imageY = topBorder + topPad + centerY + 1; - context->drawImage(image(), IntPoint(tx + imageX, ty + imageY)); + context->drawImage(image(), style()->colorSpace(), IntPoint(tx + imageX, ty + imageY)); errorPictureDrawn = true; } if (!m_altText.isEmpty()) { String text = document()->displayStringModifiedByEncoding(m_altText); - context->setFillColor(style()->color()); + context->setFillColor(style()->color(), style()->colorSpace()); int ax = tx + leftBorder + leftPad; int ay = ty + topBorder + topPad; const Font& font = style()->font(); @@ -430,23 +435,80 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty) #endif IntSize contentSize(cWidth, cHeight); - bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, contentSize); IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), contentSize); - HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(node()) : 0; - CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; - context->drawImage(image(cWidth, cHeight), rect, compositeOperator, useLowQualityScaling); + paintIntoRect(context, rect); } } +void RenderImage::paint(PaintInfo& paintInfo, int tx, int ty) +{ + RenderReplaced::paint(paintInfo, tx, ty); + + if (paintInfo.phase == PaintPhaseOutline) + paintFocusRings(paintInfo, style()); +} + +void RenderImage::paintFocusRings(PaintInfo& paintInfo, const RenderStyle* style) +{ + // Don't draw focus rings if printing. + if (document()->printing() || !document()->frame()->selection()->isFocusedAndActive()) + return; + + if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints()) + return; + + HTMLMapElement* mapElement = imageMap(); + if (!mapElement) + return; + + Document* document = mapElement->document(); + if (!document) + return; + + Node* focusedNode = document->focusedNode(); + if (!focusedNode) + return; + + RefPtr<HTMLCollection> areas = mapElement->areas(); + unsigned numAreas = areas->length(); + + // FIXME: Clip the paths to the image bounding box. + for (unsigned k = 0; k < numAreas; ++k) { + HTMLAreaElement* areaElement = static_cast<HTMLAreaElement*>(areas->item(k)); + if (focusedNode != areaElement) + continue; + + Vector<Path> focusRingPaths; + focusRingPaths.append(areaElement->getPath(this)); + paintInfo.context->drawFocusRing(focusRingPaths, style->outlineWidth(), style->outlineOffset(), style->outlineColor()); + break; + } +} + +void RenderImage::paintIntoRect(GraphicsContext* context, const IntRect& rect) +{ + if (!hasImage() || errorOccurred() || rect.width() <= 0 || rect.height() <= 0) + return; + + Image* img = image(rect.width(), rect.height()); + if (!img || img->isNull()) + return; + + HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(node()) : 0; + CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; + bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, rect.size()); + context->drawImage(image(rect.width(), rect.height()), style()->colorSpace(), rect, compositeOperator, useLowQualityScaling); +} + int RenderImage::minimumReplacedHeight() const { return errorOccurred() ? intrinsicSize().height() : 0; } -HTMLMapElement* RenderImage::imageMap() +HTMLMapElement* RenderImage::imageMap() const { HTMLImageElement* i = node() && node()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(node()) : 0; - return i ? i->document()->getImageMap(i->useMap()) : 0; + return i ? i->document()->getImageMap(i->getAttribute(usemapAttr)) : 0; } bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) @@ -550,7 +612,7 @@ int RenderImage::calcReplacedWidth(bool includeMaxWidth) const } return width; #else - return max(minW, min(width, maxW)); + return max(minW, min(width, maxW)); #endif } @@ -572,19 +634,19 @@ int RenderImage::calcReplacedHeight() const #ifdef ANDROID_LAYOUT height = max(minH, min(height, maxH)); // in SSR mode, we will fit the image to its container width - if (height && document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { + if (height && document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { int width; if (isWidthSpecified()) width = calcReplacedWidthUsing(style()->width()); else width = calcAspectRatioWidth(); int minW = calcReplacedWidthUsing(style()->minWidth()); - int maxW = style()->maxWidth().value() == undefinedLength ? width : - calcReplacedWidthUsing(style()->maxWidth()); + int maxW = style()->maxWidth().value() == undefinedLength ? width : + calcReplacedWidthUsing(style()->maxWidth()); width = max(minW, min(width, maxW)); int cw = containingBlockWidthForContent(); - if (cw && width && width>cw) + if (cw && width && width > cw) height = cw * height / width; // preserve aspect ratio } return height; diff --git a/WebCore/rendering/RenderImage.h b/WebCore/rendering/RenderImage.h index 2224412..b89a652 100644 --- a/WebCore/rendering/RenderImage.h +++ b/WebCore/rendering/RenderImage.h @@ -3,7 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2005, 2006, 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -43,7 +43,7 @@ public: void setCachedImage(CachedImage*); CachedImage* cachedImage() const { return m_cachedImage.get(); } - HTMLMapElement* imageMap(); + HTMLMapElement* imageMap() const; void resetAnimation(); @@ -57,6 +57,15 @@ protected: virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + virtual void paintIntoRect(GraphicsContext*, const IntRect&); + void paintFocusRings(PaintInfo&, const RenderStyle*); + virtual void paint(PaintInfo&, int tx, int ty); + + bool isWidthSpecified() const; + bool isHeightSpecified() const; + + virtual void intrinsicSizeChanged() { imageChanged(imagePtr()); } + private: virtual const char* renderName() const { return "RenderImage"; } @@ -82,14 +91,9 @@ private: virtual IntSize imageSize(float multiplier) const { return m_cachedImage ? m_cachedImage->imageSize(multiplier) : IntSize(); } virtual WrappedImagePtr imagePtr() const { return m_cachedImage.get(); } - virtual void intrinsicSizeChanged() { imageChanged(imagePtr()); } - int calcAspectRatioWidth() const; int calcAspectRatioHeight() const; - bool isWidthSpecified() const; - bool isHeightSpecified() const; - protected: // The image we are rendering. CachedResourceHandle<CachedImage> m_cachedImage; diff --git a/WebCore/rendering/RenderInline.cpp b/WebCore/rendering/RenderInline.cpp index 223255e..9571751 100644 --- a/WebCore/rendering/RenderInline.cpp +++ b/WebCore/rendering/RenderInline.cpp @@ -23,6 +23,7 @@ #include "config.h" #include "RenderInline.h" +#include "Chrome.h" #include "FloatQuad.h" #include "GraphicsContext.h" #include "HitTestResult.h" @@ -30,6 +31,7 @@ #include "RenderArena.h" #include "RenderBlock.h" #include "RenderView.h" +#include "TransformState.h" #include "VisiblePosition.h" #if ENABLE(DASHBOARD_SUPPORT) @@ -55,14 +57,14 @@ void RenderInline::destroy() // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. children()->destroyLeftoverChildren(); - // Destroy our continuation before anything other than anonymous children. - // The reason we don't destroy it before anonymous children is that they may - // have continuations of their own that are anonymous children of our continuation. - if (m_continuation) { - m_continuation->destroy(); - m_continuation = 0; - } - + // Destroy our continuation before anything other than anonymous children. + // The reason we don't destroy it before anonymous children is that they may + // have continuations of their own that are anonymous children of our continuation. + if (m_continuation) { + m_continuation->destroy(); + m_continuation = 0; + } + if (!documentBeingDestroyed()) { if (firstLineBox()) { // We can't wait for RenderBoxModelObject::destroy to clear the selection, @@ -134,18 +136,6 @@ void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldSt } } -static inline bool isAfterContent(RenderObject* child) -{ - if (!child) - return false; - 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()) - return false; - return true; -} - void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild) { if (continuation()) @@ -559,6 +549,23 @@ IntRect RenderInline::linesBoundingBox() const return result; } +IntRect RenderInline::linesVisibleOverflowBoundingBox() const +{ + if (!firstLineBox() || !lastLineBox()) + return IntRect(); + + // Return the width of the minimal left side and the maximal right side. + int leftSide = numeric_limits<int>::max(); + int rightSide = numeric_limits<int>::min(); + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) { + leftSide = min(leftSide, curr->leftVisibleOverflow()); + rightSide = max(rightSide, curr->rightVisibleOverflow()); + } + + return IntRect(leftSide, firstLineBox()->topVisibleOverflow(), rightSide - leftSide, + lastLineBox()->bottomVisibleOverflow() - firstLineBox()->topVisibleOverflow()); +} + IntRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { // Only run-ins are allowed in here during layout. @@ -568,7 +575,7 @@ IntRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repain return IntRect(); // Find our leftmost position. - IntRect boundingBox(linesBoundingBox()); + IntRect boundingBox(linesVisibleOverflowBoundingBox()); int left = boundingBox.x(); int top = boundingBox.y(); @@ -650,7 +657,8 @@ void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, if (repaintContainer == this) return; - RenderObject* o = container(); + bool containerSkipped; + RenderObject* o = container(repaintContainer, &containerSkipped); if (!o) return; @@ -691,10 +699,103 @@ void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, return; } else rect.setLocation(topLeft); + + if (containerSkipped) { + // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates. + IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); + rect.move(-containerOffset); + return; + } o->computeRectForRepaint(repaintContainer, rect, fixed); } +IntSize RenderInline::offsetFromContainer(RenderObject* container) const +{ + ASSERT(container == this->container()); + + IntSize offset; + if (isRelPositioned()) + offset += relativePositionOffset(); + + if (!isInline() || isReplaced()) { + RenderBlock* cb; + if (container->isBlockFlow() && (cb = toRenderBlock(container))->hasColumns()) { + IntRect rect(0, 0, 1, 1); + cb->adjustRectForColumns(rect); + } + } + + if (container->hasOverflowClip()) + offset -= toRenderBox(container)->layer()->scrolledContentOffset(); + + return offset; +} + +void RenderInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const +{ + if (repaintContainer == this) + return; + + if (RenderView *v = view()) { + if (v->layoutStateEnabled() && !repaintContainer) { + LayoutState* layoutState = v->layoutState(); + IntSize offset = layoutState->m_offset; + if (style()->position() == RelativePosition && layer()) + offset += layer()->relativePositionOffset(); + transformState.move(offset); + return; + } + } + + bool containerSkipped; + RenderObject* o = container(repaintContainer, &containerSkipped); + if (!o) + return; + + IntSize containerOffset = offsetFromContainer(o); + + bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); + if (useTransforms && shouldUseTransformFromContainer(o)) { + TransformationMatrix t; + getTransformFromContainer(o, containerOffset, t); + transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + } else + transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + + if (containerSkipped) { + // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe + // to just subtract the delta between the repaintContainer and o. + IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); + transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + return; + } + + o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); +} + +void RenderInline::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const +{ + // We don't expect this function to be called during layout. + ASSERT(!view() || !view()->layoutStateEnabled()); + + RenderObject* o = container(); + if (!o) + return; + + o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); + + IntSize containerOffset = offsetFromContainer(o); + + bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); + if (useTransforms && shouldUseTransformFromContainer(o)) { + TransformationMatrix t; + getTransformFromContainer(o, containerOffset, t); + transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + } else + transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); +} + void RenderInline::updateDragState(bool dragOn) { RenderBoxModelObject::updateDragState(dragOn); @@ -831,10 +932,16 @@ void RenderInline::imageChanged(WrappedImagePtr, const IntRect*) repaint(); } -void RenderInline::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +void RenderInline::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) { - for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) - graphicsContext->addFocusRingRect(IntRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())); + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + RootInlineBox* root = curr->root(); + int top = max(root->lineTop(), curr->y()); + int bottom = min(root->lineBottom(), curr->y() + curr->height()); + IntRect rect(tx + curr->x(), ty + top, curr->width(), bottom - top); + if (!rect.isEmpty()) + rects.append(rect); + } for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { if (!curr->isText() && !curr->isListMarker()) { @@ -844,17 +951,17 @@ void RenderInline::addFocusRingRects(GraphicsContext* graphicsContext, int tx, i pos = curr->localToAbsolute(); else if (curr->isBox()) pos.move(toRenderBox(curr)->x(), toRenderBox(curr)->y()); - curr->addFocusRingRects(graphicsContext, pos.x(), pos.y()); + curr->addFocusRingRects(rects, pos.x(), pos.y()); } } if (continuation()) { if (continuation()->isInline()) - continuation()->addFocusRingRects(graphicsContext, + continuation()->addFocusRingRects(rects, tx - containingBlock()->x() + continuation()->containingBlock()->x(), ty - containingBlock()->y() + continuation()->containingBlock()->y()); else - continuation()->addFocusRingRects(graphicsContext, + continuation()->addFocusRingRects(rects, tx - containingBlock()->x() + toRenderBox(continuation())->x(), ty - containingBlock()->y() + toRenderBox(continuation())->y()); } @@ -871,13 +978,12 @@ void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty if (!oc.isValid()) oc = style()->color(); - graphicsContext->initFocusRing(ow, style()->outlineOffset()); - addFocusRingRects(graphicsContext, tx, ty); + Vector<IntRect> focusRingRects; + addFocusRingRects(focusRingRects, tx, ty); if (style()->outlineStyleIsAuto()) - graphicsContext->drawFocusRing(oc); + graphicsContext->drawFocusRing(focusRingRects, ow, style()->outlineOffset(), oc); else - addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); - graphicsContext->clearFocusRing(); + addPDFURLRect(graphicsContext, unionRect(focusRingRects)); } if (style()->outlineStyleIsAuto() || style()->outlineStyle() == BNONE) @@ -886,9 +992,12 @@ void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty 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())); - + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + RootInlineBox* root = curr->root(); + int top = max(root->lineTop(), curr->y()); + int bottom = min(root->lineBottom(), curr->y() + curr->height()); + rects.append(IntRect(curr->x(), top, curr->width(), bottom - top)); + } rects.append(IntRect()); for (unsigned i = 1; i < rects.size() - 1; i++) diff --git a/WebCore/rendering/RenderInline.h b/WebCore/rendering/RenderInline.h index 14d76ca..d35aa85 100644 --- a/WebCore/rendering/RenderInline.h +++ b/WebCore/rendering/RenderInline.h @@ -44,7 +44,10 @@ public: virtual void absoluteRects(Vector<IntRect>&, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); + virtual IntSize offsetFromContainer(RenderObject*) const; + IntRect linesBoundingBox() const; + IntRect linesVisibleOverflowBoundingBox() const; InlineFlowBox* createAndAppendInlineFlowBox(); @@ -62,7 +65,7 @@ public: IntSize relativePositionedInlineOffset(const RenderBox* child) const; - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); void paintOutline(GraphicsContext*, int tx, int ty); int verticalPositionFromCache(bool firstLine) const; @@ -106,6 +109,9 @@ private: virtual IntRect rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth); virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed); + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState&) const; + virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const; + virtual VisiblePosition positionForPoint(const IntPoint&); virtual IntRect borderBoundingBox() const diff --git a/WebCore/rendering/RenderLayer.cpp b/WebCore/rendering/RenderLayer.cpp index ab78f40..6dbb413 100644 --- a/WebCore/rendering/RenderLayer.cpp +++ b/WebCore/rendering/RenderLayer.cpp @@ -73,11 +73,13 @@ #include "RenderScrollbar.h" #include "RenderScrollbarPart.h" #include "RenderTheme.h" +#include "RenderTreeAsText.h" #include "RenderView.h" #include "ScaleTransformOperation.h" #include "Scrollbar.h" #include "ScrollbarTheme.h" #include "SelectionController.h" +#include "TextStream.h" #include "TransformationMatrix.h" #include "TransformState.h" #include "TranslateTransformOperation.h" @@ -207,12 +209,8 @@ RenderLayer::~RenderLayer() // Make sure we have no lingering clip rects. ASSERT(!m_clipRects); - if (m_reflection) { - if (!m_reflection->documentBeingDestroyed()) - m_reflection->removeLayers(this); - m_reflection->setParent(0); - m_reflection->destroy(); - } + if (m_reflection) + removeReflection(); if (m_scrollCorner) m_scrollCorner->destroy(); @@ -247,14 +245,6 @@ bool RenderLayer::hasAcceleratedCompositing() const #endif } -void RenderLayer::setStaticY(int staticY) -{ - if (m_staticY == staticY) - return; - m_staticY = staticY; - renderer()->setChildNeedsLayout(true, false); -} - void RenderLayer::updateLayerPositions(UpdateLayerPositionsFlags flags) { if (flags & DoFullRepaint) { @@ -315,12 +305,19 @@ void RenderLayer::updateLayerPositions(UpdateLayerPositionsFlags flags) if (m_reflection) m_reflection->layout(); +#if USE(ACCELERATED_COMPOSITING) + // Clear the IsCompositingUpdateRoot flag once we've found the first compositing layer in this update. + bool isUpdateRoot = (flags & IsCompositingUpdateRoot); + if (isComposited()) + flags &= ~IsCompositingUpdateRoot; +#endif + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) child->updateLayerPositions(flags); #if USE(ACCELERATED_COMPOSITING) if ((flags & UpdateCompositingLayers) && isComposited()) - backing()->updateAfterLayout(RenderLayerBacking::CompositingChildren); + backing()->updateAfterLayout(RenderLayerBacking::CompositingChildren, isUpdateRoot); #endif // With all our children positioned, now update our marquee if we need to. @@ -380,6 +377,20 @@ TransformationMatrix RenderLayer::currentTransform() const return *m_transform; } +TransformationMatrix RenderLayer::renderableTransform(PaintBehavior paintBehavior) const +{ + if (!m_transform) + return TransformationMatrix(); + + if (paintBehavior & PaintBehaviorFlattenCompositingLayers) { + TransformationMatrix matrix = *m_transform; + makeMatrixRenderable(matrix, false /* flatten 3d */); + return matrix; + } + + return *m_transform; +} + void RenderLayer::setHasVisibleContent(bool b) { if (m_hasVisibleContent == b && !m_visibleContentStatusDirty) @@ -584,10 +595,10 @@ void RenderLayer::updateLayerPosition() setHeight(box->height()); if (!box->hasOverflowClip()) { - if (box->overflowWidth() > box->width()) - setWidth(box->overflowWidth()); - if (box->overflowHeight() > box->height()) - setHeight(box->overflowHeight()); + if (box->rightLayoutOverflow() > box->width()) + setWidth(box->rightLayoutOverflow()); + if (box->bottomLayoutOverflow() > box->height()) + setHeight(box->bottomLayoutOverflow()); } } } @@ -637,48 +648,91 @@ FloatPoint RenderLayer::perspectiveOrigin() const RenderLayer* RenderLayer::stackingContext() const { RenderLayer* layer = parent(); +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + // When using composited fixed elements, they are turned into a stacking + // context and we thus need to return them. + // We can simplify the while loop by using isStackingContext(); with + // composited fixed elements turned on, this will return true for them, + // and is otherwise equivalent to the replaced statements. + while (layer && !layer->renderer()->isRoot() && !layer->isStackingContext()) +#else while (layer && !layer->renderer()->isRenderView() && !layer->renderer()->isRoot() && layer->renderer()->style()->hasAutoZIndex()) +#endif layer = layer->parent(); return layer; } +static inline bool isPositionedContainer(RenderLayer* layer) +{ + RenderObject* o = layer->renderer(); + return o->isRenderView() || o->isPositioned() || o->isRelPositioned() || layer->hasTransform(); +} + +static inline bool isFixedPositionedContainer(RenderLayer* layer) +{ + RenderObject* o = layer->renderer(); + return o->isRenderView() || layer->hasTransform(); +} + RenderLayer* RenderLayer::enclosingPositionedAncestor() const { RenderLayer* curr = parent(); - for ( ; curr && !curr->renderer()->isRenderView() && !curr->renderer()->isPositioned() && !curr->renderer()->isRelPositioned() && !curr->hasTransform(); - curr = curr->parent()) { } + while (curr && !isPositionedContainer(curr)) + curr = curr->parent(); + return curr; } RenderLayer* RenderLayer::enclosingTransformedAncestor() const { RenderLayer* curr = parent(); - for ( ; curr && !curr->renderer()->isRenderView() && !curr->transform(); curr = curr->parent()) - { } + while (curr && !curr->renderer()->isRenderView() && !curr->transform()) + curr = curr->parent(); + return curr; } +static inline const RenderLayer* compositingContainer(const RenderLayer* layer) +{ + return layer->isNormalFlowOnly() ? layer->parent() : layer->stackingContext(); +} + #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(); + for (const RenderLayer* curr = compositingContainer(this); curr; curr = compositingContainer(curr)) { + if (curr->isComposited()) + return const_cast<RenderLayer*>(curr); } return 0; } #endif +RenderLayer* RenderLayer::clippingRoot() const +{ + const RenderLayer* current = this; + while (current) { + if (current->renderer()->isRenderView()) + return const_cast<RenderLayer*>(current); + + current = compositingContainer(current); + ASSERT(current); + if (current->transform() +#if USE(ACCELERATED_COMPOSITING) + || current->isComposited() +#endif + ) + return const_cast<RenderLayer*>(current); + } + + ASSERT_NOT_REACHED(); + return 0; +} + IntPoint RenderLayer::absoluteToContents(const IntPoint& absolutePoint) const { // We don't use convertToLayerCoords because it doesn't know about transforms @@ -717,13 +771,42 @@ RenderLayer* RenderLayer::transparentPaintingAncestor() return 0; } -static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransform, const RenderLayer* l, const RenderLayer* rootLayer) +static IntRect transparencyClipBox(const RenderLayer* l, const RenderLayer* rootLayer, PaintBehavior paintBehavior); + +static void expandClipRectForDescendantsAndReflection(IntRect& clipRect, const RenderLayer* l, const RenderLayer* rootLayer, PaintBehavior paintBehavior) +{ + // If we have a mask, then the clip is limited to the border box area (and there is + // no need to examine child layers). + if (!l->renderer()->hasMask()) { + // Note: we don't have to walk z-order lists since transparent elements always establish + // a stacking context. This means we can just walk the layer tree directly. + for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) { + if (!l->reflection() || l->reflectionLayer() != curr) + clipRect.unite(transparencyClipBox(curr, rootLayer, paintBehavior)); + } + } + + // If we have a reflection, then we need to account for that when we push the clip. Reflect our entire + // current transparencyClipBox to catch all child layers. + // FIXME: Accelerated compositing will eventually want to do something smart here to avoid incorporating this + // size into the parent layer. + if (l->renderer()->hasReflection()) { + int deltaX = 0; + int deltaY = 0; + l->convertToLayerCoords(rootLayer, deltaX, deltaY); + clipRect.move(-deltaX, -deltaY); + clipRect.unite(l->renderBox()->reflectedRect(clipRect)); + clipRect.move(deltaX, deltaY); + } +} + +static IntRect transparencyClipBox(const RenderLayer* l, const RenderLayer* rootLayer, PaintBehavior paintBehavior) { // FIXME: Although this function completely ignores CSS-imposed clipping, we did already intersect with the // paintDirtyRect, and that should cut down on the amount we have to paint. Still it // would be better to respect clips. - if (rootLayer != l && l->paintsWithTransform()) { + if (rootLayer != l && l->paintsWithTransform(paintBehavior)) { // 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; @@ -733,51 +816,36 @@ static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransfor TransformationMatrix transform; transform.translate(x, y); transform = *l->transform() * transform; - transform = transform * enclosingTransform; - // We now have a transform that will produce a rectangle in our view's space. - IntRect clipRect = transform.mapRect(l->boundingBox(l)); - - // Now shift the root layer to be us and pass down the new enclosing transform. - for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) { - if (!l->reflection() || l->reflectionLayer() != curr) - clipRect.unite(transparencyClipBox(transform, curr, l)); - } - - return clipRect; + IntRect clipRect = l->boundingBox(l); + expandClipRectForDescendantsAndReflection(clipRect, l, l, paintBehavior); + return transform.mapRect(clipRect); } - // Note: we don't have to walk z-order lists since transparent elements always establish - // a stacking context. This means we can just walk the layer tree directly. IntRect clipRect = l->boundingBox(rootLayer); - - // If we have a mask, then the clip is limited to the border box area (and there is - // no need to examine child layers). - if (!l->renderer()->hasMask()) { - for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) { - if (!l->reflection() || l->reflectionLayer() != curr) - clipRect.unite(transparencyClipBox(enclosingTransform, curr, rootLayer)); - } - } - - // Now map the clipRect via the enclosing transform - return enclosingTransform.mapRect(clipRect); + expandClipRectForDescendantsAndReflection(clipRect, l, rootLayer, paintBehavior); + return clipRect; } -void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const RenderLayer* rootLayer) +void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const RenderLayer* rootLayer, PaintBehavior paintBehavior) { - if (p->paintingDisabled() || (paintsWithTransparency() && m_usedTransparency)) + if (p->paintingDisabled() || (paintsWithTransparency(paintBehavior) && m_usedTransparency)) return; RenderLayer* ancestor = transparentPaintingAncestor(); if (ancestor) - ancestor->beginTransparencyLayers(p, rootLayer); + ancestor->beginTransparencyLayers(p, rootLayer, paintBehavior); - if (paintsWithTransparency()) { + if (paintsWithTransparency(paintBehavior)) { m_usedTransparency = true; p->save(); - p->clip(transparencyClipBox(TransformationMatrix(), this, rootLayer)); + IntRect clipRect = transparencyClipBox(this, rootLayer, paintBehavior); + p->clip(clipRect); p->beginTransparencyLayer(renderer()->opacity()); +#ifdef REVEAL_TRANSPARENCY_LAYERS + p->setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f), DeviceColorSpace); + p->fillRect(clipRect); +#endif } } @@ -935,9 +1003,10 @@ RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& xPos, i { if (ancestorLayer == this) return; - - if (renderer()->style()->position() == FixedPosition) { - // Add in the offset of the view. We can obtain this by calling + + EPosition position = renderer()->style()->position(); + if (position == FixedPosition && (!ancestorLayer || ancestorLayer == renderer()->view()->layer())) { + // If the fixed layer's container is the root, just add in the offset of the view. We can obtain this by calling // localToAbsolute() on the RenderView. FloatPoint absPos = renderer()->localToAbsolute(FloatPoint(), true); xPos += absPos.x(); @@ -945,13 +1014,80 @@ RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& xPos, i return; } + if (position == FixedPosition) { + // For a fixed layers, we need to walk up to the root to see if there's a fixed position container + // (e.g. a transformed layer). It's an error to call convertToLayerCoords() across a layer with a transform, + // so we should always find the ancestor at or before we find the fixed position container. + RenderLayer* fixedPositionContainerLayer = 0; + bool foundAncestor = false; + for (RenderLayer* currLayer = parent(); currLayer; currLayer = currLayer->parent()) { + if (currLayer == ancestorLayer) + foundAncestor = true; + + if (isFixedPositionedContainer(currLayer)) { + fixedPositionContainerLayer = currLayer; + ASSERT(foundAncestor); + break; + } + } + + ASSERT(fixedPositionContainerLayer); // We should have hit the RenderView's layer at least. + + if (fixedPositionContainerLayer != ancestorLayer) { + int fixedContainerX = 0; + int fixedContainerY = 0; + convertToLayerCoords(fixedPositionContainerLayer, fixedContainerX, fixedContainerY); + + int ancestorX = 0; + int ancestorY = 0; + ancestorLayer->convertToLayerCoords(fixedPositionContainerLayer, ancestorX, ancestorY); + + xPos += (fixedContainerX - ancestorX); + yPos += (fixedContainerY - ancestorY); + return; + } + } + + RenderLayer* parentLayer; - if (renderer()->style()->position() == AbsolutePosition) - parentLayer = enclosingPositionedAncestor(); - else + if (position == AbsolutePosition || position == FixedPosition) { + // Do what enclosingPositionedAncestor() does, but check for ancestorLayer along the way. + parentLayer = parent(); + bool foundAncestorFirst = false; + while (parentLayer) { + if (isPositionedContainer(parentLayer)) + break; + + if (parentLayer == ancestorLayer) { + foundAncestorFirst = true; + break; + } + + parentLayer = parentLayer->parent(); + } + + if (foundAncestorFirst) { + // Found ancestorLayer before the abs. positioned container, so compute offset of both relative + // to enclosingPositionedAncestor and subtract. + RenderLayer* positionedAncestor = parentLayer->enclosingPositionedAncestor(); + + int thisX = 0; + int thisY = 0; + convertToLayerCoords(positionedAncestor, thisX, thisY); + + int ancestorX = 0; + int ancestorY = 0; + ancestorLayer->convertToLayerCoords(positionedAncestor, ancestorX, ancestorY); + + xPos += (thisX - ancestorX); + yPos += (thisY - ancestorY); + return; + } + } else parentLayer = parent(); - if (!parentLayer) return; + if (!parentLayer) + return; parentLayer->convertToLayerCoords(ancestorLayer, xPos, yPos); @@ -959,12 +1095,22 @@ RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& xPos, i yPos += y(); } +static inline int adjustedScrollDelta(int beginningDelta) { + // This implemention matches Firefox's. + // http://mxr.mozilla.org/firefox/source/toolkit/content/widgets/browser.xml#856. + const int speedReducer = 12; + + int adjustedDelta = beginningDelta / speedReducer; + if (adjustedDelta > 1) + adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(adjustedDelta))) - 1; + else if (adjustedDelta < -1) + adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(-adjustedDelta))) + 1; + + return adjustedDelta; +} + void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint) { - // We want to reduce the speed if we're close from the original point to improve the handleability of the scroll - const int shortDistanceLimit = 100; // We delimit a 200 pixels long square enclosing the original point - const int speedReducer = 2; // Within this square we divide the scrolling speed by 2 - Frame* frame = renderer()->document()->frame(); if (!frame) return; @@ -981,42 +1127,52 @@ void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint) int xDelta = currentMousePosition.x() - sourcePoint.x(); int yDelta = currentMousePosition.y() - sourcePoint.y(); - if (abs(xDelta) < ScrollView::noPanScrollRadius) // at the center we let the space for the icon + if (abs(xDelta) <= ScrollView::noPanScrollRadius) // at the center we let the space for the icon xDelta = 0; - if (abs(yDelta) < ScrollView::noPanScrollRadius) + if (abs(yDelta) <= ScrollView::noPanScrollRadius) yDelta = 0; - // Let's attenuate the speed for the short distances - if (abs(xDelta) < shortDistanceLimit) - xDelta /= speedReducer; - if (abs(yDelta) < shortDistanceLimit) - yDelta /= speedReducer; - - scrollByRecursively(xDelta, yDelta); + scrollByRecursively(adjustedScrollDelta(xDelta), adjustedScrollDelta(yDelta)); } void RenderLayer::scrollByRecursively(int xDelta, int yDelta) { + if (!xDelta && !yDelta) + return; + bool restrictedByLineClamp = false; if (renderer()->parent()) - restrictedByLineClamp = renderer()->parent()->style()->lineClamp() >= 0; + restrictedByLineClamp = !renderer()->parent()->style()->lineClamp().isNone(); if (renderer()->hasOverflowClip() && !restrictedByLineClamp) { int newOffsetX = scrollXOffset() + xDelta; int newOffsetY = scrollYOffset() + yDelta; scrollToOffset(newOffsetX, newOffsetY); - // If this layer can't do the scroll we ask its parent + // If this layer can't do the scroll we ask the next layer up that can scroll to try int leftToScrollX = newOffsetX - scrollXOffset(); int leftToScrollY = newOffsetY - scrollYOffset(); if ((leftToScrollX || leftToScrollY) && renderer()->parent()) { - renderer()->parent()->enclosingLayer()->scrollByRecursively(leftToScrollX, leftToScrollY); + RenderObject* nextRenderer = renderer()->parent(); + while (nextRenderer) { + if (nextRenderer->isBox() && toRenderBox(nextRenderer)->canBeScrolledAndHasScrollableArea()) { + nextRenderer->enclosingLayer()->scrollByRecursively(leftToScrollX, leftToScrollY); + break; + } + nextRenderer = nextRenderer->parent(); + } + Frame* frame = renderer()->document()->frame(); if (frame) frame->eventHandler()->updateAutoscrollRenderer(); } - } else if (renderer()->view()->frameView()) + } else if (renderer()->view()->frameView()) { + // If we are here, we were called on a renderer that can be programmatically scrolled, but doesn't + // have an overflow clip. Which means that it is a document node that can be scrolled. renderer()->view()->frameView()->scrollBy(IntSize(xDelta, yDelta)); + // FIXME: If we didn't scroll the whole way, do we want to try looking at the frames ownerElement? + // https://bugs.webkit.org/show_bug.cgi?id=28237 + } } @@ -1071,8 +1227,10 @@ void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repai #if USE(ACCELERATED_COMPOSITING) if (compositor()->inCompositingMode()) { - if (RenderLayer* compositingAncestor = ancestorCompositingLayer()) - compositingAncestor->backing()->updateAfterLayout(RenderLayerBacking::AllDescendants); + if (RenderLayer* compositingAncestor = ancestorCompositingLayer()) { + bool isUpdateRoot = true; + compositingAncestor->backing()->updateAfterLayout(RenderLayerBacking::AllDescendants, isUpdateRoot); + } } #endif @@ -1093,7 +1251,7 @@ void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repai // The caret rect needs to be invalidated after scrolling Frame* frame = renderer()->document()->frame(); if (frame) - frame->invalidateSelection(); + frame->selection()->setNeedsLayout(); // Just schedule a full repaint of our object. if (repaint) @@ -1128,7 +1286,7 @@ void RenderLayer::scrollRectToVisible(const IntRect &rect, bool scrollToAnchor, bool restrictedByLineClamp = false; if (renderer()->parent()) { parentLayer = renderer()->parent()->enclosingLayer(); - restrictedByLineClamp = renderer()->parent()->style()->lineClamp() >= 0; + restrictedByLineClamp = !renderer()->parent()->style()->lineClamp().isNone(); } if (renderer()->hasOverflowClip() && !restrictedByLineClamp) { @@ -1275,7 +1433,9 @@ void RenderLayer::autoscroll() if (!frameView) return; +#if ENABLE(DRAG_SUPPORT) frame->eventHandler()->updateSelectionForMouseDrag(); +#endif IntPoint currentDocumentPosition = frameView->windowToContents(frame->eventHandler()->currentMousePosition()); scrollRectToVisible(IntRect(currentDocumentPosition, IntSize(1, 1)), false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); @@ -1594,6 +1754,11 @@ IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& absolutePoint) const return localPoint - bottomRight; } +bool RenderLayer::hasOverflowControls() const +{ + return m_hBar || m_vBar || m_scrollCorner || renderer()->style()->resize() != RESIZE_NONE; +} + void RenderLayer::positionOverflowControls(int tx, int ty) { if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) @@ -1779,17 +1944,23 @@ RenderLayer::updateScrollInfoAfterLayout() // Set up the range (and page step/line step). if (m_hBar) { int clientWidth = box->clientWidth(); - int pageStep = (clientWidth - cAmountToKeepWhenPaging); - if (pageStep < 0) pageStep = clientWidth; - m_hBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); + int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1); + m_hBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); m_hBar->setProportion(clientWidth, m_scrollWidth); + // Explicitly set the horizontal scroll value. This ensures that when a + // right-to-left scrollable area's width (or content width) changes, the + // top right corner of the content doesn't shift with respect to the top + // right corner of the area. Conceptually, right-to-left areas have + // their origin at the top-right, but RenderLayer is top-left oriented, + // so this is needed to keep everything working (see how scrollXOffset() + // differs from scrollYOffset() to get an idea of why the horizontal and + // vertical scrollbars need to be treated differently). m_hBar->setValue(scrollXOffset()); } if (m_vBar) { int clientHeight = box->clientHeight(); - int pageStep = (clientHeight - cAmountToKeepWhenPaging); - if (pageStep < 0) pageStep = clientHeight; - m_vBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); + int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1); + m_vBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); m_vBar->setProportion(clientHeight, m_scrollHeight); } @@ -1842,7 +2013,7 @@ void RenderLayer::paintScrollCorner(GraphicsContext* context, int tx, int ty, co return; } - context->fillRect(absRect, Color::white); + context->fillRect(absRect, Color::white, box->style()->colorSpace()); } void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) @@ -1871,7 +2042,7 @@ void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const I // Paint the resizer control. DEFINE_STATIC_LOCAL(RefPtr<Image>, resizeCornerImage, (Image::loadPlatformResource("textAreaResizeCorner"))); IntPoint imagePoint(absRect.right() - resizeCornerImage->width(), absRect.bottom() - resizeCornerImage->height()); - context->drawImage(resizeCornerImage.get(), imagePoint); + context->drawImage(resizeCornerImage.get(), box->style()->colorSpace(), imagePoint); // Draw a frame around the resizer (1px grey line) if there are any scrollbars present. // Clipping will exclude the right and bottom edges of this frame. @@ -1880,9 +2051,9 @@ void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const I context->clip(absRect); IntRect largerCorner = absRect; largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1)); - context->setStrokeColor(Color(makeRGB(217, 217, 217))); + context->setStrokeColor(Color(makeRGB(217, 217, 217)), DeviceColorSpace); context->setStrokeThickness(1.0f); - context->setFillColor(Color::transparent); + context->setFillColor(Color::transparent, DeviceColorSpace); context->drawRect(largerCorner); context->restore(); } @@ -1965,10 +2136,10 @@ bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularit return (didHorizontalScroll || didVerticalScroll); } -void RenderLayer::paint(GraphicsContext* p, const IntRect& damageRect, PaintRestriction paintRestriction, RenderObject *paintingRoot) +void RenderLayer::paint(GraphicsContext* p, const IntRect& damageRect, PaintBehavior paintBehavior, RenderObject *paintingRoot) { RenderObject::OverlapTestRequestMap overlapTestRequests; - paintLayer(this, p, damageRect, paintRestriction, paintingRoot, &overlapTestRequests); + paintLayer(this, p, damageRect, paintBehavior, paintingRoot, &overlapTestRequests); RenderObject::OverlapTestRequestMap::iterator end = overlapTestRequests.end(); for (RenderObject::OverlapTestRequestMap::iterator it = overlapTestRequests.begin(); it != end; ++it) it->first->setOverlapTestResult(false); @@ -2012,7 +2183,7 @@ static bool shouldDoSoftwarePaint(const RenderLayer* layer, bool paintingReflect #endif void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, - const IntRect& paintDirtyRect, PaintRestriction paintRestriction, + const IntRect& paintDirtyRect, PaintBehavior paintBehavior, RenderObject* paintingRoot, RenderObject::OverlapTestRequestMap* overlapTestRequests, PaintLayerFlags paintFlags) { @@ -2020,7 +2191,7 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, if (isComposited()) { // The updatingControlTints() painting pass goes through compositing layers, // but we need to ensure that we don't cache clip rects computed with the wrong root in this case. - if (p->updatingControlTints()) + if (p->updatingControlTints() || (paintBehavior & PaintBehaviorFlattenCompositingLayers)) paintFlags |= PaintLayerTemporaryClipRects; else if (!backing()->paintingGoesToWindow() && !shouldDoSoftwarePaint(this, paintFlags & PaintLayerPaintingReflection)) { // If this RenderLayer should paint into its backing, that will be done via RenderLayerBacking::paintIntoLayer(). @@ -2039,26 +2210,25 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, if (!renderer()->opacity()) return; - if (paintsWithTransparency()) + if (paintsWithTransparency(paintBehavior)) paintFlags |= PaintLayerHaveTransparency; // Apply a transform if we have one. A reflection is considered to be a transform, since it is a flip and a translate. - if (paintsWithTransform() && !(paintFlags & PaintLayerAppliedTransform)) { + if (paintsWithTransform(paintBehavior) && !(paintFlags & PaintLayerAppliedTransform)) { + TransformationMatrix layerTransform = renderableTransform(paintBehavior); // If the transform can't be inverted, then don't paint anything. - if (!m_transform->isInvertible()) + if (!layerTransform.isInvertible()) return; // If we have a transparency layer enclosing us and we are the root of a transform, then we need to establish the transparency // layer from the parent now. if (paintFlags & PaintLayerHaveTransparency) - parent()->beginTransparencyLayers(p, rootLayer); + parent()->beginTransparencyLayers(p, rootLayer, paintBehavior); // Make sure the parent's clip rects have been calculated. IntRect clipRect = paintDirtyRect; if (parent()) { - ClipRects parentRects; - parentClipRects(rootLayer, parentRects, paintFlags & PaintLayerTemporaryClipRects); - clipRect = parentRects.overflowClipRect(); + clipRect = backgroundClipRect(rootLayer, paintFlags & PaintLayerTemporaryClipRects); clipRect.intersect(paintDirtyRect); } @@ -2070,21 +2240,15 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, int x = 0; int y = 0; convertToLayerCoords(rootLayer, x, y); -#ifdef ANDROID_FASTER_MATRIX - TransformationMatrix transform(*m_transform); + TransformationMatrix transform(layerTransform); transform.translateRight(x, y); -#else - TransformationMatrix transform; - transform.translate(x, y); - transform = *m_transform * transform; -#endif // Apply the transform. p->save(); - p->concatCTM(transform); + p->concatCTM(transform.toAffineTransform()); // Now do a paint with the root layer shifted to be us. - paintLayer(this, p, transform.inverse().mapRect(paintDirtyRect), paintRestriction, paintingRoot, overlapTestRequests, paintFlags | PaintLayerAppliedTransform); + paintLayer(this, p, transform.inverse().mapRect(paintDirtyRect), paintBehavior, paintingRoot, overlapTestRequests, paintFlags | PaintLayerAppliedTransform); p->restore(); @@ -2101,7 +2265,7 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, if (m_reflection && !m_paintingInsideReflection) { // Mark that we are now inside replica painting. m_paintingInsideReflection = true; - reflectionLayer()->paintLayer(rootLayer, p, paintDirtyRect, paintRestriction, paintingRoot, overlapTestRequests, localPaintFlags | PaintLayerPaintingReflection); + reflectionLayer()->paintLayer(rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags | PaintLayerPaintingReflection); m_paintingInsideReflection = false; } @@ -2116,13 +2280,13 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, // Ensure our lists are up-to-date. updateCompositingAndLayerListsIfNeeded(); - bool selectionOnly = paintRestriction == PaintRestrictionSelectionOnly || paintRestriction == PaintRestrictionSelectionOnlyBlackText; - bool forceBlackText = paintRestriction == PaintRestrictionSelectionOnlyBlackText; + bool forceBlackText = paintBehavior & PaintBehaviorForceBlackText; + bool selectionOnly = paintBehavior & PaintBehaviorSelectionOnly; // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set). // Else, our renderer tree may or may not contain the painting root, so we pass that root along - // so it will be tested against as we decend through the renderers. + // so it will be tested against as we descend through the renderers. RenderObject* paintingRootForRenderer = 0; if (paintingRoot && !renderer()->isDescendantOf(paintingRoot)) paintingRootForRenderer = paintingRoot; @@ -2135,7 +2299,7 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) { // Begin transparency layers lazily now that we know we have to paint something. if (haveTransparency) - beginTransparencyLayers(p, rootLayer); + beginTransparencyLayers(p, rootLayer, paintBehavior); // Paint our background first, before painting any child layers. // Establish the clip used to paint our background. @@ -2152,13 +2316,13 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, // Now walk the sorted list of children with negative z-indices. if (m_negZOrderList) for (Vector<RenderLayer*>::iterator it = m_negZOrderList->begin(); it != m_negZOrderList->end(); ++it) - it[0]->paintLayer(rootLayer, p, paintDirtyRect, paintRestriction, paintingRoot, overlapTestRequests, localPaintFlags); + it[0]->paintLayer(rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags); // Now establish the appropriate clip and paint our child RenderObjects. if (shouldPaint && !clipRectToApply.isEmpty()) { // Begin transparency layers lazily now that we know we have to paint something. if (haveTransparency) - beginTransparencyLayers(p, rootLayer); + beginTransparencyLayers(p, rootLayer, paintBehavior); // Set up the clip used when painting our children. setClip(p, paintDirtyRect, clipRectToApply); @@ -2191,12 +2355,12 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, // Paint any child layers that have overflow. if (m_normalFlowList) for (Vector<RenderLayer*>::iterator it = m_normalFlowList->begin(); it != m_normalFlowList->end(); ++it) - it[0]->paintLayer(rootLayer, p, paintDirtyRect, paintRestriction, paintingRoot, overlapTestRequests, localPaintFlags); + it[0]->paintLayer(rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags); // 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) - it[0]->paintLayer(rootLayer, p, paintDirtyRect, paintRestriction, paintingRoot, overlapTestRequests, localPaintFlags); + it[0]->paintLayer(rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags); if (renderer()->hasMask() && shouldPaint && !selectionOnly && !damageRect.isEmpty()) { setClip(p, paintDirtyRect, damageRect); @@ -2245,15 +2409,10 @@ bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) } } - // Now determine if the result is inside an anchor; make sure an image map wins if - // it already set URLElement and only use the innermost. + // Now determine if the result is inside an anchor - if the urlElement isn't already set. Node* node = result.innerNode(); - while (node) { - // for imagemaps, URLElement is the associated area element not the image itself - if (node->isLink() && !result.URLElement() && !node->hasTagName(imgTag)) - result.setURLElement(static_cast<Element*>(node)); - node = node->eventParentNode(); - } + if (node && !result.URLElement()) + result.setURLElement(static_cast<Element*>(node->enclosingLinkEventParentOrSelf())); // Next set up the correct :hover/:active state along the new chain. updateHoverActiveState(request, result); @@ -2368,9 +2527,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont if (transform() && !appliedTransform) { // Make sure the parent's clip rects have been calculated. if (parent()) { - ClipRects parentRects; - parentClipRects(rootLayer, parentRects, useTemporaryClipRects); - IntRect clipRect = parentRects.overflowClipRect(); + IntRect clipRect = backgroundClipRect(rootLayer, useTemporaryClipRects); // Go ahead and test the enclosing clip now. if (!clipRect.contains(hitTestPoint)) return 0; @@ -2499,7 +2656,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. if (fgRect.contains(hitTestPoint) && isSelfPaintingLayer()) { - // Hit test with a temporary HitTestResult, because we onlyl want to commit to 'result' if we know we're frontmost. + // Hit test with a temporary HitTestResult, because we only want to commit to 'result' if we know we're frontmost. HitTestResult tempResult(result.point()); if (hitTestContents(request, tempResult, layerBounds, hitTestPoint, HitTestDescendants) && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { @@ -2668,10 +2825,10 @@ void RenderLayer::parentClipRects(const RenderLayer* rootLayer, ClipRects& clipR clipRects = *parent()->clipRects(); } -void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds, - IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect, bool temporaryClipRects) const +IntRect RenderLayer::backgroundClipRect(const RenderLayer* rootLayer, bool temporaryClipRects) const { - if (rootLayer != this && parent()) { + IntRect backgroundRect; + if (parent()) { ClipRects parentRects; parentClipRects(rootLayer, parentRects, temporaryClipRects); backgroundRect = renderer()->style()->position() == FixedPosition ? parentRects.fixedClipRect() : @@ -2681,7 +2838,15 @@ void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& pa ASSERT(view); if (view && parentRects.fixed() && rootLayer->renderer() == view) backgroundRect.move(view->frameView()->scrollX(), view->frameView()->scrollY()); + } + return backgroundRect; +} +void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds, + IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect, bool temporaryClipRects) const +{ + if (rootLayer != this && parent()) { + backgroundRect = backgroundClipRect(rootLayer, temporaryClipRects); backgroundRect.intersect(paintDirtyRect); } else backgroundRect = paintDirtyRect; @@ -2730,17 +2895,49 @@ void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& pa IntRect RenderLayer::childrenClipRect() const { RenderLayer* rootLayer = renderer()->view()->layer(); + RenderLayer* clippingRootLayer = clippingRoot(); IntRect layerBounds, backgroundRect, foregroundRect, outlineRect; - calculateRects(rootLayer, rootLayer->boundingBox(rootLayer), layerBounds, backgroundRect, foregroundRect, outlineRect); - return foregroundRect; + calculateRects(clippingRootLayer, rootLayer->boundingBox(rootLayer), layerBounds, backgroundRect, foregroundRect, outlineRect); + return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregroundRect)).enclosingBoundingBox(); } IntRect RenderLayer::selfClipRect() const { RenderLayer* rootLayer = renderer()->view()->layer(); + RenderLayer* clippingRootLayer = clippingRoot(); IntRect layerBounds, backgroundRect, foregroundRect, outlineRect; - calculateRects(rootLayer, rootLayer->boundingBox(rootLayer), layerBounds, backgroundRect, foregroundRect, outlineRect); - return backgroundRect; + calculateRects(clippingRootLayer, rootLayer->boundingBox(rootLayer), layerBounds, backgroundRect, foregroundRect, outlineRect); + return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(backgroundRect)).enclosingBoundingBox(); +} + +void RenderLayer::addBlockSelectionGapsBounds(const IntRect& bounds) +{ + m_blockSelectionGapsBounds.unite(bounds); +} + +void RenderLayer::clearBlockSelectionGapsBounds() +{ + m_blockSelectionGapsBounds = IntRect(); + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + child->clearBlockSelectionGapsBounds(); +} + +void RenderLayer::repaintBlockSelectionGaps() +{ + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + child->repaintBlockSelectionGaps(); + + if (m_blockSelectionGapsBounds.isEmpty()) + return; + + IntRect rect = m_blockSelectionGapsBounds; + rect.move(-scrolledContentOffset()); + if (renderer()->hasOverflowClip()) + rect.intersect(toRenderBox(renderer())->overflowClipRect(0, 0)); + if (renderer()->hasClip()) + rect.intersect(toRenderBox(renderer())->clipRect(0, 0)); + if (!rect.isEmpty()) + renderer()->repaintRectangle(rect); } bool RenderLayer::intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect, const RenderLayer* rootLayer) const @@ -2771,7 +2968,7 @@ 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 - // inline. In other words, if some <span> wraps to three lines, we'll create a bounding box that fully encloses the root + // inline. In other words, if some <span> wraps to three lines, we'll create a bounding box that fully encloses the // line boxes of all three lines (including overflow on those lines). // (2) Left/Top Overflow. The width/height of layers already includes right/bottom overflow. However, in the case of left/top // overflow, we have to create a bounding box that will extend to include this overflow. @@ -2785,8 +2982,8 @@ IntRect RenderLayer::localBoundingBox() const InlineFlowBox* firstBox = inlineFlow->firstLineBox(); if (!firstBox) return result; - int top = firstBox->root()->topOverflow(); - int bottom = inlineFlow->lastLineBox()->root()->bottomOverflow(); + int top = firstBox->topVisibleOverflow(); + int bottom = inlineFlow->lastLineBox()->bottomVisibleOverflow(); int left = firstBox->x(); for (InlineRunBox* curr = firstBox->nextLineBox(); curr; curr = curr->nextLineBox()) left = min(left, curr->x()); @@ -2797,7 +2994,7 @@ IntRect RenderLayer::localBoundingBox() const if (child->isTableCell()) { IntRect bbox = toRenderBox(child)->borderBoxRect(); result.unite(bbox); - IntRect overflowRect = renderBox()->overflowRect(false); + IntRect overflowRect = renderBox()->visibleOverflowRect(); if (bbox != overflowRect) result.unite(overflowRect); } @@ -2810,7 +3007,7 @@ IntRect RenderLayer::localBoundingBox() const else { IntRect bbox = box->borderBoxRect(); result = bbox; - IntRect overflowRect = box->overflowRect(false); + IntRect overflowRect = box->visibleOverflowRect(); if (bbox != overflowRect) result.unite(overflowRect); } @@ -2873,6 +3070,11 @@ void RenderLayer::clearBacking() { m_backing.clear(); } + +bool RenderLayer::hasCompositedMask() const +{ + return m_backing && m_backing->hasMaskLayer(); +} #endif void RenderLayer::setParent(RenderLayer* parent) @@ -3089,7 +3291,7 @@ void RenderLayer::updateCompositingAndLayerListsIfNeeded() #if USE(ACCELERATED_COMPOSITING) if (compositor()->inCompositingMode()) { if ((isStackingContext() && m_zOrderListsDirty) || m_normalFlowListDirty) - compositor()->updateCompositingLayers(this); + compositor()->updateCompositingLayers(CompositingUpdateOnPaitingOrHitTest, this); return; } #endif @@ -3150,7 +3352,7 @@ void RenderLayer::repaintIncludingNonCompositingDescendants(RenderBoxModelObject bool RenderLayer::shouldBeNormalFlowOnly() const { - return (renderer()->hasOverflowClip() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isVideo()) && + return (renderer()->hasOverflowClip() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isVideo() || renderer()->isEmbeddedObject()) && !renderer()->isPositioned() && !renderer()->isRelPositioned() && !renderer()->hasTransform() && @@ -3159,7 +3361,7 @@ bool RenderLayer::shouldBeNormalFlowOnly() const bool RenderLayer::isSelfPaintingLayer() const { - return !isNormalFlowOnly() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isTableRow() || renderer()->isVideo(); + return !isNormalFlowOnly() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isTableRow() || renderer()->isVideo() || renderer()->isEmbeddedObject(); } void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle*) @@ -3173,7 +3375,7 @@ void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle*) dirtyStackingContextZOrderLists(); } - if (renderer()->style()->overflowX() == OMARQUEE && renderer()->style()->marqueeBehavior() != MNONE) { + if (renderer()->style()->overflowX() == OMARQUEE && renderer()->style()->marqueeBehavior() != MNONE && renderer()->isBox()) { if (!m_marquee) m_marquee = new RenderMarquee(this); m_marquee->updateMarqueeStyle(); @@ -3183,10 +3385,9 @@ void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle*) m_marquee = 0; } - if (!hasReflection() && m_reflection) { - m_reflection->destroy(); - m_reflection = 0; - } else if (hasReflection()) { + if (!hasReflection() && m_reflection) + removeReflection(); + else if (hasReflection()) { if (!m_reflection) createReflection(); updateReflectionStyle(); @@ -3260,6 +3461,16 @@ void RenderLayer::createReflection() m_reflection->setParent(renderer()); // We create a 1-way connection. } +void RenderLayer::removeReflection() +{ + if (!m_reflection->documentBeingDestroyed()) + m_reflection->removeLayers(this); + + m_reflection->setParent(0); + m_reflection->destroy(); + m_reflection = 0; +} + void RenderLayer::updateReflectionStyle() { RefPtr<RenderStyle> newStyle = RenderStyle::create(); @@ -3298,3 +3509,16 @@ void RenderLayer::updateReflectionStyle() } } // namespace WebCore + +#ifndef NDEBUG +void showLayerTree(const WebCore::RenderLayer* layer) +{ + if (!layer) + return; + + if (WebCore::Frame* frame = layer->renderer()->document()->frame()) { + WebCore::String output = externalRepresentation(frame, WebCore::RenderAsTextShowAllLayers | WebCore::RenderAsTextShowLayerNesting | WebCore::RenderAsTextShowCompositedLayers); + fprintf(stderr, "%s\n", output.utf8().data()); + } +} +#endif diff --git a/WebCore/rendering/RenderLayer.h b/WebCore/rendering/RenderLayer.h index 1772c66..2c8d184 100644 --- a/WebCore/rendering/RenderLayer.h +++ b/WebCore/rendering/RenderLayer.h @@ -202,9 +202,10 @@ public: bool isTransparent() const; RenderLayer* transparentPaintingAncestor(); - void beginTransparencyLayers(GraphicsContext*, const RenderLayer* rootLayer); + void beginTransparencyLayers(GraphicsContext*, const RenderLayer* rootLayer, PaintBehavior); bool hasReflection() const { return renderer()->hasReflection(); } + bool isReflection() const { return renderer()->isReplica(); } RenderReplica* reflection() const { return m_reflection; } RenderLayer* reflectionLayer() const; @@ -262,6 +263,7 @@ public: int verticalScrollbarWidth() const; int horizontalScrollbarHeight() const; + bool hasOverflowControls() const; void positionOverflowControls(int tx, int ty); bool isPointInResizeControl(const IntPoint& absolutePoint) const; bool hitTestOverflowControls(HitTestResult&, const IntPoint& localPoint); @@ -298,10 +300,11 @@ public: enum UpdateLayerPositionsFlag { DoFullRepaint = 1, CheckForRepaint = 1 << 1, - UpdateCompositingLayers = 1 << 2, + IsCompositingUpdateRoot = 1 << 2, + UpdateCompositingLayers = 1 << 3, }; typedef unsigned UpdateLayerPositionsFlags; - void updateLayerPositions(UpdateLayerPositionsFlags = DoFullRepaint | UpdateCompositingLayers); + void updateLayerPositions(UpdateLayerPositionsFlags = DoFullRepaint | IsCompositingUpdateRoot | UpdateCompositingLayers); void updateTransform(); @@ -311,10 +314,22 @@ public: void clearClipRectsIncludingDescendants(); void clearClipRects(); + void addBlockSelectionGapsBounds(const IntRect&); + void clearBlockSelectionGapsBounds(); + void repaintBlockSelectionGaps(); + // Get the enclosing stacking context for this layer. A stacking context is a layer // that has a non-auto z-index. RenderLayer* stackingContext() const; - bool isStackingContext() const { return !hasAutoZIndex() || renderer()->isRenderView(); } +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + bool isFixed() const { return renderer()->isPositioned() && renderer()->style()->position() == FixedPosition; } + // If fixed elements are composited, they will be containing children + bool isStackingContext() const { + return !hasAutoZIndex() || renderer()->isRenderView() || (isComposited() && isFixed()); + } +#else + bool isStackingContext() const { return !hasAutoZIndex() || renderer()->isRenderView() ; } +#endif void dirtyZOrderLists(); void dirtyStackingContextZOrderLists(); @@ -327,6 +342,7 @@ public: Vector<RenderLayer*>* normalFlowList() const { return m_normalFlowList; } bool hasVisibleContent() const { return m_hasVisibleContent; } + bool hasVisibleDescendant() const { return m_hasVisibleDescendant; } void setHasVisibleContent(bool); void dirtyVisibleContentStatus(); @@ -334,6 +350,9 @@ public: // the <html> layer and the root layer). RenderLayer* enclosingPositionedAncestor() const; + // The layer relative to which clipping rects for this layer are computed. + RenderLayer* clippingRoot() const; + #if USE(ACCELERATED_COMPOSITING) // Enclosing compositing layer; if includeSelf is true, may return this. RenderLayer* enclosingCompositingLayer(bool includeSelf = true) const; @@ -350,7 +369,7 @@ public: // paints the layers that intersect the damage rect from back to // front. The hitTest method looks for mouse events by walking // layers that intersect the point from front to back. - void paint(GraphicsContext*, const IntRect& damageRect, PaintRestriction = PaintRestrictionNone, RenderObject* paintingRoot = 0); + void paint(GraphicsContext*, const IntRect& damageRect, PaintBehavior = PaintBehaviorNormal, RenderObject* paintingRoot = 0); bool hitTest(const HitTestRequest&, HitTestResult&); // This method figures out our layerBounds in coordinates relative to @@ -388,7 +407,7 @@ public: int staticX() const { return m_staticX; } int staticY() const { return m_staticY; } void setStaticX(int staticX) { m_staticX = staticX; } - void setStaticY(int staticY); + void setStaticY(int staticY) { m_staticY = staticY; } bool hasTransform() const { return renderer()->hasTransform(); } // Note that this transform has the transform-origin baked in. @@ -397,6 +416,7 @@ public: // resulting transform has transform-origin baked in. If the layer does not have a transform, // returns the identity matrix. TransformationMatrix currentTransform() const; + TransformationMatrix renderableTransform(PaintBehavior) const; // Get the perspective transform, which is applied to transformed sublayers. // Returns true if the layer has a -webkit-perspective. @@ -415,21 +435,23 @@ public: #if USE(ACCELERATED_COMPOSITING) bool isComposited() const { return m_backing != 0; } + bool hasCompositedMask() const; RenderLayerBacking* backing() const { return m_backing.get(); } RenderLayerBacking* ensureBacking(); void clearBacking(); #else bool isComposited() const { return false; } + bool hasCompositedMask() const { return false; } #endif - bool paintsWithTransparency() const + bool paintsWithTransparency(PaintBehavior paintBehavior) const { - return isTransparent() && !isComposited(); + return isTransparent() && ((paintBehavior & PaintBehaviorFlattenCompositingLayers) || !isComposited()); } - bool paintsWithTransform() const + bool paintsWithTransform(PaintBehavior paintBehavior) const { - return transform() && !isComposited(); + return transform() && ((paintBehavior & PaintBehaviorFlattenCompositingLayers) || !isComposited()); } private: @@ -461,7 +483,7 @@ private: typedef unsigned PaintLayerFlags; void paintLayer(RenderLayer* rootLayer, GraphicsContext*, const IntRect& paintDirtyRect, - PaintRestriction, RenderObject* paintingRoot, RenderObject::OverlapTestRequestMap* = 0, + PaintBehavior, RenderObject* paintingRoot, RenderObject::OverlapTestRequestMap* = 0, PaintLayerFlags paintFlags = 0); RenderLayer* hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, @@ -507,11 +529,14 @@ private: Node* enclosingElement() const; void createReflection(); + void removeReflection(); + void updateReflectionStyle(); bool paintingInsideReflection() const { return m_paintingInsideReflection; } void setPaintingInsideReflection(bool b) { m_paintingInsideReflection = b; } void parentClipRects(const RenderLayer* rootLayer, ClipRects&, bool temporaryClipRects = false) const; + IntRect backgroundClipRect(const RenderLayer* rootLayer, bool temporaryClipRects) const; RenderLayer* enclosingTransformedAncestor() const; @@ -637,6 +662,9 @@ protected: RenderScrollbarPart* m_scrollCorner; RenderScrollbarPart* m_resizer; +private: + IntRect m_blockSelectionGapsBounds; + #if USE(ACCELERATED_COMPOSITING) OwnPtr<RenderLayerBacking> m_backing; #endif @@ -644,4 +672,9 @@ protected: } // namespace WebCore +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showLayerTree(const WebCore::RenderLayer* layer); +#endif + #endif // RenderLayer_h diff --git a/WebCore/rendering/RenderLayerBacking.cpp b/WebCore/rendering/RenderLayerBacking.cpp index e98c458..3f60557 100644 --- a/WebCore/rendering/RenderLayerBacking.cpp +++ b/WebCore/rendering/RenderLayerBacking.cpp @@ -28,18 +28,27 @@ #if USE(ACCELERATED_COMPOSITING) #include "AnimationController.h" +#if ENABLE(3D_CANVAS) +#include "WebGLRenderingContext.h" +#endif #include "CSSPropertyNames.h" #include "CSSStyleSelector.h" #include "FrameView.h" #include "GraphicsContext.h" #include "GraphicsLayer.h" +#include "HTMLCanvasElement.h" #include "HTMLElement.h" #include "HTMLNames.h" +#include "InspectorTimelineAgent.h" +#include "KeyframeList.h" +#include "PluginWidget.h" #include "RenderBox.h" #include "RenderImage.h" #include "RenderLayerCompositor.h" +#include "RenderEmbeddedObject.h" #include "RenderVideo.h" #include "RenderView.h" +#include "Settings.h" #include "RenderLayerBacking.h" @@ -47,17 +56,26 @@ using namespace std; namespace WebCore { +using namespace HTMLNames; + static bool hasBorderOutlineOrShadow(const RenderStyle*); -static bool hasBoxDecorations(const RenderStyle*); -static bool hasBoxDecorationsWithBackgroundImage(const RenderStyle*); +static bool hasBoxDecorationsOrBackground(const RenderStyle*); +static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle*); + +static inline bool is3DCanvas(RenderObject* renderer) +{ +#if ENABLE(3D_CANVAS) + if (renderer->isCanvas()) + return static_cast<HTMLCanvasElement*>(renderer->node())->is3D(); +#else + UNUSED_PARAM(renderer); +#endif + return false; +} RenderLayerBacking::RenderLayerBacking(RenderLayer* layer) : m_owningLayer(layer) - , m_ancestorClippingLayer(0) - , m_graphicsLayer(0) - , m_foregroundLayer(0) - , m_clippingLayer(0) - , m_hasDirectlyCompositedContent(false) + , m_artificiallyInflatedBounds(false) { createGraphicsLayer(); } @@ -66,12 +84,13 @@ RenderLayerBacking::~RenderLayerBacking() { updateClippingLayers(false, false); updateForegroundLayer(false); + updateMaskLayer(false); destroyGraphicsLayer(); } void RenderLayerBacking::createGraphicsLayer() { - m_graphicsLayer = GraphicsLayer::createGraphicsLayer(this); + m_graphicsLayer = GraphicsLayer::create(this); #ifndef NDEBUG if (renderer()->node()) { @@ -79,16 +98,18 @@ void RenderLayerBacking::createGraphicsLayer() m_graphicsLayer->setName("Document Node"); else { if (renderer()->node()->isHTMLElement() && renderer()->node()->hasID()) - m_graphicsLayer->setName(renderer()->renderName() + String(" ") + static_cast<HTMLElement*>(renderer()->node())->id()); + m_graphicsLayer->setName(renderer()->renderName() + String(" ") + static_cast<HTMLElement*>(renderer()->node())->getIDAttribute()); else m_graphicsLayer->setName(renderer()->renderName()); } - } else + } else if (m_owningLayer->isReflection()) + m_graphicsLayer->setName("Reflection"); + else m_graphicsLayer->setName("Anonymous Node"); #endif // NDEBUG - updateLayerOpacity(); - updateLayerTransform(); + updateLayerOpacity(renderer()->style()); + updateLayerTransform(renderer()->style()); } void RenderLayerBacking::destroyGraphicsLayer() @@ -96,25 +117,19 @@ void RenderLayerBacking::destroyGraphicsLayer() if (m_graphicsLayer) m_graphicsLayer->removeFromParent(); - delete m_graphicsLayer; m_graphicsLayer = 0; - - delete m_foregroundLayer; m_foregroundLayer = 0; - - delete m_clippingLayer; m_clippingLayer = 0; + m_maskLayer = 0; } -void RenderLayerBacking::updateLayerOpacity() +void RenderLayerBacking::updateLayerOpacity(const RenderStyle* style) { - m_graphicsLayer->setOpacity(compositingOpacity(renderer()->opacity())); + m_graphicsLayer->setOpacity(compositingOpacity(style->opacity())); } -void RenderLayerBacking::updateLayerTransform() +void RenderLayerBacking::updateLayerTransform(const RenderStyle* style) { - 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; @@ -126,7 +141,31 @@ void RenderLayerBacking::updateLayerTransform() m_graphicsLayer->setTransform(t); } -void RenderLayerBacking::updateAfterLayout(UpdateDepth updateDepth) +static bool hasNonZeroTransformOrigin(const RenderObject* renderer) +{ + RenderStyle* style = renderer->style(); + return (style->transformOriginX().type() == Fixed && style->transformOriginX().value()) + || (style->transformOriginY().type() == Fixed && style->transformOriginY().value()); +} + +void RenderLayerBacking::updateCompositedBounds() +{ + IntRect layerBounds = compositor()->calculateCompositedBounds(m_owningLayer, m_owningLayer); + + // If the element has a transform-origin that has fixed lengths, and the renderer has zero size, + // then we need to ensure that the compositing layer has non-zero size so that we can apply + // the transform-origin via the GraphicsLayer anchorPoint (which is expressed as a fractional value). + if (layerBounds.isEmpty() && hasNonZeroTransformOrigin(renderer())) { + layerBounds.setWidth(1); + layerBounds.setHeight(1); + m_artificiallyInflatedBounds = true; + } else + m_artificiallyInflatedBounds = false; + + setCompositedBounds(layerBounds); +} + +void RenderLayerBacking::updateAfterLayout(UpdateDepth updateDepth, bool isUpdateRoot) { RenderLayerCompositor* layerCompositor = compositor(); if (!layerCompositor->compositingLayersNeedRebuild()) { @@ -137,10 +176,10 @@ void RenderLayerBacking::updateAfterLayout(UpdateDepth updateDepth) // // The solution is to update compositing children of this layer here, // via updateCompositingChildrenGeometry(). - setCompositedBounds(layerCompositor->calculateCompositedBounds(m_owningLayer, m_owningLayer)); + updateCompositedBounds(); layerCompositor->updateCompositingDescendantGeometry(m_owningLayer, m_owningLayer, updateDepth); - if (!m_owningLayer->parent()) { + if (isUpdateRoot) { updateGraphicsLayerGeometry(); layerCompositor->updateRootLayerPosition(); } @@ -158,19 +197,33 @@ bool RenderLayerBacking::updateGraphicsLayerConfiguration() if (updateClippingLayers(compositor->clippedByAncestor(m_owningLayer), compositor->clipsCompositingDescendants(m_owningLayer))) layerConfigChanged = true; - m_hasDirectlyCompositedContent = false; - if (canUseDirectCompositing()) { - if (renderer()->isImage()) { - updateImageContents(); - m_hasDirectlyCompositedContent = true; - m_graphicsLayer->setDrawsContent(false); + if (updateMaskLayer(m_owningLayer->renderer()->hasMask())) + m_graphicsLayer->setMaskLayer(m_maskLayer.get()); + + if (m_owningLayer->hasReflection()) { + if (m_owningLayer->reflectionLayer()->backing()) { + GraphicsLayer* reflectionLayer = m_owningLayer->reflectionLayer()->backing()->graphicsLayer(); + m_graphicsLayer->setReplicatedByLayer(reflectionLayer); } + } else + m_graphicsLayer->setReplicatedByLayer(0); + + if (isDirectlyCompositedImage()) + updateImageContents(); + + if (renderer()->isEmbeddedObject() && toRenderEmbeddedObject(renderer())->allowsAcceleratedCompositing()) { + PluginWidget* pluginWidget = static_cast<PluginWidget*>(toRenderEmbeddedObject(renderer())->widget()); + m_graphicsLayer->setContentsToMedia(pluginWidget->platformLayer()); + } - if (rendererHasBackground()) - m_graphicsLayer->setBackgroundColor(rendererBackgroundColor()); - else - m_graphicsLayer->clearBackgroundColor(); +#if ENABLE(3D_CANVAS) + if (is3DCanvas(renderer())) { + HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer()->node()); + WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(canvas->renderingContext()); + if (context->graphicsContext3D()->platformGraphicsContext3D()) + m_graphicsLayer->setContentsToGraphicsContext3D(context->graphicsContext3D()); } +#endif return layerConfigChanged; } @@ -184,14 +237,14 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() // 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(); + updateLayerTransform(renderer()->style()); // Set opacity, if it is not animating. if (!renderer()->animation()->isAnimatingPropertyOnRenderer(renderer(), CSSPropertyOpacity)) - updateLayerOpacity(); + updateLayerOpacity(renderer()->style()); RenderStyle* style = renderer()->style(); - m_graphicsLayer->setPreserves3D(style->transformStyle3D() == TransformStyle3DPreserve3D); + m_graphicsLayer->setPreserves3D(style->transformStyle3D() == TransformStyle3DPreserve3D && !renderer()->hasReflection()); m_graphicsLayer->setBackfaceVisibility(style->backfaceVisibility() == BackfaceVisibilityVisible); RenderLayer* compAncestor = m_owningLayer->ancestorCompositingLayer(); @@ -222,10 +275,7 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() // Call calculateRects to get the backgroundRect which is what is used to clip the contents of this // layer. Note that we call it with temporaryClipRects = true because normally when computing clip rects // for a compositing layer, rootLayer is the layer itself. - ClipRects parentRects; - m_owningLayer->parentClipRects(compAncestor, parentRects, true); - IntRect parentClipRect = parentRects.overflowClipRect(); - + IntRect parentClipRect = m_owningLayer->backgroundClipRect(compAncestor, true); m_ancestorClippingLayer->setPosition(FloatPoint() + (parentClipRect.location() - graphicsLayerParentLocation)); m_ancestorClippingLayer->setSize(parentClipRect.size()); @@ -251,13 +301,19 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() } // If we have a layer that clips children, position it. + IntRect clippingBox; if (m_clippingLayer) { - IntRect clippingBox = toRenderBox(renderer())->overflowClipRect(0, 0); + 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_maskLayer) { + m_maskLayer->setSize(m_graphicsLayer->size()); + m_maskLayer->setPosition(FloatPoint()); + } + if (m_owningLayer->hasTransform()) { const IntRect borderBox = toRenderBox(renderer())->borderBoxRect(); @@ -293,15 +349,35 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() } if (m_foregroundLayer) { - // The contents layer is always coincidental with the graphicsLayer for now. - m_foregroundLayer->setPosition(IntPoint(0, 0)); - m_foregroundLayer->setSize(newSize); - m_foregroundLayer->setOffsetFromRenderer(m_graphicsLayer->offsetFromRenderer()); + FloatPoint foregroundPosition; + FloatSize foregroundSize = newSize; + IntSize foregroundOffset = m_graphicsLayer->offsetFromRenderer(); + // If we have a clipping layer (which clips descendants), then the foreground layer is a child of it, + // so that it gets correctly sorted with children. In that case, position relative to the clipping layer. + if (m_clippingLayer) { + foregroundPosition = FloatPoint() + (localCompositingBounds.location() - clippingBox.location()); + foregroundSize = FloatSize(clippingBox.size()); + foregroundOffset = clippingBox.location() - IntPoint(); + } + + m_foregroundLayer->setPosition(foregroundPosition); + m_foregroundLayer->setSize(foregroundSize); + m_foregroundLayer->setOffsetFromRenderer(foregroundOffset); + } + + if (m_owningLayer->reflectionLayer() && m_owningLayer->reflectionLayer()->isComposited()) { + RenderLayerBacking* reflectionBacking = m_owningLayer->reflectionLayer()->backing(); + reflectionBacking->updateGraphicsLayerGeometry(); + + // The reflection layer has the bounds of m_owningLayer->reflectionLayer(), + // but the reflected layer is the bounds of this layer, so we need to position it appropriately. + FloatRect layerBounds = compositedBounds(); + FloatRect reflectionLayerBounds = reflectionBacking->compositedBounds(); + reflectionBacking->graphicsLayer()->setReplicatedLayerPosition(FloatPoint() + (layerBounds.location() - reflectionLayerBounds.location())); } m_graphicsLayer->setContentsRect(contentsBox()); - if (!m_hasDirectlyCompositedContent) - m_graphicsLayer->setDrawsContent(!isSimpleContainerCompositingLayer() && !paintingGoesToWindow()); + m_graphicsLayer->setDrawsContent(containsPaintedContent()); } void RenderLayerBacking::updateInternalHierarchy() @@ -311,12 +387,12 @@ void RenderLayerBacking::updateInternalHierarchy() if (m_ancestorClippingLayer) { m_ancestorClippingLayer->removeAllChildren(); m_graphicsLayer->removeFromParent(); - m_ancestorClippingLayer->addChild(m_graphicsLayer); + m_ancestorClippingLayer->addChild(m_graphicsLayer.get()); } if (m_clippingLayer) { m_clippingLayer->removeFromParent(); - m_graphicsLayer->addChild(m_clippingLayer); + m_graphicsLayer->addChild(m_clippingLayer.get()); } } @@ -327,7 +403,7 @@ bool RenderLayerBacking::updateClippingLayers(bool needsAncestorClip, bool needs if (needsAncestorClip) { if (!m_ancestorClippingLayer) { - m_ancestorClippingLayer = GraphicsLayer::createGraphicsLayer(this); + m_ancestorClippingLayer = GraphicsLayer::create(this); #ifndef NDEBUG m_ancestorClippingLayer->setName("Ancestor clipping Layer"); #endif @@ -336,14 +412,13 @@ bool RenderLayerBacking::updateClippingLayers(bool needsAncestorClip, bool needs } } 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); + m_clippingLayer = GraphicsLayer::create(0); #ifndef NDEBUG m_clippingLayer->setName("Child clipping Layer"); #endif @@ -352,7 +427,6 @@ bool RenderLayerBacking::updateClippingLayers(bool needsAncestorClip, bool needs } } else if (m_clippingLayer) { m_clippingLayer->removeFromParent(); - delete m_clippingLayer; m_clippingLayer = 0; layersChanged = true; } @@ -368,25 +442,61 @@ bool RenderLayerBacking::updateForegroundLayer(bool needsForegroundLayer) bool layerChanged = false; if (needsForegroundLayer) { if (!m_foregroundLayer) { - m_foregroundLayer = GraphicsLayer::createGraphicsLayer(this); + m_foregroundLayer = GraphicsLayer::create(this); #ifndef NDEBUG - m_foregroundLayer->setName("Contents"); + m_foregroundLayer->setName("Foreground"); #endif m_foregroundLayer->setDrawsContent(true); - m_foregroundLayer->setDrawingPhase(GraphicsLayerPaintForegroundMask); - m_graphicsLayer->setDrawingPhase(GraphicsLayerPaintBackgroundMask); + m_foregroundLayer->setPaintingPhase(GraphicsLayerPaintForeground); layerChanged = true; } } else if (m_foregroundLayer) { m_foregroundLayer->removeFromParent(); - delete m_foregroundLayer; m_foregroundLayer = 0; - m_graphicsLayer->setDrawingPhase(GraphicsLayerPaintAllMask); layerChanged = true; } + + if (layerChanged) + m_graphicsLayer->setPaintingPhase(paintingPhaseForPrimaryLayer()); + + return layerChanged; +} + +bool RenderLayerBacking::updateMaskLayer(bool needsMaskLayer) +{ + bool layerChanged = false; + if (needsMaskLayer) { + if (!m_maskLayer) { + m_maskLayer = GraphicsLayer::create(this); +#ifndef NDEBUG + m_maskLayer->setName("Mask"); +#endif + m_maskLayer->setDrawsContent(true); + m_maskLayer->setPaintingPhase(GraphicsLayerPaintMask); + layerChanged = true; + } + } else if (m_maskLayer) { + m_maskLayer = 0; + layerChanged = true; + } + + if (layerChanged) + m_graphicsLayer->setPaintingPhase(paintingPhaseForPrimaryLayer()); + return layerChanged; } +GraphicsLayerPaintingPhase RenderLayerBacking::paintingPhaseForPrimaryLayer() const +{ + unsigned phase = GraphicsLayerPaintBackground; + if (!m_foregroundLayer) + phase |= GraphicsLayerPaintForeground; + if (!m_maskLayer) + phase |= GraphicsLayerPaintMask; + + return static_cast<GraphicsLayerPaintingPhase>(phase); +} + float RenderLayerBacking::compositingOpacity(float rendererOpacity) const { float finalOpacity = rendererOpacity; @@ -413,12 +523,12 @@ static bool hasBorderOutlineOrShadow(const RenderStyle* style) return style->hasBorder() || style->hasBorderRadius() || style->hasOutline() || style->hasAppearance() || style->boxShadow(); } -static bool hasBoxDecorations(const RenderStyle* style) +static bool hasBoxDecorationsOrBackground(const RenderStyle* style) { return hasBorderOutlineOrShadow(style) || style->hasBackground(); } -static bool hasBoxDecorationsWithBackgroundImage(const RenderStyle* style) +static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle* style) { return hasBorderOutlineOrShadow(style) || style->hasBackgroundImage(); } @@ -478,7 +588,7 @@ bool RenderLayerBacking::isSimpleContainerCompositingLayer() const // Reject anything that has a border, a border-radius or outline, // or any background (color or image). // FIXME: we could optimize layers for simple backgrounds. - if (hasBoxDecorations(style)) + if (hasBoxDecorationsOrBackground(style)) return false; // If we have got this far and the renderer has no children, then we're ok. @@ -495,18 +605,18 @@ bool RenderLayerBacking::isSimpleContainerCompositingLayer() const // Reject anything that has a border, a border-radius or outline, // or is not a simple background (no background, or solid color). - if (hasBoxDecorationsWithBackgroundImage(style)) + if (hasBoxDecorationsOrBackgroundImage(style)) return false; // Now look at the body's renderer. HTMLElement* body = renderObject->document()->body(); - RenderObject* bodyObject = (body && body->hasLocalName(HTMLNames::bodyTag)) ? body->renderer() : 0; + RenderObject* bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; if (!bodyObject) return false; style = bodyObject->style(); - if (hasBoxDecorationsWithBackgroundImage(style)) + if (hasBoxDecorationsOrBackgroundImage(style)) return false; // Ceck to see if all the body's children are compositing layers. @@ -523,9 +633,11 @@ bool RenderLayerBacking::isSimpleContainerCompositingLayer() const return true; } +// Conservative test for having no rendered children. bool RenderLayerBacking::hasNonCompositingContent() const { - // Conservative test for having no rendered children. + if (m_owningLayer->hasOverflowControls()) + return true; // Some HTML can cause whitespace text nodes to have renderers, like: // <div> @@ -542,7 +654,6 @@ bool RenderLayerBacking::hasNonCompositingContent() const } } - // FIXME: test for overflow controls. if (m_owningLayer->isStackingContext()) { // Use the m_hasCompositingDescendant bit to optimize? if (Vector<RenderLayer*>* negZOrderList = m_owningLayer->negZOrderList()) { @@ -576,32 +687,43 @@ bool RenderLayerBacking::hasNonCompositingContent() const return false; } -// A layer can use direct compositing if the render layer's object is a replaced object and has no children. -// This allows the GraphicsLayer to display the RenderLayer contents directly; it's used for images. -bool RenderLayerBacking::canUseDirectCompositing() const +bool RenderLayerBacking::containsPaintedContent() const { - RenderObject* renderObject = renderer(); - - // Reject anything that isn't an image - if (!renderObject->isImage() && !renderObject->isVideo()) + if (isSimpleContainerCompositingLayer() || paintingGoesToWindow() || m_artificiallyInflatedBounds || m_owningLayer->isReflection()) return false; - - if (renderObject->hasMask() || renderObject->hasReflection()) + + if (isDirectlyCompositedImage()) return false; - // Video can use an inner layer even if it has box decorations; we draw those into another layer. - if (renderObject->isVideo()) - return true; - - // Reject anything that would require the image to be drawn via the GraphicsContext, - // like border, shadows etc. Solid background color is OK. - return !hasBoxDecorationsWithBackgroundImage(renderObject->style()); + // FIXME: we could optimize cases where the image, video or canvas is known to fill the border box entirely, + // and set background color on the layer in that case, instead of allocating backing store and painting. + if (renderer()->isVideo() || is3DCanvas(renderer())) + return hasBoxDecorationsOrBackground(renderer()->style()); + + return true; } - + +// An image can be directly compositing if it's the sole content of the layer, and has no box decorations +// that require painting. Direct compositing saves backing store. +bool RenderLayerBacking::isDirectlyCompositedImage() const +{ + RenderObject* renderObject = renderer(); + return renderObject->isImage() && !hasBoxDecorationsOrBackground(renderObject->style()); +} + void RenderLayerBacking::rendererContentChanged() { - if (canUseDirectCompositing() && renderer()->isImage()) + if (isDirectlyCompositedImage()) { updateImageContents(); + return; + } + +#if ENABLE(3D_CANVAS) + if (is3DCanvas(renderer())) { + m_graphicsLayer->setGraphicsContext3DNeedsDisplay(); + return; + } +#endif } void RenderLayerBacking::updateImageContents() @@ -705,13 +827,16 @@ void RenderLayerBacking::setContentsNeedDisplay() if (m_foregroundLayer && m_foregroundLayer->drawsContent()) m_foregroundLayer->setNeedsDisplay(); + + if (m_maskLayer && m_maskLayer->drawsContent()) + m_maskLayer->setNeedsDisplay(); } // r is in the coordinate space of the layer's render object void RenderLayerBacking::setContentsNeedDisplayInRect(const IntRect& r) { if (m_graphicsLayer && m_graphicsLayer->drawsContent()) { - FloatPoint dirtyOrigin = contentsToGraphicsLayerCoordinates(m_graphicsLayer, FloatPoint(r.x(), r.y())); + FloatPoint dirtyOrigin = contentsToGraphicsLayerCoordinates(m_graphicsLayer.get(), FloatPoint(r.x(), r.y())); FloatRect dirtyRect(dirtyOrigin, r.size()); FloatRect bounds(FloatPoint(), m_graphicsLayer->size()); if (bounds.intersects(dirtyRect)) @@ -722,6 +847,11 @@ void RenderLayerBacking::setContentsNeedDisplayInRect(const IntRect& r) // FIXME: do incremental repaint m_foregroundLayer->setNeedsDisplay(); } + + if (m_maskLayer && m_maskLayer->drawsContent()) { + // FIXME: do incremental repaint + m_maskLayer->setNeedsDisplay(); + } } static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) @@ -742,7 +872,7 @@ static void restoreClip(GraphicsContext* p, const IntRect& paintDirtyRect, const // 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 - PaintRestriction paintRestriction, GraphicsLayerPaintingPhase paintingPhase, + PaintBehavior paintBehavior, GraphicsLayerPaintingPhase paintingPhase, RenderObject* paintingRoot) { if (paintingGoesToWindow()) { @@ -752,14 +882,6 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* m_owningLayer->updateLayerListsIfNeeded(); - // Paint the reflection first if we have one. - if (m_owningLayer->hasReflection()) { - // Mark that we are now inside replica painting. - m_owningLayer->setPaintingInsideReflection(true); - m_owningLayer->reflectionLayer()->paintLayer(rootLayer, context, paintDirtyRect, paintRestriction, paintingRoot, 0, RenderLayer::PaintLayerPaintingReflection); - m_owningLayer->setPaintingInsideReflection(false); - } - // Calculate the clip rects we should use. IntRect layerBounds, damageRect, clipRectToApply, outlineRect; m_owningLayer->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect); @@ -777,9 +899,9 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* if (paintingRoot && !renderer()->isDescendantOf(paintingRoot)) paintingRootForRenderer = paintingRoot; - bool shouldPaint = m_owningLayer->hasVisibleContent() && m_owningLayer->isSelfPaintingLayer(); + bool shouldPaint = (m_owningLayer->hasVisibleContent() || m_owningLayer->hasVisibleDescendant()) && m_owningLayer->isSelfPaintingLayer(); - if (shouldPaint && (paintingPhase & GraphicsLayerPaintBackgroundMask)) { + if (shouldPaint && (paintingPhase & GraphicsLayerPaintBackground)) { // 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; @@ -792,9 +914,9 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* int rw; int rh; - if (box->view()->frameView()) { - rw = box->view()->frameView()->contentsWidth(); - rh = box->view()->frameView()->contentsHeight(); + if (FrameView* frameView = box->view()->frameView()) { + rw = frameView->contentsWidth(); + rh = frameView->contentsHeight(); } else { rw = box->view()->width(); rh = box->view()->height(); @@ -823,18 +945,18 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* restoreClip(context, paintDirtyRect, damageRect); } - if (shouldPaint && (paintingPhase & GraphicsLayerPaintForegroundMask)) { + bool forceBlackText = paintBehavior & PaintBehaviorForceBlackText; + bool selectionOnly = paintBehavior & PaintBehaviorSelectionOnly; + + if (shouldPaint && (paintingPhase & GraphicsLayerPaintForeground)) { // 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, paintRestriction, paintingRoot); + it[0]->paintLayer(rootLayer, context, paintDirtyRect, paintBehavior, 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, @@ -868,16 +990,18 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* 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, paintRestriction, paintingRoot); + it[0]->paintLayer(rootLayer, context, paintDirtyRect, paintBehavior, 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, paintRestriction, paintingRoot); + it[0]->paintLayer(rootLayer, context, paintDirtyRect, paintBehavior, paintingRoot); } - + } + + if (shouldPaint && (paintingPhase & GraphicsLayerPaintMask)) { if (renderer()->hasMask() && !selectionOnly && !damageRect.isEmpty()) { setClip(context, paintDirtyRect, damageRect); @@ -893,9 +1017,27 @@ void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* ASSERT(!m_owningLayer->m_usedTransparency); } +#if ENABLE(INSPECTOR) +static InspectorTimelineAgent* inspectorTimelineAgent(RenderObject* renderer) +{ + Frame* frame = renderer->document()->frame(); + if (!frame) + return 0; + Page* page = frame->page(); + if (!page) + return 0; + return page->inspectorTimelineAgent(); +} +#endif + // Up-call from compositing layer drawing callback. -void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase drawingPhase, const IntRect& clip) +void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase paintingPhase, const IntRect& clip) { +#if ENABLE(INSPECTOR) + if (InspectorTimelineAgent* timelineAgent = inspectorTimelineAgent(m_owningLayer->renderer())) + timelineAgent->willPaint(clip); +#endif + // We have to use the same root as for hit testing, because both methods // can compute and cache clipRects. IntRect enclosingBBox = compositedBounds(); @@ -912,10 +1054,25 @@ void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& co IntRect dirtyRect = enclosingBBox; dirtyRect.intersect(clipRect); - paintIntoLayer(m_owningLayer, &context, dirtyRect, PaintRestrictionNone, drawingPhase, renderer()); + paintIntoLayer(m_owningLayer, &context, dirtyRect, PaintBehaviorNormal, paintingPhase, renderer()); + +#if ENABLE(INSPECTOR) + if (InspectorTimelineAgent* timelineAgent = inspectorTimelineAgent(m_owningLayer->renderer())) + timelineAgent->didPaint(); +#endif } -bool RenderLayerBacking::startAnimation(double beginTime, const Animation* anim, const KeyframeList& keyframes) +bool RenderLayerBacking::showDebugBorders() const +{ + return compositor() ? compositor()->showDebugBorders() : false; +} + +bool RenderLayerBacking::showRepaintCounter() const +{ + return compositor() ? compositor()->showRepaintCounter() : false; +} + +bool RenderLayerBacking::startAnimation(double timeOffset, const Animation* anim, const KeyframeList& keyframes) { bool hasOpacity = keyframes.containsProperty(CSSPropertyOpacity); bool hasTransform = keyframes.containsProperty(CSSPropertyWebkitTransform); @@ -946,10 +1103,10 @@ bool RenderLayerBacking::startAnimation(double beginTime, const Animation* anim, bool didAnimateTransform = !hasTransform; bool didAnimateOpacity = !hasOpacity; - if (hasTransform && m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), anim, keyframes.animationName(), beginTime)) + if (hasTransform && m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), anim, keyframes.animationName(), timeOffset)) didAnimateTransform = true; - if (hasOpacity && m_graphicsLayer->addAnimation(opacityVector, IntSize(), anim, keyframes.animationName(), beginTime)) + if (hasOpacity && m_graphicsLayer->addAnimation(opacityVector, IntSize(), anim, keyframes.animationName(), timeOffset)) didAnimateOpacity = true; bool runningAcceleratedAnimation = didAnimateTransform && didAnimateOpacity; @@ -959,7 +1116,7 @@ bool RenderLayerBacking::startAnimation(double beginTime, const Animation* anim, return runningAcceleratedAnimation; } -bool RenderLayerBacking::startTransition(double beginTime, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle) +bool RenderLayerBacking::startTransition(double timeOffset, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle) { bool didAnimate = false; ASSERT(property != cAnimateAll); @@ -970,8 +1127,12 @@ bool RenderLayerBacking::startTransition(double beginTime, int property, const R KeyframeValueList opacityVector(AnimatedPropertyOpacity); opacityVector.insert(new FloatAnimationValue(0, compositingOpacity(fromStyle->opacity()))); opacityVector.insert(new FloatAnimationValue(1, compositingOpacity(toStyle->opacity()))); - if (m_graphicsLayer->addAnimation(opacityVector, toRenderBox(renderer())->borderBoxRect().size(), opacityAnim, String(), beginTime)) + // The boxSize param is only used for transform animations (which can only run on RenderBoxes), so we pass an empty size here. + if (m_graphicsLayer->addAnimation(opacityVector, IntSize(), opacityAnim, String(), timeOffset)) { + // To ensure that the correct opacity is visible when the animation ends, also set the final opacity. + updateLayerOpacity(toStyle); didAnimate = true; + } } } @@ -981,8 +1142,11 @@ bool RenderLayerBacking::startTransition(double beginTime, int property, const R KeyframeValueList transformVector(AnimatedPropertyWebkitTransform); transformVector.insert(new TransformAnimationValue(0, &fromStyle->transform())); transformVector.insert(new TransformAnimationValue(1, &toStyle->transform())); - if (m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), transformAnim, String(), beginTime)) + if (m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), transformAnim, String(), timeOffset)) { + // To ensure that the correct transform is visible when the animation ends, also set the final opacity. + updateLayerTransform(toStyle); didAnimate = true; + } } } @@ -1008,9 +1172,9 @@ void RenderLayerBacking::animationFinished(const String& animationName) m_graphicsLayer->removeAnimationsForKeyframes(animationName); } -void RenderLayerBacking::animationPaused(const String& animationName) +void RenderLayerBacking::animationPaused(double timeOffset, const String& animationName) { - m_graphicsLayer->pauseAnimation(animationName); + m_graphicsLayer->pauseAnimation(animationName, timeOffset); } void RenderLayerBacking::transitionFinished(int property) diff --git a/WebCore/rendering/RenderLayerBacking.h b/WebCore/rendering/RenderLayerBacking.h index b027685..7aea926 100644 --- a/WebCore/rendering/RenderLayerBacking.h +++ b/WebCore/rendering/RenderLayerBacking.h @@ -54,7 +54,7 @@ public: RenderLayer* owningLayer() const { return m_owningLayer; } enum UpdateDepth { CompositingChildren, AllDescendants }; - void updateAfterLayout(UpdateDepth); + void updateAfterLayout(UpdateDepth, bool isUpdateRoot); // Returns true if layer configuration changed. bool updateGraphicsLayerConfiguration(); @@ -63,21 +63,23 @@ public: // Update contents and clipping structure. void updateInternalHierarchy(); // make private - GraphicsLayer* graphicsLayer() const { return m_graphicsLayer; } + GraphicsLayer* graphicsLayer() const { return m_graphicsLayer.get(); } // Layer to clip children bool hasClippingLayer() const { return m_clippingLayer != 0; } - GraphicsLayer* clippingLayer() const { return m_clippingLayer; } + GraphicsLayer* clippingLayer() const { return m_clippingLayer.get(); } // Layer to get clipped by ancestor bool hasAncestorClippingLayer() const { return m_ancestorClippingLayer != 0; } - GraphicsLayer* ancestorClippingLayer() const { return m_ancestorClippingLayer; } + GraphicsLayer* ancestorClippingLayer() const { return m_ancestorClippingLayer.get(); } bool hasContentsLayer() const { return m_foregroundLayer != 0; } - GraphicsLayer* foregroundLayer() const { return m_foregroundLayer; } + GraphicsLayer* foregroundLayer() const { return m_foregroundLayer.get(); } - GraphicsLayer* parentForSublayers() const { return m_clippingLayer ? m_clippingLayer : m_graphicsLayer; } - GraphicsLayer* childForSuperlayers() const { return m_ancestorClippingLayer ? m_ancestorClippingLayer : m_graphicsLayer; } + bool hasMaskLayer() const { return m_maskLayer != 0; } + + GraphicsLayer* parentForSublayers() const { return m_clippingLayer ? m_clippingLayer.get() : m_graphicsLayer.get(); } + GraphicsLayer* childForSuperlayers() const { return m_ancestorClippingLayer ? m_ancestorClippingLayer.get() : m_graphicsLayer.get(); } // RenderLayers with backing normally short-circuit paintLayer() because // their content is rendered via callbacks from GraphicsLayer. However, the document @@ -95,10 +97,10 @@ public: void rendererContentChanged(); // Interface to start, finish, suspend and resume animations and transitions - bool startAnimation(double beginTime, const Animation* anim, const KeyframeList& keyframes); - bool startTransition(double beginTime, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle); + bool startAnimation(double timeOffset, const Animation* anim, const KeyframeList& keyframes); + bool startTransition(double timeOffset, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle); void animationFinished(const String& name); - void animationPaused(const String& name); + void animationPaused(double timeOffset, const String& name); void transitionFinished(int property); void suspendAnimations(double time = 0); @@ -106,6 +108,7 @@ public: IntRect compositedBounds() const; void setCompositedBounds(const IntRect&); + void updateCompositedBounds(); FloatPoint graphicsLayerToContentsCoordinates(const GraphicsLayer*, const FloatPoint&); FloatPoint contentsToGraphicsLayerCoordinates(const GraphicsLayer*, const FloatPoint&); @@ -116,6 +119,9 @@ public: virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& clip); + virtual bool showDebugBorders() const; + virtual bool showRepaintCounter() const; + IntRect contentsBox() const; private: @@ -127,25 +133,28 @@ private: bool updateClippingLayers(bool needsAncestorClip, bool needsDescendantClip); bool updateForegroundLayer(bool needsForegroundLayer); + bool updateMaskLayer(bool needsMaskLayer); + GraphicsLayerPaintingPhase paintingPhaseForPrimaryLayer() const; + IntSize contentOffsetInCompostingLayer() const; // 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(); + void updateLayerOpacity(const RenderStyle*); + void updateLayerTransform(const RenderStyle*); // Return the opacity value that this layer should use for compositing. float compositingOpacity(float rendererOpacity) const; - // Returns true if this RenderLayer only has content that can be rendered directly - // by the compositing layer, without drawing (e.g. solid background color). + // Returns true if this compositing layer has no visible content. bool isSimpleContainerCompositingLayer() const; - // Returns true if we can optimize the RenderLayer to draw the replaced content - // directly into a compositing buffer - bool canUseDirectCompositing() const; + // Returns true if this layer has content that needs to be rendered by painting into the backing store. + bool containsPaintedContent() const; + // Returns true if the RenderLayer just contains an image that we can composite directly. + bool isDirectlyCompositedImage() const; void updateImageContents(); bool rendererHasBackground() const; @@ -154,7 +163,7 @@ private: bool hasNonCompositingContent() const; void paintIntoLayer(RenderLayer* rootLayer, GraphicsContext*, const IntRect& paintDirtyRect, - PaintRestriction paintRestriction, GraphicsLayerPaintingPhase, RenderObject* paintingRoot); + PaintBehavior paintBehavior, GraphicsLayerPaintingPhase, RenderObject* paintingRoot); static int graphicsLayerToCSSProperty(AnimatedPropertyID); static AnimatedPropertyID cssToGraphicsLayerProperty(int); @@ -162,14 +171,15 @@ private: 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_foregroundLayer; // 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 + OwnPtr<GraphicsLayer> m_ancestorClippingLayer; // only used if we are clipped by an ancestor which is not a stacking context + OwnPtr<GraphicsLayer> m_graphicsLayer; + OwnPtr<GraphicsLayer> m_foregroundLayer; // only used in cases where we need to draw the foreground separately + OwnPtr<GraphicsLayer> m_clippingLayer; // only used if we have clipping on a stacking context, with compositing children + OwnPtr<GraphicsLayer> m_maskLayer; // only used if we have a mask IntRect m_compositedBounds; - bool m_hasDirectlyCompositedContent; + bool m_artificiallyInflatedBounds; // bounds had to be made non-zero to make transform-origin work }; } // namespace WebCore diff --git a/WebCore/rendering/RenderLayerCompositor.cpp b/WebCore/rendering/RenderLayerCompositor.cpp index fb15800..22118fe 100644 --- a/WebCore/rendering/RenderLayerCompositor.cpp +++ b/WebCore/rendering/RenderLayerCompositor.cpp @@ -29,15 +29,18 @@ #include "RenderLayerCompositor.h" #include "AnimationController.h" +#include "Chrome.h" #include "ChromeClient.h" #include "CSSPropertyNames.h" #include "Frame.h" #include "FrameView.h" #include "GraphicsLayer.h" -#include "HitTestRequest.h" #include "HitTestResult.h" +#include "HTMLCanvasElement.h" #include "Page.h" +#include "RenderEmbeddedObject.h" #include "RenderLayerBacking.h" +#include "RenderReplica.h" #include "RenderVideo.h" #include "RenderView.h" #include "Settings.h" @@ -58,10 +61,15 @@ bool WebCoreHas3DRendering = true; namespace WebCore { +using namespace HTMLNames; + struct CompositingState { CompositingState(RenderLayer* compAncestor) : m_compositingAncestor(compAncestor) , m_subtreeIsCompositing(false) +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + , m_fixedSibling(false) +#endif #ifndef NDEBUG , m_depth(0) #endif @@ -70,6 +78,9 @@ struct CompositingState { RenderLayer* m_compositingAncestor; bool m_subtreeIsCompositing; +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + bool m_fixedSibling; +#endif #ifndef NDEBUG int m_depth; #endif @@ -79,6 +90,8 @@ RenderLayerCompositor::RenderLayerCompositor(RenderView* renderView) : m_renderView(renderView) , m_rootPlatformLayer(0) , m_hasAcceleratedCompositing(true) + , m_showDebugBorders(false) + , m_showRepaintCounter(false) , m_compositingConsultsOverlap(true) , m_compositing(false) , m_rootLayerAttached(false) @@ -92,7 +105,6 @@ RenderLayerCompositor::RenderLayerCompositor(RenderView* renderView) RenderLayerCompositor::~RenderLayerCompositor() { ASSERT(!m_rootLayerAttached); - delete m_rootPlatformLayer; } void RenderLayerCompositor::enableCompositingMode(bool enable /* = true */) @@ -110,16 +122,24 @@ void RenderLayerCompositor::enableCompositingMode(bool enable /* = true */) } } -void RenderLayerCompositor::cacheAcceleratedCompositingEnabledFlag() +void RenderLayerCompositor::cacheAcceleratedCompositingFlags() { bool hasAcceleratedCompositing = false; - if (Settings* settings = m_renderView->document()->settings()) + bool showDebugBorders = false; + bool showRepaintCounter = false; + + if (Settings* settings = m_renderView->document()->settings()) { hasAcceleratedCompositing = settings->acceleratedCompositingEnabled(); + showDebugBorders = settings->showDebugBorders(); + showRepaintCounter = settings->showRepaintCounter(); + } - if (hasAcceleratedCompositing != m_hasAcceleratedCompositing) + if (hasAcceleratedCompositing != m_hasAcceleratedCompositing || showDebugBorders != m_showDebugBorders || showRepaintCounter != m_showRepaintCounter) setCompositingLayersNeedRebuild(); m_hasAcceleratedCompositing = hasAcceleratedCompositing; + m_showDebugBorders = showDebugBorders; + m_showRepaintCounter = showRepaintCounter; } void RenderLayerCompositor::setCompositingLayersNeedRebuild(bool needRebuild) @@ -138,16 +158,30 @@ void RenderLayerCompositor::scheduleSync() page->chrome()->client()->scheduleCompositingLayerSync(); } -void RenderLayerCompositor::updateCompositingLayers(RenderLayer* updateRoot) +void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType updateType, RenderLayer* updateRoot) { - // When m_compositingConsultsOverlap is true, then layer positions affect compositing, - // so we can only bail here when we're not looking at overlap. - if (!m_compositingLayersNeedRebuild && !m_compositingConsultsOverlap) + bool checkForHierarchyUpdate = false; + bool needGeometryUpdate = false; + + switch (updateType) { + case CompositingUpdateAfterLayoutOrStyleChange: + case CompositingUpdateOnPaitingOrHitTest: + checkForHierarchyUpdate = true; + break; + case CompositingUpdateOnScroll: + if (m_compositingConsultsOverlap) + checkForHierarchyUpdate = true; // Overlap can change with scrolling, so need to check for hierarchy updates. + + needGeometryUpdate = true; + break; + } + + if (!checkForHierarchyUpdate && !needGeometryUpdate) return; ASSERT(inCompositingMode()); - bool needLayerRebuild = m_compositingLayersNeedRebuild; + bool needHierarchyUpdate = m_compositingLayersNeedRebuild; if (!updateRoot) { // Only clear the flag if we're updating the entire hierarchy. m_compositingLayersNeedRebuild = false; @@ -160,26 +194,33 @@ void RenderLayerCompositor::updateCompositingLayers(RenderLayer* updateRoot) double startTime = WTF::currentTime(); #endif - // Go through the layers in presentation order, so that we can compute which - // RLs need compositing layers. - // FIXME: we could maybe do this in one pass, but the parenting logic would be more - // complex. - { + if (checkForHierarchyUpdate) { + // Go through the layers in presentation order, so that we can compute which RenderLayers need compositing layers. + // FIXME: we could maybe do this and the hierarchy udpate in one pass, but the parenting logic would be more complex. CompositingState compState(updateRoot); - bool layersChanged; + bool layersChanged = false; if (m_compositingConsultsOverlap) { OverlapMap overlapTestRequestMap; computeCompositingRequirements(updateRoot, &overlapTestRequestMap, compState, layersChanged); } else computeCompositingRequirements(updateRoot, 0, compState, layersChanged); - needLayerRebuild |= layersChanged; + needHierarchyUpdate |= layersChanged; } - // Now create and parent the compositing layers. - { + if (needHierarchyUpdate) { + // Update the hierarchy of the compositing layers. CompositingState compState(updateRoot); - rebuildCompositingLayerTree(updateRoot, compState, needLayerRebuild); + Vector<GraphicsLayer*> childList; + rebuildCompositingLayerTree(updateRoot, compState, childList); + + // Host the document layer in the RenderView's root layer. + if (updateRoot == rootRenderLayer() && !childList.isEmpty()) + m_rootPlatformLayer->setChildren(childList); + } else if (needGeometryUpdate) { + // We just need to do a geometry update. This is only used for position:fixed scrolling; + // most of the time, geometry is updated via RenderLayer::styleChanged(). + updateLayerTreeGeometry(updateRoot); } #if PROFILE_LAYER_REBUILD @@ -216,6 +257,17 @@ bool RenderLayerCompositor::updateBacking(RenderLayer* layer, CompositingChangeR } } else { if (layer->backing()) { + // If we're removing backing on a reflection, clear the source GraphicsLayer's pointer to + // its replica GraphicsLayer. In practice this should never happen because reflectee and reflection + // are both either composited, or not composited. + if (layer->isReflection()) { + RenderLayer* sourceLayer = toRenderBoxModelObject(layer->renderer()->parent())->layer(); + if (RenderLayerBacking* backing = sourceLayer->backing()) { + ASSERT(backing->graphicsLayer()->replicaLayer() == layer->backing()->graphicsLayer()); + backing->graphicsLayer()->setReplicatedByLayer(0); + } + } + layer->clearBacking(); layerChanged = true; @@ -287,6 +339,13 @@ IntRect RenderLayerCompositor::calculateCompositedBounds(const RenderLayer* laye return boundingBoxRect; } + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (!reflection->isComposited()) { + IntRect childUnionBounds = calculateCompositedBounds(reflection, layer); + unionBounds.unite(childUnionBounds); + } + } + ASSERT(layer->isStackingContext() || (!layer->m_posZOrderList || layer->m_posZOrderList->size() == 0)); if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { @@ -322,7 +381,7 @@ IntRect RenderLayerCompositor::calculateCompositedBounds(const RenderLayer* laye } } - if (layer->paintsWithTransform()) { + if (layer->paintsWithTransform(PaintBehaviorNormal)) { TransformationMatrix* affineTrans = layer->transform(); boundingBoxRect = affineTrans->mapRect(boundingBoxRect); unionBounds = affineTrans->mapRect(unionBounds); @@ -425,11 +484,11 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O bool haveComputedBounds = false; IntRect absBounds; - if (overlapMap && mustOverlapCompositedLayers) { + if (overlapMap && !overlapMap->isEmpty()) { // If we're testing for overlap, we only need to composite if we overlap something that is already composited. absBounds = layer->renderer()->localToAbsoluteQuad(FloatRect(layer->localBoundingBox())).enclosingBoundingBox(); haveComputedBounds = true; - mustOverlapCompositedLayers &= overlapsCompositedLayers(*overlapMap, absBounds); + mustOverlapCompositedLayers = overlapsCompositedLayers(*overlapMap, absBounds); } layer->setMustOverlapCompositedLayers(mustOverlapCompositedLayers); @@ -443,7 +502,19 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O #endif const bool willBeComposited = needsToBeComposited(layer); + +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + // If we are a fixed layer, signal it to our siblings + if (layer->isFixed()) + compositingState.m_fixedSibling = true; + + if (!willBeComposited && compositingState.m_fixedSibling) + layer->setMustOverlapCompositedLayers(true); + + if (willBeComposited || compositingState.m_fixedSibling) { +#else if (willBeComposited) { +#endif // Tell the parent it has compositing descendants. compositingState.m_subtreeIsCompositing = true; // This layer now acts as the ancestor for kids. @@ -464,13 +535,32 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O ASSERT(!layer->m_zOrderListsDirty); if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + childState.m_fixedSibling = false; + + // For the negative z-order, if we have a fixed layer + // we need to make all the siblings composited layers. + // Otherwise a negative layer (below the fixed layer) could + // still be drawn onto a higher z-order layer (e.g. the body) + // if not immediately intersecting with our fixed layer. + // So it's not enough here to only set m_fixedSibling for + // subsequent siblings as we do for the normal flow + // and positive z-order. + for (size_t j = 0; j < listSize; ++j) { + if ((negZOrderList->at(j))->isFixed()) { + childState.m_fixedSibling = true; + break; + } + } +#endif + for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); computeCompositingRequirements(curLayer, overlapMap, childState, layersChanged); // 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) { + if (!willBeComposited && childState.m_subtreeIsCompositing) { // make layer compositing layer->setMustOverlapCompositedLayers(true); childState.m_compositingAncestor = layer; @@ -484,6 +574,9 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O ASSERT(!layer->m_normalFlowListDirty); if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + childState.m_fixedSibling = false; +#endif for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); computeCompositingRequirements(curLayer, overlapMap, childState, layersChanged); @@ -493,6 +586,9 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O if (layer->isStackingContext()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + childState.m_fixedSibling = false; +#endif for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); computeCompositingRequirements(curLayer, overlapMap, childState, layersChanged); @@ -503,13 +599,15 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O // 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)) { + if (!willBeComposited && childState.m_subtreeIsCompositing && requiresCompositingWhenDescendantsAreCompositing(layer->renderer())) { layer->setMustOverlapCompositedLayers(true); if (overlapMap) addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); } + if (layer->reflectionLayer()) + layer->reflectionLayer()->setMustOverlapCompositedLayers(needsToBeComposited(layer)); + // Subsequent layers in the parent stacking context also need to composite. if (childState.m_subtreeIsCompositing) compositingState.m_subtreeIsCompositing = true; @@ -526,10 +624,14 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, O // Update backing now, so that we can use isComposited() reliably during tree traversal in rebuildCompositingLayerTree(). if (updateBacking(layer, CompositingChangeRepaintNow)) layersChanged = true; + + if (layer->reflectionLayer() && updateLayerCompositingState(layer->reflectionLayer(), CompositingChangeRepaintNow)) + layersChanged = true; } void RenderLayerCompositor::setCompositingParent(RenderLayer* childLayer, RenderLayer* parentLayer) { + ASSERT(!parentLayer || childLayer->ancestorCompositingLayer() == parentLayer); ASSERT(childLayer->isComposited()); // It's possible to be called with a parent that isn't yet composited when we're doing @@ -571,40 +673,41 @@ void RenderLayerCompositor::parentInRootLayer(RenderLayer* layer) #if ENABLE(VIDEO) bool RenderLayerCompositor::canAccelerateVideoRendering(RenderVideo* o) const { - // FIXME: ideally we need to look at all ancestors for mask or video. But for now, - // just bail on the obvious cases. - if (o->hasMask() || o->hasReflection() || !m_hasAcceleratedCompositing) + if (!m_hasAcceleratedCompositing) return false; return o->supportsAcceleratedRendering(); } #endif -void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, struct CompositingState& compositingState, bool updateHierarchy) +void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, const CompositingState& compositingState, Vector<GraphicsLayer*>& childLayersOfEnclosingLayer) { // Make the layer compositing if necessary, and set up clipping and content layers. // Note that we can only do work here that is independent of whether the descendant layers // have been processed. computeCompositingRequirements() will already have done the repaint if necessary. + RenderLayerBacking* layerBacking = layer->backing(); if (layerBacking) { // The compositing state of all our children has been updated already, so now // we can compute and cache the composited bounds for this layer. - layerBacking->setCompositedBounds(calculateCompositedBounds(layer, layer)); + layerBacking->updateCompositedBounds(); + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (reflection->backing()) + reflection->backing()->updateCompositedBounds(); + } layerBacking->updateGraphicsLayerConfiguration(); layerBacking->updateGraphicsLayerGeometry(); if (!layer->parent()) updateRootLayerPosition(); - - // FIXME: make this more incremental - if (updateHierarchy) - layerBacking->parentForSublayers()->removeAllChildren(); } - // host the document layer in the RenderView's root layer - if (updateHierarchy && layer->isRootLayer() && layer->isComposited()) - parentInRootLayer(layer); + // If this layer has backing, then we are collecting its children, otherwise appending + // to the compositing child list of an enclosing layer. + Vector<GraphicsLayer*> layerChildren; + Vector<GraphicsLayer*>& childList = layerBacking ? layerChildren : childLayersOfEnclosingLayer; CompositingState childState = compositingState; if (layer->isComposited()) @@ -625,19 +728,13 @@ void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, stru size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); - rebuildCompositingLayerTree(curLayer, childState, updateHierarchy); - if (updateHierarchy && curLayer->isComposited()) - setCompositingParent(curLayer, childState.m_compositingAncestor); + rebuildCompositingLayerTree(curLayer, childState, childList); } } - if (updateHierarchy && layerBacking && layerBacking->foregroundLayer()) { - // we only have a contents layer if we have an m_layer - layerBacking->foregroundLayer()->removeFromParent(); - - GraphicsLayer* hostingLayer = layerBacking->clippingLayer() ? layerBacking->clippingLayer() : layerBacking->graphicsLayer(); - hostingLayer->addChild(layerBacking->foregroundLayer()); - } + // If a negative z-order child is compositing, we get a foreground layer which needs to get parented. + if (layerBacking && layerBacking->foregroundLayer()) + childList.append(layerBacking->foregroundLayer()); } ASSERT(!layer->m_normalFlowListDirty); @@ -645,38 +742,93 @@ void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, stru size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); - rebuildCompositingLayerTree(curLayer, childState, updateHierarchy); - if (updateHierarchy && curLayer->isComposited()) - setCompositingParent(curLayer, childState.m_compositingAncestor); + rebuildCompositingLayerTree(curLayer, childState, childList); } } - + if (layer->isStackingContext()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); - rebuildCompositingLayerTree(curLayer, childState, updateHierarchy); - if (updateHierarchy && curLayer->isComposited()) - setCompositingParent(curLayer, childState.m_compositingAncestor); + rebuildCompositingLayerTree(curLayer, childState, childList); } } } + + if (layerBacking) { + layerBacking->parentForSublayers()->setChildren(layerChildren); + childLayersOfEnclosingLayer.append(layerBacking->childForSuperlayers()); + } } +// This just updates layer geometry without changing the hierarchy. +void RenderLayerCompositor::updateLayerTreeGeometry(RenderLayer* layer) +{ + if (RenderLayerBacking* layerBacking = layer->backing()) { + // The compositing state of all our children has been updated already, so now + // we can compute and cache the composited bounds for this layer. + layerBacking->updateCompositedBounds(); + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (reflection->backing()) + reflection->backing()->updateCompositedBounds(); + } + + layerBacking->updateGraphicsLayerConfiguration(); + layerBacking->updateGraphicsLayerGeometry(); + + if (!layer->parent()) + updateRootLayerPosition(); + } + + if (layer->isStackingContext()) { + ASSERT(!layer->m_zOrderListsDirty); + + if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) + updateLayerTreeGeometry(negZOrderList->at(i)); + } + } + + ASSERT(!layer->m_normalFlowListDirty); + if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + for (size_t i = 0; i < listSize; ++i) + updateLayerTreeGeometry(normalFlowList->at(i)); + } + + if (layer->isStackingContext()) { + if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) + updateLayerTreeGeometry(posZOrderList->at(i)); + } + } +} // Recurs down the RenderLayer tree until its finds the compositing descendants of compositingAncestor and updates their geometry. void RenderLayerCompositor::updateCompositingDescendantGeometry(RenderLayer* compositingAncestor, RenderLayer* layer, RenderLayerBacking::UpdateDepth updateDepth) { if (layer != compositingAncestor) { if (RenderLayerBacking* layerBacking = layer->backing()) { - layerBacking->setCompositedBounds(calculateCompositedBounds(layer, layer)); + layerBacking->updateCompositedBounds(); + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (reflection->backing()) + reflection->backing()->updateCompositedBounds(); + } + layerBacking->updateGraphicsLayerGeometry(); if (updateDepth == RenderLayerBacking::CompositingChildren) return; } } + if (layer->reflectionLayer()) + updateCompositingDescendantGeometry(compositingAncestor, layer->reflectionLayer(), updateDepth); + if (!layer->hasCompositingDescendant()) return; @@ -711,6 +863,7 @@ void RenderLayerCompositor::repaintCompositedLayersAbsoluteRect(const IntRect& a void RenderLayerCompositor::recursiveRepaintLayerRect(RenderLayer* layer, const IntRect& rect) { + // FIXME: This method does not work correctly with transforms. if (layer->isComposited()) layer->setBackingNeedsRepaintInRect(rect); @@ -719,7 +872,8 @@ void RenderLayerCompositor::recursiveRepaintLayerRect(RenderLayer* layer, const size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); - int x = 0, y = 0; + int x = 0; + int y = 0; curLayer->convertToLayerCoords(layer, x, y); IntRect childRect(rect); childRect.move(-x, -y); @@ -731,7 +885,8 @@ void RenderLayerCompositor::recursiveRepaintLayerRect(RenderLayer* layer, const size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); - int x = 0, y = 0; + int x = 0; + int y = 0; curLayer->convertToLayerCoords(layer, x, y); IntRect childRect(rect); childRect.move(-x, -y); @@ -743,7 +898,8 @@ void RenderLayerCompositor::recursiveRepaintLayerRect(RenderLayer* layer, const size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); - int x = 0, y = 0; + int x = 0; + int y = 0; curLayer->convertToLayerCoords(layer, x, y); IntRect childRect(rect); childRect.move(-x, -y); @@ -759,7 +915,7 @@ RenderLayer* RenderLayerCompositor::rootRenderLayer() const GraphicsLayer* RenderLayerCompositor::rootPlatformLayer() const { - return m_rootPlatformLayer; + return m_rootPlatformLayer.get(); } void RenderLayerCompositor::didMoveOnscreen() @@ -772,7 +928,7 @@ void RenderLayerCompositor::didMoveOnscreen() if (!page) return; - page->chrome()->client()->attachRootGraphicsLayer(frame, m_rootPlatformLayer); + page->chrome()->client()->attachRootGraphicsLayer(frame, m_rootPlatformLayer.get()); m_rootLayerAttached = true; } @@ -793,7 +949,7 @@ void RenderLayerCompositor::willMoveOffscreen() void RenderLayerCompositor::updateRootLayerPosition() { if (m_rootPlatformLayer) - m_rootPlatformLayer->setSize(FloatSize(m_renderView->overflowWidth(), m_renderView->overflowHeight())); + m_rootPlatformLayer->setSize(FloatSize(m_renderView->rightLayoutOverflow(), m_renderView->bottomLayoutOverflow())); } void RenderLayerCompositor::didStartAcceleratedAnimation() @@ -814,21 +970,80 @@ bool RenderLayerCompositor::needsToBeComposited(const RenderLayer* layer) const if (!m_hasAcceleratedCompositing || !layer->isSelfPaintingLayer()) return false; +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + // if an ancestor is fixed positioned, we need to be composited... + const RenderLayer* currLayer = layer; + while ((currLayer = currLayer->parent())) { + if (currLayer->isComposited() && currLayer->isFixed()) + return true; + } +#endif + return requiresCompositingLayer(layer) || layer->mustOverlapCompositedLayers(); } +#if PLATFORM(ANDROID) +bool RenderLayerCompositor::requiresCompositingForMobileSites(const RenderLayer* layer) const +{ + // First, check if we are in an iframe, and if so bail out + if (m_renderView->document()->frame()->tree()->parent()) + return false; + + RenderObject* renderer = layer->renderer(); + // Check for transforms + if (requiresCompositingForTransform(renderer)) + return true; + + // Check for animations + if (requiresCompositingForAnimation(renderer)) + return true; + +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + // For the moment, we want to only enable fixed composited layers on mobile websites. + // We can consider a website as being a 'mobile' site if all the + // following checks are true: + // 1) - the viewport width is either undefined (-1) or equal to device-width (0), and + // 2) - no scaling is allowed + if (!layer->isFixed()) + return false; + + Settings* settings = m_renderView->document()->settings(); + if (!settings) + return false; + + if ((settings->viewportWidth() == -1 || settings->viewportWidth() == 0) && + !settings->viewportUserScalable()) + return true; +#endif + + return false; +} +#endif + // 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 { + RenderObject* renderer = layer->renderer(); + // The compositing state of a reflection should match that of its reflected layer. + if (layer->isReflection()) { + renderer = renderer->parent(); // The RenderReplica's parent is the object being reflected. + layer = toRenderBoxModelObject(renderer)->layer(); + } // The root layer always has a compositing layer, but it may not have backing. return (inCompositingMode() && layer->isRootLayer()) || - requiresCompositingForTransform(layer->renderer()) || - requiresCompositingForVideo(layer->renderer()) || - layer->renderer()->style()->backfaceVisibility() == BackfaceVisibilityHidden || - clipsCompositingDescendants(layer) || - requiresCompositingForAnimation(layer->renderer()); +#if PLATFORM(ANDROID) + requiresCompositingForMobileSites(layer) || +#else + requiresCompositingForTransform(renderer) || + requiresCompositingForVideo(renderer) || + requiresCompositingForCanvas(renderer) || + requiresCompositingForPlugin(renderer) || + requiresCompositingForAnimation(renderer) || +#endif + renderer->style()->backfaceVisibility() == BackfaceVisibilityHidden || + clipsCompositingDescendants(layer); } // Return true if the given layer has some ancestor in the RenderLayer hierarchy that clips, @@ -862,10 +1077,8 @@ bool RenderLayerCompositor::clippedByAncestor(RenderLayer* layer) const if (!computeClipRoot || computeClipRoot == layer) return false; - ClipRects parentRects; - layer->parentClipRects(computeClipRoot, parentRects, true); - - return parentRects.overflowClipRect() != ClipRects::infiniteRect(); + IntRect backgroundRect = layer->backgroundClipRect(computeClipRoot, true); + return backgroundRect != ClipRects::infiniteRect(); } // Return true if the given layer is a stacking context and has compositing child @@ -893,10 +1106,30 @@ bool RenderLayerCompositor::requiresCompositingForVideo(RenderObject* renderer) RenderVideo* video = toRenderVideo(renderer); return canAccelerateVideoRendering(video); } +#else + UNUSED_PARAM(renderer); +#endif + return false; +} + +bool RenderLayerCompositor::requiresCompositingForCanvas(RenderObject* renderer) const +{ +#if ENABLE(3D_CANVAS) + if (renderer->isCanvas()) { + HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer->node()); + return canvas->is3D(); + } +#else + UNUSED_PARAM(renderer); #endif return false; } +bool RenderLayerCompositor::requiresCompositingForPlugin(RenderObject* renderer) const +{ + return renderer->isEmbeddedObject() && toRenderEmbeddedObject(renderer)->allowsAcceleratedCompositing(); +} + bool RenderLayerCompositor::requiresCompositingForAnimation(RenderObject* renderer) const { if (AnimationController* animController = renderer->animation()) { @@ -906,6 +1139,11 @@ bool RenderLayerCompositor::requiresCompositingForAnimation(RenderObject* render return false; } +bool RenderLayerCompositor::requiresCompositingWhenDescendantsAreCompositing(RenderObject* renderer) const +{ + return renderer->hasTransform() || renderer->isTransparent() || renderer->hasMask() || renderer->hasReflection(); +} + // 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. @@ -919,8 +1157,8 @@ void RenderLayerCompositor::ensureRootPlatformLayer() if (m_rootPlatformLayer) return; - m_rootPlatformLayer = GraphicsLayer::createGraphicsLayer(0); - m_rootPlatformLayer->setSize(FloatSize(m_renderView->overflowWidth(), m_renderView->overflowHeight())); + m_rootPlatformLayer = GraphicsLayer::create(0); + m_rootPlatformLayer->setSize(FloatSize(m_renderView->rightLayoutOverflow(), m_renderView->bottomLayoutOverflow())); m_rootPlatformLayer->setPosition(FloatPoint(0, 0)); // The root layer does flipping if we need it on this platform. m_rootPlatformLayer->setGeometryOrientation(GraphicsLayer::compositingCoordinatesOrientation()); @@ -937,7 +1175,6 @@ void RenderLayerCompositor::destroyRootPlatformLayer() return; willMoveOffscreen(); - delete m_rootPlatformLayer; m_rootPlatformLayer = 0; } @@ -985,4 +1222,3 @@ bool RenderLayerCompositor::layerHas3DContent(const RenderLayer* layer) const } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) - diff --git a/WebCore/rendering/RenderLayerCompositor.h b/WebCore/rendering/RenderLayerCompositor.h index 02929dc..eeacdf7 100644 --- a/WebCore/rendering/RenderLayerCompositor.h +++ b/WebCore/rendering/RenderLayerCompositor.h @@ -38,6 +38,12 @@ class GraphicsLayer; class RenderVideo; #endif +enum CompositingUpdateType { + CompositingUpdateAfterLayoutOrStyleChange, + CompositingUpdateOnPaitingOrHitTest, + CompositingUpdateOnScroll +}; + // RenderLayerCompositor manages the hierarchy of // composited RenderLayers. It determines which RenderLayers // become compositing, and creates and maintains a hierarchy of @@ -47,7 +53,6 @@ class RenderVideo; class RenderLayerCompositor { public: - RenderLayerCompositor(RenderView*); ~RenderLayerCompositor(); @@ -61,8 +66,11 @@ public: // Returns true if the accelerated compositing is enabled bool hasAcceleratedCompositing() const { return m_hasAcceleratedCompositing; } - // Copy the acceleratedCompositingEnabledFlag from Settings - void cacheAcceleratedCompositingEnabledFlag(); + bool showDebugBorders() const { return m_showDebugBorders; } + bool showRepaintCounter() const { return m_showRepaintCounter; } + + // Copy the accelerated compositing related flags from Settings + void cacheAcceleratedCompositingFlags(); // Called when the layer hierarchy needs to be updated (compositing layers have been // created, destroyed or re-parented). @@ -77,7 +85,7 @@ public: void scheduleSync(); // Rebuild the tree of compositing layers - void updateCompositingLayers(RenderLayer* updateRoot = 0); + void updateCompositingLayers(CompositingUpdateType = CompositingUpdateAfterLayoutOrStyleChange, RenderLayer* updateRoot = 0); // Update the compositing state of the given layer. Returns true if that state changed. enum CompositingChangeRepaint { CompositingChangeRepaintNow, CompositingChangeWillRepaintLater }; @@ -147,8 +155,13 @@ private: // Returns true if any layer's compositing changed void computeCompositingRequirements(RenderLayer*, OverlapMap*, struct CompositingState&, bool& layersChanged); - void rebuildCompositingLayerTree(RenderLayer* layer, struct CompositingState&, bool updateHierarchy); + + // Recurses down the tree, parenting descendant compositing layers and collecting an array of child layers for the current compositing layer. + void rebuildCompositingLayerTree(RenderLayer* layer, const struct CompositingState&, Vector<GraphicsLayer*>& childGraphicsLayersOfEnclosingLayer); + // Recurses down the tree, updating layer geometry only. + void updateLayerTreeGeometry(RenderLayer*); + // Hook compositing layers together void setCompositingParent(RenderLayer* childLayer, RenderLayer* parentLayer); void removeCompositedChildren(RenderLayer*); @@ -164,11 +177,21 @@ private: bool requiresCompositingForAnimation(RenderObject*) const; bool requiresCompositingForTransform(RenderObject*) const; bool requiresCompositingForVideo(RenderObject*) const; + bool requiresCompositingForCanvas(RenderObject*) const; + bool requiresCompositingForPlugin(RenderObject*) const; + bool requiresCompositingWhenDescendantsAreCompositing(RenderObject*) const; + +#if PLATFORM(ANDROID) + // Whether we are on a mobile site + bool requiresCompositingForMobileSites(const RenderLayer* layer) const; +#endif private: RenderView* m_renderView; - GraphicsLayer* m_rootPlatformLayer; + OwnPtr<GraphicsLayer> m_rootPlatformLayer; bool m_hasAcceleratedCompositing; + bool m_showDebugBorders; + bool m_showRepaintCounter; bool m_compositingConsultsOverlap; bool m_compositing; bool m_rootLayerAttached; diff --git a/WebCore/rendering/RenderLineBoxList.cpp b/WebCore/rendering/RenderLineBoxList.cpp index 00566b8..57bc26c 100644 --- a/WebCore/rendering/RenderLineBoxList.cpp +++ b/WebCore/rendering/RenderLineBoxList.cpp @@ -162,8 +162,8 @@ void RenderLineBoxList::paint(RenderBoxModelObject* renderer, RenderObject::Pain // 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; + int yPos = firstLineBox()->topVisibleOverflow() - renderer->maximalOutlineSize(paintInfo.phase); + int h = renderer->maximalOutlineSize(paintInfo.phase) + lastLineBox()->bottomVisibleOverflow() - yPos; yPos += ty; if (yPos >= paintInfo.rect.bottom() || yPos + h <= paintInfo.rect.y()) return; @@ -184,21 +184,22 @@ void RenderLineBoxList::paint(RenderBoxModelObject* renderer, RenderObject::Pain // 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 (curr->bottomVisibleOverflow() - curr->topVisibleOverflow() <= v->printRect().height()) { + if (ty + curr->bottomVisibleOverflow() > v->printRect().bottom()) { + if (ty + curr->topVisibleOverflow() < v->truncatedAt()) + v->setBestTruncatedAt(ty + curr->root()->topVisibleOverflow(), renderer); // If we were able to truncate, don't paint. - if (ty + curr->root()->topOverflow() >= v->truncatedAt()) + if (ty + curr->topVisibleOverflow() >= 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); + int top = min(curr->topVisibleOverflow(), curr->root()->selectionTop()) - renderer->maximalOutlineSize(info.phase); + int bottom = curr->bottomVisibleOverflow() + renderer->maximalOutlineSize(info.phase); h = bottom - top; yPos = ty + top; + v->setMinimumColumnHeight(h); if (yPos < info.rect.bottom() && yPos + h > info.rect.y()) curr->paint(info, tx, ty); } @@ -229,14 +230,14 @@ bool RenderLineBoxList::hitTest(RenderBoxModelObject* renderer, const HitTestReq // 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())) + if ((y >= ty + lastLineBox()->root()->bottomVisibleOverflow()) || (y < ty + firstLineBox()->root()->topVisibleOverflow())) 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()) { + if (y >= ty + curr->root()->topVisibleOverflow() && y < ty + curr->root()->bottomVisibleOverflow()) { bool inside = curr->nodeAtPoint(request, result, x, y, tx, ty); if (inside) { renderer->updateHitTestResult(result, IntPoint(x - tx, y - ty)); diff --git a/WebCore/rendering/RenderListBox.cpp b/WebCore/rendering/RenderListBox.cpp index e6c28f7..15c652c 100644 --- a/WebCore/rendering/RenderListBox.cpp +++ b/WebCore/rendering/RenderListBox.cpp @@ -321,7 +321,8 @@ void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, in textColor = theme()->inactiveListBoxSelectionForegroundColor(); } - paintInfo.context->setFillColor(textColor); + ColorSpace colorSpace = itemStyle->colorSpace(); + paintInfo.context->setFillColor(textColor, colorSpace); Font itemFont = style()->font(); if (isOptionGroupElement(element)) { @@ -358,9 +359,10 @@ void RenderListBox::paintItemBackground(PaintInfo& paintInfo, int tx, int ty, in // Draw the background for this list box item if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) { + ColorSpace colorSpace = element->renderStyle() ? element->renderStyle()->colorSpace() : style()->colorSpace(); IntRect itemRect = itemBoundingBoxRect(tx, ty, listIndex); itemRect.intersect(controlClipRect(tx, ty)); - paintInfo.context->fillRect(itemRect, backColor); + paintInfo.context->fillRect(itemRect, backColor, colorSpace); } } @@ -508,7 +510,7 @@ bool RenderListBox::listIndexIsVisible(int index) return index >= m_indexOffset && index < m_indexOffset + numVisibleItems(); } -bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) +bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node**) { return m_vBar && m_vBar->scroll(direction, granularity, multiplier); } @@ -527,8 +529,7 @@ void RenderListBox::valueChanged(Scrollbar*) if (newOffset != m_indexOffset) { m_indexOffset = newOffset; repaint(); - // Fire the scroll DOM event. - node()->dispatchEvent(eventNames().scrollEvent, false, false); + node()->dispatchEvent(Event::create(eventNames().scrollEvent, false, false)); } } diff --git a/WebCore/rendering/RenderListBox.h b/WebCore/rendering/RenderListBox.h index e5454e5..aafb87e 100644 --- a/WebCore/rendering/RenderListBox.h +++ b/WebCore/rendering/RenderListBox.h @@ -68,7 +68,7 @@ private: virtual bool isPointInOverflowControl(HitTestResult&, int x, int y, int tx, int ty); - virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); + virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f, Node** stopNode = 0); virtual void calcPrefWidths(); virtual int baselinePosition(bool firstLine, bool isRootLineBox) const; diff --git a/WebCore/rendering/RenderListItem.cpp b/WebCore/rendering/RenderListItem.cpp index 374ef66..54a7dd2 100644 --- a/WebCore/rendering/RenderListItem.cpp +++ b/WebCore/rendering/RenderListItem.cpp @@ -1,9 +1,7 @@ /** - * 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) - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) * * This library is free software; you can redistribute it and/or @@ -53,8 +51,8 @@ void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* old { RenderBlock::styleDidChange(diff, oldStyle); - if (style()->listStyleType() != LNONE || - (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) { + if (style()->listStyleType() != NoneListStyle + || (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) { RefPtr<RenderStyle> newStyle = RenderStyle::create(); // The marker always inherits from the list item, regardless of where it might end // up (e.g., in some deeply nested line box). See CSS3 spec. @@ -77,11 +75,16 @@ void RenderListItem::destroy() RenderBlock::destroy(); } +static bool isList(Node* node) +{ + return (node->hasTagName(ulTag) || node->hasTagName(olTag)); +} + static Node* enclosingList(Node* node) { Node* parent = node->parentNode(); for (Node* n = parent; n; n = n->parentNode()) - if (n->hasTagName(ulTag) || n->hasTagName(olTag)) + if (isList(n)) return n; // If there's no actual <ul> or <ol> list element, then our parent acts as // our list for purposes of determining what other list items should be @@ -89,22 +92,38 @@ static Node* enclosingList(Node* node) return parent; } +static Node* enclosingList(const RenderObject* renderer) +{ + Node* node = renderer->node(); + if (node) + return enclosingList(node); + + renderer = renderer->parent(); + while (renderer && !renderer->node()) + renderer = renderer->parent(); + + node = renderer->node(); + if (isList(node)) + return node; + + return enclosingList(node); +} + static RenderListItem* previousListItem(Node* list, const RenderListItem* item) { - for (Node* node = item->node()->traversePreviousNode(); node != list; node = node->traversePreviousNode()) { - RenderObject* renderer = node->renderer(); - if (!renderer || !renderer->isListItem()) + for (RenderObject* renderer = item->previousInPreOrder(); renderer != list->renderer(); renderer = renderer->previousInPreOrder()) { + if (!renderer->isListItem()) continue; - Node* otherList = enclosingList(node); + Node* otherList = enclosingList(renderer); // This item is part of our current list, so it's what we're looking for. if (list == otherList) return toRenderListItem(renderer); // We found ourself inside another list; lets skip the rest of it. - // Use traverseNextNode() here because the other list itself may actually + // Use nextInPreOrder() here because the other list itself may actually // be a list item itself. We need to examine it, so we do this to counteract - // the traversePreviousNode() that will be done by the loop. + // the previousInPreOrder() that will be done by the loop. if (otherList) - node = otherList->traverseNextNode(); + renderer = otherList->renderer()->nextInPreOrder(); } return 0; } @@ -113,7 +132,7 @@ inline int RenderListItem::calcValue() const { if (m_hasExplicitValue) return m_explicitValue; - Node* list = enclosingList(node()); + Node* list = enclosingList(this); // FIXME: This recurses to a possible depth of the length of the list. // That's not good -- we need to change this to an iterative algorithm. if (RenderListItem* previousItem = previousListItem(list, this)) @@ -247,21 +266,30 @@ void RenderListItem::positionListMarker() int markerXPos; RootInlineBox* root = m_marker->inlineBoxWrapper()->root(); + // FIXME: Inline flows in the line box hierarchy that have self-painting layers should act as cutoff points + // and really shouldn't keep propagating overflow up. This won't really break anything other than repainting + // not being as tight as it could be though. if (style()->direction() == LTR) { 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()) { - root->setHorizontalOverflowPositions(markerXPos, root->rightOverflow()); - adjustOverflow = true; + for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) { + if (markerXPos < box->leftLayoutOverflow()) { + box->setHorizontalOverflowPositions(markerXPos, box->rightLayoutOverflow(), box->leftVisualOverflow(), box->rightVisualOverflow()); + if (box == root) + adjustOverflow = true; + } } } else { 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()) { - root->setHorizontalOverflowPositions(root->leftOverflow(), markerXPos + m_marker->width()); - adjustOverflow = true; + for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) { + if (markerXPos + m_marker->width() > box->rightLayoutOverflow()) { + box->setHorizontalOverflowPositions(box->leftLayoutOverflow(), markerXPos + m_marker->width(), box->leftVisualOverflow(), box->rightVisualOverflow()); + if (box == root) + adjustOverflow = true; + } } } @@ -271,9 +299,9 @@ void RenderListItem::positionListMarker() do { o = o->parentBox(); if (o->isRenderBlock()) - toRenderBlock(o)->addVisualOverflow(markerRect); + toRenderBlock(o)->addLayoutOverflow(markerRect); markerRect.move(-o->x(), -o->y()); - } while (o != this); + } while (o != this && !o->hasSelfPaintingLayer()); } } } @@ -315,6 +343,8 @@ void RenderListItem::explicitValueChanged() void RenderListItem::setExplicitValue(int value) { + ASSERT(node()); + if (m_hasExplicitValue && m_explicitValue == value) return; m_explicitValue = value; @@ -325,6 +355,8 @@ void RenderListItem::setExplicitValue(int value) void RenderListItem::clearExplicitValue() { + ASSERT(node()); + if (!m_hasExplicitValue) return; m_hasExplicitValue = false; diff --git a/WebCore/rendering/RenderListItem.h b/WebCore/rendering/RenderListItem.h index 21544f9..c4c41dc 100644 --- a/WebCore/rendering/RenderListItem.h +++ b/WebCore/rendering/RenderListItem.h @@ -44,6 +44,8 @@ public: void setNotInList(bool notInList) { m_notInList = notInList; } bool notInList() const { return m_notInList; } + const String& markerText() const; + private: virtual const char* renderName() const { return "RenderListItem"; } @@ -59,8 +61,6 @@ private: virtual void positionListMarker(); - const String& markerText() const; - virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); void updateMarkerLocation(); diff --git a/WebCore/rendering/RenderListMarker.cpp b/WebCore/rendering/RenderListMarker.cpp index 9627711..d0353ee 100644 --- a/WebCore/rendering/RenderListMarker.cpp +++ b/WebCore/rendering/RenderListMarker.cpp @@ -3,6 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * Copyright (C) 2010 Daniel Bates (dbates@intudata.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -40,6 +41,8 @@ namespace WebCore { const int cMarkerPadding = 7; +enum SequenceType { NumericSequence, AlphabeticSequence }; + static String toRoman(int number, bool upper) { // FIXME: CSS3 describes how to make this work for much larger numbers, @@ -48,7 +51,9 @@ static String toRoman(int number, bool upper) if (number < 1 || number > 3999) return String::number(number); - const int lettersSize = 12; // big enough for three each of I, X, C, and M + // Big enough to store largest roman number less than 3999 which + // is 3888 (MMMDCCCLXXXVIII) + const int lettersSize = 15; UChar letters[lettersSize]; int length = 0; @@ -75,26 +80,63 @@ static String toRoman(int number, bool upper) return String(&letters[lettersSize - length], length); } -static String toAlphabetic(int number, const UChar* alphabet, int alphabetSize) +static inline String toAlphabeticOrNumeric(int number, const UChar* sequence, int sequenceSize, SequenceType type) { - ASSERT(alphabetSize >= 10); + ASSERT(sequenceSize >= 2); - if (number < 1) - return String::number(number); + const int lettersSize = sizeof(number) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign. - const int lettersSize = 10; // big enough for a 32-bit int, with a 10-letter alphabet UChar letters[lettersSize]; - --number; - letters[lettersSize - 1] = alphabet[number % alphabetSize]; + bool isNegativeNumber = false; + unsigned numberShadow = number; + if (type == AlphabeticSequence) { + ASSERT(number > 0); + --numberShadow; + } else if (number < 0) { + numberShadow = -number; + isNegativeNumber = true; + } + letters[lettersSize - 1] = sequence[numberShadow % sequenceSize]; int length = 1; - while ((number /= alphabetSize) > 0) - letters[lettersSize - ++length] = alphabet[number % alphabetSize - 1]; + + if (type == AlphabeticSequence) { + while ((numberShadow /= sequenceSize) > 0) + letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize - 1]; + } else { + while ((numberShadow /= sequenceSize) > 0) + letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize]; + } + if (isNegativeNumber) + letters[lettersSize - ++length] = hyphenMinus; ASSERT(length <= lettersSize); return String(&letters[lettersSize - length], length); } +static String toAlphabetic(int number, const UChar* alphabet, int alphabetSize) +{ + if (number < 1) + return String::number(number); + + return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence); +} + +static String toNumeric(int number, const UChar* numerals, int numeralsSize) +{ + return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence); +} + +template <size_t size> static inline String toAlphabetic(int number, const UChar(&alphabet)[size]) +{ + return toAlphabetic(number, alphabet, size); +} + +template <size_t size> static inline String toNumeric(int number, const UChar(&alphabet)[size]) +{ + return toNumeric(number, alphabet, size); +} + static int toHebrewUnder1000(int number, UChar letters[5]) { // FIXME: CSS3 mentions various refinements not implemented here. @@ -335,58 +377,273 @@ static String toCJKIdeographic(int number, const UChar table[16]) return String(characters, length); } +static UChar listMarkerSuffix(EListStyleType type) +{ + // Note, the following switch statement has been explicitly + // grouped by list-style-type suffix. + switch (type) { + case NoneListStyle: + case Disc: + case Circle: + case Square: + ASSERT_NOT_REACHED(); + return ' '; + case Afar: + case Amharic: + case AmharicAbegede: + case Ethiopic: + case EthiopicAbegede: + case EthiopicAbegedeAmEt: + case EthiopicAbegedeGez: + case EthiopicAbegedeTiEr: + case EthiopicAbegedeTiEt: + case EthiopicHalehameAaEr: + case EthiopicHalehameAaEt: + case EthiopicHalehameAmEt: + case EthiopicHalehameGez: + case EthiopicHalehameOmEt: + case EthiopicHalehameSidEt: + case EthiopicHalehameSoEt: + case EthiopicHalehameTiEr: + case EthiopicHalehameTiEt: + case EthiopicHalehameTig: + case Oromo: + case Sidama: + case Somali: + case Tigre: + case TigrinyaEr: + case TigrinyaErAbegede: + case TigrinyaEt: + case TigrinyaEtAbegede: + return ethiopicPrefaceColon; + case Armenian: + case ArabicIndic: + case Bengali: + case BinaryListStyle: + case Cambodian: + case CJKIdeographic: + case CjkEarthlyBranch: + case CjkHeavenlyStem: + case DecimalLeadingZero: + case DecimalListStyle: + case Devanagari: + case Georgian: + case Gujarati: + case Gurmukhi: + case Hangul: + case HangulConsonant: + case Hebrew: + case Hiragana: + case HiraganaIroha: + case Kannada: + case Katakana: + case KatakanaIroha: + case Khmer: + case Lao: + case LowerAlpha: + case LowerGreek: + case LowerHexadecimal: + case LowerLatin: + case LowerNorwegian: + case LowerRoman: + case Malayalam: + case Mongolian: + case Myanmar: + case Octal: + case Oriya: + case Persian: + case Telugu: + case Thai: + case Tibetan: + case UpperAlpha: + case UpperGreek: + case UpperHexadecimal: + case UpperLatin: + case UpperNorwegian: + case UpperRoman: + case Urdu: + return '.'; + } + + ASSERT_NOT_REACHED(); + return '.'; +} + String listMarkerText(EListStyleType type, int value) { switch (type) { - case LNONE: + case NoneListStyle: return ""; // We use the same characters for text security. // See RenderText::setInternalString. - case CIRCLE: + case Circle: return String(&whiteBullet, 1); - case DISC: + case Disc: return String(&bullet, 1); - case SQUARE: + case Square: // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE // instead, but I think this looks better. return String(&blackSquare, 1); - case LDECIMAL: + case DecimalListStyle: return String::number(value); - case DECIMAL_LEADING_ZERO: + case DecimalLeadingZero: if (value < -9 || value > 9) return String::number(value); if (value < 0) return "-0" + String::number(-value); // -01 to -09 return "0" + String::number(value); // 00 to 09 - case LOWER_ALPHA: - case LOWER_LATIN: { + case ArabicIndic: { + static const UChar arabicIndicNumerals[10] = { + 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669 + }; + return toNumeric(value, arabicIndicNumerals); + } + case BinaryListStyle: { + static const UChar binaryNumerals[2] = { + '0', '1' + }; + return toNumeric(value, binaryNumerals); + } + case Bengali: { + static const UChar bengaliNumerals[10] = { + 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF + }; + return toNumeric(value, bengaliNumerals); + } + case Cambodian: + case Khmer: { + static const UChar khmerNumerals[10] = { + 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9 + }; + return toNumeric(value, khmerNumerals); + } + case Devanagari: { + static const UChar devanagariNumerals[10] = { + 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F + }; + return toNumeric(value, devanagariNumerals); + } + case Gujarati: { + static const UChar gujaratiNumerals[10] = { + 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF + }; + return toNumeric(value, gujaratiNumerals); + } + case Gurmukhi: { + static const UChar gurmukhiNumerals[10] = { + 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F + }; + return toNumeric(value, gurmukhiNumerals); + } + case Kannada: { + static const UChar kannadaNumerals[10] = { + 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF + }; + return toNumeric(value, kannadaNumerals); + } + case LowerHexadecimal: { + static const UChar lowerHexadecimalNumerals[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + return toNumeric(value, lowerHexadecimalNumerals); + } + case Lao: { + static const UChar laoNumerals[10] = { + 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9 + }; + return toNumeric(value, laoNumerals); + } + case Malayalam: { + static const UChar malayalamNumerals[10] = { + 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F + }; + return toNumeric(value, malayalamNumerals); + } + case Mongolian: { + static const UChar mongolianNumerals[10] = { + 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819 + }; + return toNumeric(value, mongolianNumerals); + } + case Myanmar: { + static const UChar myanmarNumerals[10] = { + 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049 + }; + return toNumeric(value, myanmarNumerals); + } + case Octal: { + static const UChar octalNumerals[8] = { + '0', '1', '2', '3', '4', '5', '6', '7' + }; + return toNumeric(value, octalNumerals); + } + case Oriya: { + static const UChar oriyaNumerals[10] = { + 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F + }; + return toNumeric(value, oriyaNumerals); + } + case Persian: + case Urdu: { + static const UChar urduNumerals[10] = { + 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9 + }; + return toNumeric(value, urduNumerals); + } + case Telugu: { + static const UChar teluguNumerals[10] = { + 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F + }; + return toNumeric(value, teluguNumerals); + } + case Tibetan: { + static const UChar tibetanNumerals[10] = { + 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29 + }; + return toNumeric(value, tibetanNumerals); + } + case Thai: { + static const UChar thaiNumerals[10] = { + 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59 + }; + return toNumeric(value, thaiNumerals); + } + case UpperHexadecimal: { + static const UChar upperHexadecimalNumerals[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + return toNumeric(value, upperHexadecimalNumerals); + } + + case LowerAlpha: + case LowerLatin: { static const UChar lowerLatinAlphabet[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; - return toAlphabetic(value, lowerLatinAlphabet, 26); + return toAlphabetic(value, lowerLatinAlphabet); } - case UPPER_ALPHA: - case UPPER_LATIN: { + case UpperAlpha: + case UpperLatin: { static const UChar upperLatinAlphabet[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; - return toAlphabetic(value, upperLatinAlphabet, 26); + return toAlphabetic(value, upperLatinAlphabet); } - case LOWER_GREEK: { + case LowerGreek: { static const UChar lowerGreekAlphabet[24] = { 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9 }; - return toAlphabetic(value, lowerGreekAlphabet, 24); + return toAlphabetic(value, lowerGreekAlphabet); } - case HIRAGANA: { + case Hiragana: { // FIXME: This table comes from the CSS3 draft, and is probably // incorrect, given the comments in that draft. static const UChar hiraganaAlphabet[48] = { @@ -397,9 +654,9 @@ String listMarkerText(EListStyleType type, int value) 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093 }; - return toAlphabetic(value, hiraganaAlphabet, 48); + return toAlphabetic(value, hiraganaAlphabet); } - case HIRAGANA_IROHA: { + case HiraganaIroha: { // FIXME: This table comes from the CSS3 draft, and is probably // incorrect, given the comments in that draft. static const UChar hiraganaIrohaAlphabet[47] = { @@ -410,9 +667,9 @@ String listMarkerText(EListStyleType type, int value) 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081, 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059 }; - return toAlphabetic(value, hiraganaIrohaAlphabet, 47); + return toAlphabetic(value, hiraganaIrohaAlphabet); } - case KATAKANA: { + case Katakana: { // FIXME: This table comes from the CSS3 draft, and is probably // incorrect, given the comments in that draft. static const UChar katakanaAlphabet[48] = { @@ -423,9 +680,9 @@ String listMarkerText(EListStyleType type, int value) 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3 }; - return toAlphabetic(value, katakanaAlphabet, 48); + return toAlphabetic(value, katakanaAlphabet); } - case KATAKANA_IROHA: { + case KatakanaIroha: { // FIXME: This table comes from the CSS3 draft, and is probably // incorrect, given the comments in that draft. static const UChar katakanaIrohaAlphabet[47] = { @@ -436,10 +693,187 @@ String listMarkerText(EListStyleType type, int value) 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1, 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9 }; - return toAlphabetic(value, katakanaIrohaAlphabet, 47); + return toAlphabetic(value, katakanaIrohaAlphabet); } - case CJK_IDEOGRAPHIC: { + case Afar: + case EthiopicHalehameAaEt: + case EthiopicHalehameAaEr: { + static const UChar ethiopicHalehameAaErAlphabet[18] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290, + 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348 + }; + return toAlphabetic(value, ethiopicHalehameAaErAlphabet); + } + case Amharic: + case EthiopicHalehameAmEt: { + static const UChar ethiopicHalehameAmEtAlphabet[33] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240, + 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, + 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, + 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameAmEtAlphabet); + } + case AmharicAbegede: + case EthiopicAbegedeAmEt: { + static const UChar ethiopicAbegedeAmEtAlphabet[33] = { + 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0, + 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290, + 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238, + 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350 + }; + return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet); + } + case CjkEarthlyBranch: { + static const UChar cjkEarthlyBranchAlphabet[12] = { + 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533, + 0x9149, 0x620C, 0x4EA5 + }; + return toAlphabetic(value, cjkEarthlyBranchAlphabet); + } + case CjkHeavenlyStem: { + static const UChar cjkHeavenlyStemAlphabet[10] = { + 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC, + 0x7678 + }; + return toAlphabetic(value, cjkHeavenlyStemAlphabet); + } + case Ethiopic: + case EthiopicHalehameGez: { + static const UChar ethiopicHalehameGezAlphabet[26] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260, + 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8, + 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameGezAlphabet); + } + case EthiopicAbegede: + case EthiopicAbegedeGez: { + static const UChar ethiopicAbegedeGezAlphabet[26] = { + 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320, + 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338, + 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350 + }; + return toAlphabetic(value, ethiopicAbegedeGezAlphabet); + } + case HangulConsonant: { + static const UChar hangulConsonantAlphabet[14] = { + 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148, + 0x314A, 0x314B, 0x314C, 0x314D, 0x314E + }; + return toAlphabetic(value, hangulConsonantAlphabet); + } + case Hangul: { + static const UChar hangulAlphabet[14] = { + 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790, + 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558 + }; + return toAlphabetic(value, hangulAlphabet); + } + case Oromo: + case EthiopicHalehameOmEt: { + static const UChar ethiopicHalehameOmEtAlphabet[25] = { + 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270, + 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8, + 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348 + }; + return toAlphabetic(value, ethiopicHalehameOmEtAlphabet); + } + case Sidama: + case EthiopicHalehameSidEt: { + static const UChar ethiopicHalehameSidEtAlphabet[26] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, + 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, + 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348 + }; + return toAlphabetic(value, ethiopicHalehameSidEtAlphabet); + } + case Somali: + case EthiopicHalehameSoEt: { + static const UChar ethiopicHalehameSoEtAlphabet[22] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, + 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, + 0x1300, 0x1308, 0x1338, 0x1348 + }; + return toAlphabetic(value, ethiopicHalehameSoEtAlphabet); + } + case Tigre: + case EthiopicHalehameTig: { + static const UChar ethiopicHalehameTigAlphabet[27] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, + 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8, + 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameTigAlphabet); + } + case TigrinyaEr: + case EthiopicHalehameTiEr: { + static const UChar ethiopicHalehameTiErAlphabet[31] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250, + 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8, + 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, + 0x1330, 0x1338, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameTiErAlphabet); + } + case TigrinyaErAbegede: + case EthiopicAbegedeTiEr: { + static const UChar ethiopicAbegedeTiErAlphabet[31] = { + 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0, + 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290, + 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238, + 0x1270, 0x1278, 0x1330, 0x1350 + }; + return toAlphabetic(value, ethiopicAbegedeTiErAlphabet); + } + case TigrinyaEt: + case EthiopicHalehameTiEt: { + static const UChar ethiopicHalehameTiEtAlphabet[34] = { + 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240, + 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, + 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, + 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350 + }; + return toAlphabetic(value, ethiopicHalehameTiEtAlphabet); + } + case TigrinyaEtAbegede: + case EthiopicAbegedeTiEt: { + static const UChar ethiopicAbegedeTiEtAlphabet[34] = { + 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0, + 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290, + 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, + 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350 + }; + return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet); + } + case UpperGreek: { + static const UChar upperGreekAlphabet[24] = { + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, + 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, + 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9 + }; + return toAlphabetic(value, upperGreekAlphabet); + } + case LowerNorwegian: { + static const UChar lowerNorwegianAlphabet[29] = { + 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, + 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, + 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E6, + 0x00F8, 0x00E5 + }; + return toAlphabetic(value, lowerNorwegianAlphabet); + } + case UpperNorwegian: { + static const UChar upperNorwegianAlphabet[29] = { + 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, + 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, + 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C6, + 0x00D8, 0x00C5 + }; + return toAlphabetic(value, upperNorwegianAlphabet); + } + case CJKIdeographic: { static const UChar traditionalChineseInformalTable[16] = { 0x842C, 0x5104, 0x5146, 0x5341, 0x767E, 0x5343, @@ -449,19 +883,19 @@ String listMarkerText(EListStyleType type, int value) return toCJKIdeographic(value, traditionalChineseInformalTable); } - case LOWER_ROMAN: + case LowerRoman: return toRoman(value, false); - case UPPER_ROMAN: + case UpperRoman: return toRoman(value, true); - case ARMENIAN: + case Armenian: // CSS3 says "armenian" means "lower-armenian". // But the CSS2.1 test suite contains uppercase test results for "armenian", // so we'll match the test suite. return toArmenian(value, true); - case GEORGIAN: + case Georgian: return toGeorgian(value); - case HEBREW: + case Hebrew: return toHebrew(value); } @@ -517,6 +951,15 @@ bool RenderListMarker::isImage() const return m_image && !m_image->errorOccurred(); } +IntRect RenderListMarker::localSelectionRect() +{ + InlineBox* box = inlineBoxWrapper(); + if (!box) + return IntRect(); + RootInlineBox* root = box->root(); + return IntRect(x(), root->selectionTop() - y(), width(), root->selectionHeight()); +} + void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) { if (paintInfo.phase != PaintPhaseForeground) @@ -543,10 +986,11 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) paintCustomHighlight(tx, ty, style()->highlight(), true); #endif - context->drawImage(m_image->image(this, marker.size()), marker.location()); + context->drawImage(m_image->image(this, marker.size()), style()->colorSpace(), marker.location()); if (selectionState() != SelectionNone) { - // FIXME: selectionRect() is in absolute, not painting coordinates. - context->fillRect(selectionRect(), selectionBackgroundColor()); + IntRect selRect = localSelectionRect(); + selRect.move(tx, ty); + context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace()); } return; } @@ -558,46 +1002,104 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) #endif if (selectionState() != SelectionNone) { - // FIXME: selectionRect() is in absolute, not painting coordinates. - context->fillRect(selectionRect(), selectionBackgroundColor()); + IntRect selRect = localSelectionRect(); + selRect.move(tx, ty); + context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace()); } const Color color(style()->color()); - context->setStrokeColor(color); + context->setStrokeColor(color, style()->colorSpace()); context->setStrokeStyle(SolidStroke); context->setStrokeThickness(1.0f); - context->setFillColor(color); + context->setFillColor(color, style()->colorSpace()); - switch (style()->listStyleType()) { - case DISC: + EListStyleType type = style()->listStyleType(); + switch (type) { + case Disc: context->drawEllipse(marker); return; - case CIRCLE: - context->setFillColor(Color::transparent); + case Circle: + context->setFillColor(Color::transparent, DeviceColorSpace); context->drawEllipse(marker); return; - case SQUARE: + case Square: context->drawRect(marker); return; - case LNONE: + case NoneListStyle: return; - case ARMENIAN: - case CJK_IDEOGRAPHIC: - case DECIMAL_LEADING_ZERO: - case GEORGIAN: - case HEBREW: - case HIRAGANA: - case HIRAGANA_IROHA: - case KATAKANA: - case KATAKANA_IROHA: - case LDECIMAL: - case LOWER_ALPHA: - case LOWER_GREEK: - case LOWER_LATIN: - case LOWER_ROMAN: - case UPPER_ALPHA: - case UPPER_LATIN: - case UPPER_ROMAN: + case Afar: + case Amharic: + case AmharicAbegede: + case ArabicIndic: + case Armenian: + case BinaryListStyle: + case Bengali: + case Cambodian: + case CJKIdeographic: + case CjkEarthlyBranch: + case CjkHeavenlyStem: + case DecimalLeadingZero: + case DecimalListStyle: + case Devanagari: + case Ethiopic: + case EthiopicAbegede: + case EthiopicAbegedeAmEt: + case EthiopicAbegedeGez: + case EthiopicAbegedeTiEr: + case EthiopicAbegedeTiEt: + case EthiopicHalehameAaEr: + case EthiopicHalehameAaEt: + case EthiopicHalehameAmEt: + case EthiopicHalehameGez: + case EthiopicHalehameOmEt: + case EthiopicHalehameSidEt: + case EthiopicHalehameSoEt: + case EthiopicHalehameTiEr: + case EthiopicHalehameTiEt: + case EthiopicHalehameTig: + case Georgian: + case Gujarati: + case Gurmukhi: + case Hangul: + case HangulConsonant: + case Hebrew: + case Hiragana: + case HiraganaIroha: + case Kannada: + case Katakana: + case KatakanaIroha: + case Khmer: + case Lao: + case LowerAlpha: + case LowerGreek: + case LowerHexadecimal: + case LowerLatin: + case LowerNorwegian: + case LowerRoman: + case Malayalam: + case Mongolian: + case Myanmar: + case Octal: + case Oriya: + case Oromo: + case Persian: + case Sidama: + case Somali: + case Telugu: + case Thai: + case Tibetan: + case Tigre: + case TigrinyaEr: + case TigrinyaErAbegede: + case TigrinyaEt: + case TigrinyaEtAbegede: + case UpperAlpha: + case UpperGreek: + case UpperHexadecimal: + case UpperLatin: + case UpperNorwegian: + case UpperRoman: + case Urdu: break; } if (m_text.isEmpty()) @@ -618,16 +1120,17 @@ void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) } const Font& font = style()->font(); + const UChar suffix = listMarkerSuffix(type); if (style()->direction() == LTR) { int width = font.width(textRun); context->drawText(style()->font(), textRun, marker.location()); - const UChar periodSpace[2] = { '.', ' ' }; - context->drawText(style()->font(), TextRun(periodSpace, 2), marker.location() + IntSize(width, 0)); + UChar suffixSpace[2] = { suffix, ' ' }; + context->drawText(style()->font(), TextRun(suffixSpace, 2), marker.location() + IntSize(width, 0)); } else { - const UChar spacePeriod[2] = { ' ', '.' }; - TextRun spacePeriodRun(spacePeriod, 2); - int width = font.width(spacePeriodRun); - context->drawText(style()->font(), spacePeriodRun, marker.location()); + UChar spaceSuffix[2] = { ' ', suffix }; + TextRun spaceSuffixRun(spaceSuffix, 2); + int width = font.width(spaceSuffixRun); + context->drawText(style()->font(), spaceSuffixRun, marker.location()); context->drawText(style()->font(), textRun, marker.location() + IntSize(width, 0)); } } @@ -691,39 +1194,95 @@ void RenderListMarker::calcPrefWidths() int width = 0; EListStyleType type = style()->listStyleType(); switch (type) { - case LNONE: + case NoneListStyle: break; - case CIRCLE: - case DISC: - case SQUARE: + case Circle: + case Disc: + case Square: m_text = listMarkerText(type, 0); // value is ignored for these types width = (font.ascent() * 2 / 3 + 1) / 2 + 2; break; - case ARMENIAN: - case CJK_IDEOGRAPHIC: - case DECIMAL_LEADING_ZERO: - case GEORGIAN: - case HEBREW: - case HIRAGANA: - case HIRAGANA_IROHA: - case KATAKANA: - case KATAKANA_IROHA: - case LDECIMAL: - case LOWER_ALPHA: - case LOWER_GREEK: - case LOWER_LATIN: - case LOWER_ROMAN: - case UPPER_ALPHA: - case UPPER_LATIN: - case UPPER_ROMAN: + case Afar: + case Amharic: + case AmharicAbegede: + case ArabicIndic: + case Armenian: + case BinaryListStyle: + case Bengali: + case Cambodian: + case CJKIdeographic: + case CjkEarthlyBranch: + case CjkHeavenlyStem: + case DecimalLeadingZero: + case DecimalListStyle: + case Devanagari: + case Ethiopic: + case EthiopicAbegede: + case EthiopicAbegedeAmEt: + case EthiopicAbegedeGez: + case EthiopicAbegedeTiEr: + case EthiopicAbegedeTiEt: + case EthiopicHalehameAaEr: + case EthiopicHalehameAaEt: + case EthiopicHalehameAmEt: + case EthiopicHalehameGez: + case EthiopicHalehameOmEt: + case EthiopicHalehameSidEt: + case EthiopicHalehameSoEt: + case EthiopicHalehameTiEr: + case EthiopicHalehameTiEt: + case EthiopicHalehameTig: + case Georgian: + case Gujarati: + case Gurmukhi: + case Hangul: + case HangulConsonant: + case Hebrew: + case Hiragana: + case HiraganaIroha: + case Kannada: + case Katakana: + case KatakanaIroha: + case Khmer: + case Lao: + case LowerAlpha: + case LowerGreek: + case LowerHexadecimal: + case LowerLatin: + case LowerNorwegian: + case LowerRoman: + case Malayalam: + case Mongolian: + case Myanmar: + case Octal: + case Oriya: + case Oromo: + case Persian: + case Sidama: + case Somali: + case Telugu: + case Thai: + case Tibetan: + case Tigre: + case TigrinyaEr: + case TigrinyaErAbegede: + case TigrinyaEt: + case TigrinyaEtAbegede: + case UpperAlpha: + case UpperGreek: + case UpperHexadecimal: + case UpperLatin: + case UpperNorwegian: + case UpperRoman: + case Urdu: m_text = listMarkerText(type, m_listItem->value()); if (m_text.isEmpty()) width = 0; else { int itemWidth = font.width(m_text); - const UChar periodSpace[2] = { '.', ' ' }; - int periodSpaceWidth = font.width(TextRun(periodSpace, 2)); - width = itemWidth + periodSpaceWidth; + UChar suffixSpace[2] = { listMarkerSuffix(type), ' ' }; + int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2)); + width = itemWidth + suffixSpaceWidth; } break; } @@ -750,9 +1309,9 @@ void RenderListMarker::updateMargins() else marginLeft = cMarkerPadding; } else switch (style()->listStyleType()) { - case DISC: - case CIRCLE: - case SQUARE: + case Disc: + case Circle: + case Square: if (style()->direction() == LTR) { marginLeft = -1; marginRight = font.ascent() - minPrefWidth() + 1; @@ -771,12 +1330,12 @@ void RenderListMarker::updateMargins() else { int offset = font.ascent() * 2 / 3; switch (style()->listStyleType()) { - case DISC: - case CIRCLE: - case SQUARE: + case Disc: + case Circle: + case Square: marginLeft = -offset - cMarkerPadding - 1; break; - case LNONE: + case NoneListStyle: break; default: marginLeft = m_text.isEmpty() ? 0 : -minPrefWidth() - offset / 2; @@ -788,12 +1347,12 @@ void RenderListMarker::updateMargins() else { int offset = font.ascent() * 2 / 3; switch (style()->listStyleType()) { - case DISC: - case CIRCLE: - case SQUARE: + case Disc: + case Circle: + case Square: marginLeft = offset + cMarkerPadding + 1 - minPrefWidth(); break; - case LNONE: + case NoneListStyle: break; default: marginLeft = m_text.isEmpty() ? 0 : offset / 2; @@ -833,42 +1392,99 @@ IntRect RenderListMarker::getRelativeMarkerRect() if (isImage()) return IntRect(x(), y(), m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height()); - switch (style()->listStyleType()) { - case DISC: - case CIRCLE: - case SQUARE: { + EListStyleType type = style()->listStyleType(); + switch (type) { + case Disc: + case Circle: + case Square: { // FIXME: Are these particular rounding rules necessary? const Font& font = style()->font(); int ascent = font.ascent(); int bulletWidth = (ascent * 2 / 3 + 1) / 2; return IntRect(x() + 1, y() + 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth); } - case LNONE: + case NoneListStyle: return IntRect(); - case ARMENIAN: - case CJK_IDEOGRAPHIC: - case DECIMAL_LEADING_ZERO: - case GEORGIAN: - case HEBREW: - case HIRAGANA: - case HIRAGANA_IROHA: - case KATAKANA: - case KATAKANA_IROHA: - case LDECIMAL: - case LOWER_ALPHA: - case LOWER_GREEK: - case LOWER_LATIN: - case LOWER_ROMAN: - case UPPER_ALPHA: - case UPPER_LATIN: - case UPPER_ROMAN: + case Afar: + case Amharic: + case AmharicAbegede: + case ArabicIndic: + case Armenian: + case BinaryListStyle: + case Bengali: + case Cambodian: + case CJKIdeographic: + case CjkEarthlyBranch: + case CjkHeavenlyStem: + case DecimalLeadingZero: + case DecimalListStyle: + case Devanagari: + case Ethiopic: + case EthiopicAbegede: + case EthiopicAbegedeAmEt: + case EthiopicAbegedeGez: + case EthiopicAbegedeTiEr: + case EthiopicAbegedeTiEt: + case EthiopicHalehameAaEr: + case EthiopicHalehameAaEt: + case EthiopicHalehameAmEt: + case EthiopicHalehameGez: + case EthiopicHalehameOmEt: + case EthiopicHalehameSidEt: + case EthiopicHalehameSoEt: + case EthiopicHalehameTiEr: + case EthiopicHalehameTiEt: + case EthiopicHalehameTig: + case Georgian: + case Gujarati: + case Gurmukhi: + case Hangul: + case HangulConsonant: + case Hebrew: + case Hiragana: + case HiraganaIroha: + case Kannada: + case Katakana: + case KatakanaIroha: + case Khmer: + case Lao: + case LowerAlpha: + case LowerGreek: + case LowerHexadecimal: + case LowerLatin: + case LowerNorwegian: + case LowerRoman: + case Malayalam: + case Mongolian: + case Myanmar: + case Octal: + case Oriya: + case Oromo: + case Persian: + case Sidama: + case Somali: + case Telugu: + case Thai: + case Tibetan: + case Tigre: + case TigrinyaEr: + case TigrinyaErAbegede: + case TigrinyaEt: + case TigrinyaEtAbegede: + case UpperAlpha: + case UpperGreek: + case UpperHexadecimal: + case UpperLatin: + case UpperNorwegian: + case UpperRoman: + case Urdu: if (m_text.isEmpty()) return IntRect(); const Font& font = style()->font(); int itemWidth = font.width(m_text); - const UChar periodSpace[2] = { '.', ' ' }; - int periodSpaceWidth = font.width(TextRun(periodSpace, 2)); - return IntRect(x(), y() + font.ascent(), itemWidth + periodSpaceWidth, font.height()); + UChar suffixSpace[2] = { listMarkerSuffix(type), ' ' }; + int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2)); + return IntRect(x(), y() + font.ascent(), itemWidth + suffixSpaceWidth, font.height()); } return IntRect(); diff --git a/WebCore/rendering/RenderListMarker.h b/WebCore/rendering/RenderListMarker.h index 5b46278..971877b 100644 --- a/WebCore/rendering/RenderListMarker.h +++ b/WebCore/rendering/RenderListMarker.h @@ -73,6 +73,7 @@ private: virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); IntRect getRelativeMarkerRect(); + IntRect localSelectionRect(); String m_text; RefPtr<StyleImage> m_image; diff --git a/WebCore/rendering/RenderMarquee.cpp b/WebCore/rendering/RenderMarquee.cpp index 31a8305..bb917f8 100644 --- a/WebCore/rendering/RenderMarquee.cpp +++ b/WebCore/rendering/RenderMarquee.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * Portions are Copyright (C) 1998 Netscape Communications Corporation. * @@ -152,7 +153,11 @@ int RenderMarquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge void RenderMarquee::start() { - if (m_timer.isActive() || m_layer->renderer()->style()->marqueeIncrement().isZero()) + if (m_timer.isActive() || m_layer->renderer()->style()->marqueeIncrement().isZero() +#if ENABLE(WCSS) && ENABLE(XHTMLMP) + || (m_layer->renderer()->document()->isXHTMLMPDocument() && !m_layer->renderer()->style()->marqueeLoopCount()) +#endif + ) return; // We may end up propagating a scroll event. It is important that we suspend events until diff --git a/WebCore/rendering/RenderMarquee.h b/WebCore/rendering/RenderMarquee.h index 886c343..1651454 100644 --- a/WebCore/rendering/RenderMarquee.h +++ b/WebCore/rendering/RenderMarquee.h @@ -53,7 +53,7 @@ namespace WebCore { class RenderLayer; // This class handles the auto-scrolling of layers with overflow: marquee. -class RenderMarquee { +class RenderMarquee : public Noncopyable { public: RenderMarquee(RenderLayer*); diff --git a/WebCore/rendering/RenderMedia.cpp b/WebCore/rendering/RenderMedia.cpp index b87e99d..7100435 100644 --- a/WebCore/rendering/RenderMedia.cpp +++ b/WebCore/rendering/RenderMedia.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,6 +33,7 @@ #include "HTMLNames.h" #include "MediaControlElements.h" #include "MouseEvent.h" +#include "RenderTheme.h" #include <wtf/CurrentTime.h> #include <wtf/MathExtras.h> @@ -44,32 +45,30 @@ using namespace HTMLNames; static const double cTimeUpdateRepeatDelay = 0.2; static const double cOpacityAnimationRepeatDelay = 0.05; -// FIXME get this from style -static const double cOpacityAnimationDurationFadeIn = 0.1; -static const double cOpacityAnimationDurationFadeOut = 0.3; RenderMedia::RenderMedia(HTMLMediaElement* video) - : RenderReplaced(video) + : RenderImage(video) , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired) , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired) , m_mouseOver(false) , m_opacityAnimationStartTime(0) - , m_opacityAnimationDuration(cOpacityAnimationDurationFadeIn) + , m_opacityAnimationDuration(0) , m_opacityAnimationFrom(0) , m_opacityAnimationTo(1.0f) { } RenderMedia::RenderMedia(HTMLMediaElement* video, const IntSize& intrinsicSize) - : RenderReplaced(video, intrinsicSize) + : RenderImage(video) , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired) , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired) , m_mouseOver(false) , m_opacityAnimationStartTime(0) - , m_opacityAnimationDuration(cOpacityAnimationDurationFadeIn) + , m_opacityAnimationDuration(0) , m_opacityAnimationFrom(0) , m_opacityAnimationTo(1.0f) { + setIntrinsicSize(intrinsicSize); } RenderMedia::~RenderMedia() @@ -88,7 +87,7 @@ void RenderMedia::destroy() m_controlsShadowRoot->detach(); m_controlsShadowRoot = 0; } - RenderReplaced::destroy(); + RenderImage::destroy(); } HTMLMediaElement* RenderMedia::mediaElement() const @@ -103,7 +102,7 @@ MediaPlayer* RenderMedia::player() const void RenderMedia::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { - RenderReplaced::styleDidChange(diff, oldStyle); + RenderImage::styleDidChange(diff, oldStyle); if (m_controlsShadowRoot) { if (m_panel) @@ -120,6 +119,8 @@ void RenderMedia::styleDidChange(StyleDifference diff, const RenderStyle* oldSty m_rewindButton->updateStyle(); if (m_returnToRealtimeButton) m_returnToRealtimeButton->updateStyle(); + if (m_toggleClosedCaptionsButton) + m_toggleClosedCaptionsButton->updateStyle(); if (m_statusDisplay) m_statusDisplay->updateStyle(); if (m_timelineContainer) @@ -132,6 +133,10 @@ void RenderMedia::styleDidChange(StyleDifference diff, const RenderStyle* oldSty m_currentTimeDisplay->updateStyle(); if (m_timeRemainingDisplay) m_timeRemainingDisplay->updateStyle(); + if (m_volumeSliderContainer) + m_volumeSliderContainer->updateStyle(); + if (m_volumeSlider) + m_volumeSlider->updateStyle(); } } @@ -139,7 +144,7 @@ void RenderMedia::layout() { IntSize oldSize = contentBoxRect().size(); - RenderReplaced::layout(); + RenderImage::layout(); RenderBox* controlsRenderer = m_controlsShadowRoot ? m_controlsShadowRoot->renderBox() : 0; if (!controlsRenderer) @@ -218,6 +223,13 @@ void RenderMedia::createReturnToRealtimeButton() m_returnToRealtimeButton->attachToParent(m_panel.get()); } +void RenderMedia::createToggleClosedCaptionsButton() +{ + ASSERT(!m_toggleClosedCaptionsButton); + m_toggleClosedCaptionsButton = new MediaControlToggleClosedCaptionsButtonElement(document(), mediaElement()); + m_toggleClosedCaptionsButton->attachToParent(m_panel.get()); +} + void RenderMedia::createStatusDisplay() { ASSERT(!m_statusDisplay); @@ -240,6 +252,23 @@ void RenderMedia::createTimeline() m_timeline->attachToParent(m_timelineContainer.get()); } +void RenderMedia::createVolumeSliderContainer() +{ + ASSERT(!m_volumeSliderContainer); + m_volumeSliderContainer = new MediaControlVolumeSliderContainerElement(document(), mediaElement()); + m_volumeSliderContainer->attachToParent(m_panel.get()); +} + +void RenderMedia::createVolumeSlider() +{ + ASSERT(!m_volumeSlider); + m_volumeSlider = new MediaControlVolumeSliderElement(document(), mediaElement()); + m_volumeSlider->setAttribute(precisionAttr, "float"); + m_volumeSlider->setAttribute(maxAttr, "1"); + m_volumeSlider->setAttribute(valueAttr, String::number(mediaElement()->volume())); + m_volumeSlider->attachToParent(m_volumeSliderContainer.get()); +} + void RenderMedia::createCurrentTimeDisplay() { ASSERT(!m_currentTimeDisplay); @@ -285,7 +314,10 @@ void RenderMedia::updateControls() m_currentTimeDisplay = 0; m_timeRemainingDisplay = 0; m_fullscreenButton = 0; + m_volumeSliderContainer = 0; + m_volumeSlider = 0; m_controlsShadowRoot = 0; + m_toggleClosedCaptionsButton = 0; } m_opacityAnimationTo = 1.0f; m_opacityAnimationTimer.stop(); @@ -298,19 +330,23 @@ void RenderMedia::updateControls() createPanel(); if (m_panel) { createRewindButton(); - createMuteButton(); createPlayButton(); createReturnToRealtimeButton(); createStatusDisplay(); createTimelineContainer(); - createSeekBackButton(); - createSeekForwardButton(); - createFullscreenButton(); if (m_timelineContainer) { createCurrentTimeDisplay(); createTimeline(); createTimeRemainingDisplay(); } + createSeekBackButton(); + createSeekForwardButton(); + createToggleClosedCaptionsButton(); + createFullscreenButton(); + createMuteButton(); + createVolumeSliderContainer(); + if (m_volumeSliderContainer) + createVolumeSlider(); m_panel->attach(); } } @@ -336,6 +372,8 @@ void RenderMedia::updateControls() m_playButton->update(); if (m_timelineContainer) m_timelineContainer->update(); + if (m_volumeSliderContainer) + m_volumeSliderContainer->update(); if (m_timeline) m_timeline->update(); if (m_currentTimeDisplay) @@ -350,10 +388,14 @@ void RenderMedia::updateControls() m_rewindButton->update(); if (m_returnToRealtimeButton) m_returnToRealtimeButton->update(); + if (m_toggleClosedCaptionsButton) + m_toggleClosedCaptionsButton->update(); if (m_statusDisplay) m_statusDisplay->update(); if (m_fullscreenButton) m_fullscreenButton->update(); + if (m_volumeSlider) + m_volumeSlider->update(); updateTimeDisplay(); updateControlVisibility(); @@ -366,37 +408,20 @@ void RenderMedia::timeUpdateTimerFired(Timer<RenderMedia>*) updateTimeDisplay(); } -String RenderMedia::formatTime(float time) -{ - if (!isfinite(time)) - time = 0; - int seconds = (int)fabsf(time); - int hours = seconds / (60 * 60); - int minutes = (seconds / 60) % 60; - seconds %= 60; - if (hours) { - if (hours > 9) - return String::format("%s%02d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); - else - return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); - } - else - return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds); -} - void RenderMedia::updateTimeDisplay() { if (!m_currentTimeDisplay || !m_currentTimeDisplay->renderer() || m_currentTimeDisplay->renderer()->style()->display() == NONE || style()->visibility() != VISIBLE) return; + float now = mediaElement()->currentTime(); float duration = mediaElement()->duration(); - String timeString = formatTime(now); + // Allow the theme to format the time ExceptionCode ec; - m_currentTimeDisplay->setInnerText(timeString, ec); - - timeString = formatTime(now - duration); - m_timeRemainingDisplay->setInnerText(timeString, ec); + m_currentTimeDisplay->setInnerText(theme()->formatMediaControlsCurrentTime(now, duration), ec); + m_currentTimeDisplay->setCurrentValue(now); + m_timeRemainingDisplay->setInnerText(theme()->formatMediaControlsRemainingTime(now, duration), ec); + m_timeRemainingDisplay->setCurrentValue(now - duration); } void RenderMedia::updateControlVisibility() @@ -430,9 +455,9 @@ void RenderMedia::updateControlVisibility() } if (animateFrom < animateTo) - m_opacityAnimationDuration = cOpacityAnimationDurationFadeIn; + m_opacityAnimationDuration = m_panel->renderer()->theme()->mediaControlsFadeInDuration(); else - m_opacityAnimationDuration = cOpacityAnimationDurationFadeOut; + m_opacityAnimationDuration = m_panel->renderer()->theme()->mediaControlsFadeOutDuration(); m_opacityAnimationFrom = animateFrom; m_opacityAnimationTo = animateTo; @@ -463,13 +488,53 @@ void RenderMedia::opacityAnimationTimerFired(Timer<RenderMedia>*) changeOpacity(m_panel.get(), opacity); } +void RenderMedia::updateVolumeSliderContainer(bool visible) +{ + if (!mediaElement()->hasAudio() || !m_volumeSliderContainer || !m_volumeSlider) + return; + + if (visible && !m_volumeSliderContainer->isVisible()) { + if (!m_muteButton || !m_muteButton->renderer() || !m_muteButton->renderBox()) + return; + + RefPtr<RenderStyle> s = m_volumeSliderContainer->styleForElement(); + int height = s->height().isPercent() ? 0 : s->height().value(); + int x = m_muteButton->renderBox()->offsetLeft(); + int y = m_muteButton->renderBox()->offsetTop() - height; + FloatPoint absPoint = m_muteButton->renderer()->localToAbsolute(FloatPoint(x, y), true, true); + if (absPoint.y() < 0) + y = m_muteButton->renderBox()->offsetTop() + m_muteButton->renderBox()->height(); + m_volumeSliderContainer->setVisible(true); + m_volumeSliderContainer->setPosition(x, y); + m_volumeSliderContainer->update(); + m_volumeSlider->update(); + } else if (!visible && m_volumeSliderContainer->isVisible()) { + m_volumeSliderContainer->setVisible(false); + m_volumeSliderContainer->updateStyle(); + } +} + void RenderMedia::forwardEvent(Event* event) { if (event->isMouseEvent() && m_controlsShadowRoot) { MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); IntPoint point(mouseEvent->absoluteLocation()); - if (m_muteButton && m_muteButton->hitTest(point)) + bool showVolumeSlider = false; + if (m_muteButton && m_muteButton->hitTest(point)) { m_muteButton->defaultEventHandler(event); + if (event->type() != eventNames().mouseoutEvent) + showVolumeSlider = true; + } + + if (m_volumeSliderContainer && m_volumeSliderContainer->hitTest(point)) + showVolumeSlider = true; + + if (m_volumeSlider && m_volumeSlider->hitTest(point)) { + m_volumeSlider->defaultEventHandler(event); + showVolumeSlider = true; + } + + updateVolumeSliderContainer(showVolumeSlider); if (m_playButton && m_playButton->hitTest(point)) m_playButton->defaultEventHandler(event); @@ -486,6 +551,9 @@ void RenderMedia::forwardEvent(Event* event) if (m_returnToRealtimeButton && m_returnToRealtimeButton->hitTest(point)) m_returnToRealtimeButton->defaultEventHandler(event); + if (m_toggleClosedCaptionsButton && m_toggleClosedCaptionsButton->hitTest(point)) + m_toggleClosedCaptionsButton->defaultEventHandler(event); + if (m_timeline && m_timeline->hitTest(point)) m_timeline->defaultEventHandler(event); @@ -508,7 +576,7 @@ void RenderMedia::forwardEvent(Event* event) int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) const { - int bottom = RenderReplaced::lowestPosition(includeOverflowInterior, includeSelf); + int bottom = RenderImage::lowestPosition(includeOverflowInterior, includeSelf); if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return bottom; @@ -517,7 +585,7 @@ int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const { - int right = RenderReplaced::rightmostPosition(includeOverflowInterior, includeSelf); + int right = RenderImage::rightmostPosition(includeOverflowInterior, includeSelf); if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return right; @@ -526,7 +594,7 @@ int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSel int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const { - int left = RenderReplaced::leftmostPosition(includeOverflowInterior, includeSelf); + int left = RenderImage::leftmostPosition(includeOverflowInterior, includeSelf); if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) return left; diff --git a/WebCore/rendering/RenderMedia.h b/WebCore/rendering/RenderMedia.h index 5697bda..0d24c4c 100644 --- a/WebCore/rendering/RenderMedia.h +++ b/WebCore/rendering/RenderMedia.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,7 +28,7 @@ #if ENABLE(VIDEO) -#include "RenderReplaced.h" +#include "RenderImage.h" #include "Timer.h" namespace WebCore { @@ -40,28 +40,29 @@ class MediaControlPlayButtonElement; class MediaControlSeekButtonElement; class MediaControlRewindButtonElement; class MediaControlReturnToRealtimeButtonElement; +class MediaControlToggleClosedCaptionsButtonElement; class MediaControlTimelineElement; +class MediaControlVolumeSliderElement; class MediaControlFullscreenButtonElement; class MediaControlTimeDisplayElement; class MediaControlStatusDisplayElement; class MediaControlTimelineContainerElement; +class MediaControlVolumeSliderContainerElement; class MediaControlElement; class MediaPlayer; -class RenderMedia : public RenderReplaced { +class RenderMedia : public RenderImage { public: RenderMedia(HTMLMediaElement*); RenderMedia(HTMLMediaElement*, const IntSize& intrinsicSize); virtual ~RenderMedia(); - + const RenderObjectChildList* children() const { return &m_children; } RenderObjectChildList* children() { return &m_children; } HTMLMediaElement* mediaElement() const; MediaPlayer* player() const; - static String formatTime(float time); - bool shouldShowTimeDisplayControls() const; void updateFromElement(); @@ -82,6 +83,7 @@ private: virtual const char* renderName() const { return "RenderMedia"; } virtual bool isMedia() const { return true; } + virtual bool isImage() const { return false; } virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; @@ -96,9 +98,12 @@ private: void createSeekForwardButton(); void createRewindButton(); void createReturnToRealtimeButton(); + void createToggleClosedCaptionsButton(); void createStatusDisplay(); void createTimelineContainer(); void createTimeline(); + void createVolumeSliderContainer(); + void createVolumeSlider(); void createCurrentTimeDisplay(); void createTimeRemainingDisplay(); void createFullscreenButton(); @@ -109,6 +114,8 @@ private: void changeOpacity(HTMLElement*, float opacity); void opacityAnimationTimerFired(Timer<RenderMedia>*); + void updateVolumeSliderContainer(bool visible); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); RefPtr<HTMLElement> m_controlsShadowRoot; @@ -119,9 +126,12 @@ private: RefPtr<MediaControlSeekButtonElement> m_seekForwardButton; RefPtr<MediaControlRewindButtonElement> m_rewindButton; RefPtr<MediaControlReturnToRealtimeButtonElement> m_returnToRealtimeButton; + RefPtr<MediaControlToggleClosedCaptionsButtonElement> m_toggleClosedCaptionsButton; RefPtr<MediaControlTimelineElement> m_timeline; + RefPtr<MediaControlVolumeSliderElement> m_volumeSlider; RefPtr<MediaControlFullscreenButtonElement> m_fullscreenButton; RefPtr<MediaControlTimelineContainerElement> m_timelineContainer; + RefPtr<MediaControlVolumeSliderContainerElement> m_volumeSliderContainer; RefPtr<MediaControlTimeDisplayElement> m_currentTimeDisplay; RefPtr<MediaControlTimeDisplayElement> m_timeRemainingDisplay; RefPtr<MediaControlStatusDisplayElement> m_statusDisplay; diff --git a/WebCore/rendering/RenderMediaControls.cpp b/WebCore/rendering/RenderMediaControls.cpp index 06d901a..17576ae 100644 --- a/WebCore/rendering/RenderMediaControls.cpp +++ b/WebCore/rendering/RenderMediaControls.cpp @@ -37,7 +37,7 @@ using namespace std; namespace WebCore { -#if !defined(NDEBUG) && defined(USE_DEBUG_SAFARI_THEME) +#ifdef DEBUG_ALL SOFT_LINK_DEBUG_LIBRARY(SafariTheme) #else SOFT_LINK_LIBRARY(SafariTheme) @@ -82,16 +82,6 @@ void RenderMediaControls::adjustMediaSliderThumbSize(RenderObject* o) o->style()->setHeight(Length(static_cast<int>(mediaSliderThumbHeight * zoomLevel), Fixed)); } -static HTMLMediaElement* parentMediaElement(RenderObject* o) -{ - Node* node = o->node(); - Node* mediaNode = node ? node->shadowAncestorNode() : 0; - if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag))) - return 0; - - return static_cast<HTMLMediaElement*>(mediaNode); -} - bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { ASSERT(SafariThemeLibrary()); @@ -100,6 +90,15 @@ bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, R case MediaFullscreenButton: paintThemePart(SafariTheme::MediaFullscreenButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); break; + case MediaShowClosedCaptionsButton: + case MediaHideClosedCaptionsButton: +#if SAFARI_THEME_VERSION >= 4 + if (MediaControlToggleClosedCaptionsButtonElement* btn = static_cast<MediaControlToggleClosedCaptionsButtonElement*>(o->node())) { + bool captionsVisible = btn->displayType() == MediaHideClosedCaptionsButton; + paintThemePart(captionsVisible ? SafariTheme::MediaHideClosedCaptionsButtonPart : SafariTheme::MediaShowClosedCaptionsButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); + } +#endif + break; case MediaMuteButton: case MediaUnMuteButton: if (MediaControlMuteButtonElement* btn = static_cast<MediaControlMuteButtonElement*>(o->node())) { @@ -110,8 +109,8 @@ bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, R case MediaPauseButton: case MediaPlayButton: if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(o->node())) { - bool currentlyPlaying = btn->displayType() == MediaPlayButton; - paintThemePart(currentlyPlaying ? SafariTheme::MediaPauseButtonPart : SafariTheme::MediaPlayButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); + bool canPlay = btn->displayType() == MediaPlayButton; + paintThemePart(canPlay ? SafariTheme::MediaPlayButtonPart : SafariTheme::MediaPauseButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); } break; case MediaSeekBackButton: @@ -121,13 +120,25 @@ bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, R paintThemePart(SafariTheme::MediaSeekForwardButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); break; case MediaSlider: { - if (HTMLMediaElement* mediaElement = parentMediaElement(o)) + if (HTMLMediaElement* mediaElement = toParentMediaElement(o)) STPaintProgressIndicator(SafariTheme::MediaType, paintInfo.context->platformContext(), r, NSRegularControlSize, 0, mediaElement->percentLoaded()); break; } case MediaSliderThumb: paintThemePart(SafariTheme::MediaSliderThumbPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); break; + case MediaVolumeSliderContainer: + // FIXME: Implement volume slider. + ASSERT_NOT_REACHED(); + break; + case MediaVolumeSlider: + // FIXME: Implement volume slider. + ASSERT_NOT_REACHED(); + break; + case MediaVolumeSliderThumb: + // FIXME: Implement volume slider. + ASSERT_NOT_REACHED(); + break; case MediaTimelineContainer: ASSERT_NOT_REACHED(); break; @@ -147,4 +158,3 @@ bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, R #endif // #if ENABLE(VIDEO) } // namespace WebCore - diff --git a/WebCore/rendering/RenderMediaControlsChromium.cpp b/WebCore/rendering/RenderMediaControlsChromium.cpp new file mode 100644 index 0000000..50feb46 --- /dev/null +++ b/WebCore/rendering/RenderMediaControlsChromium.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2009 Apple Inc. + * Copyright (C) 2009 Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderMediaControlsChromium.h" + +#include "Gradient.h" +#include "GraphicsContext.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" + +namespace WebCore { + +#if ENABLE(VIDEO) + +typedef WTF::HashMap<const char*, Image*> MediaControlImageMap; +static MediaControlImageMap* gMediaControlImageMap = 0; + +static Image* platformResource(const char* name) +{ + if (!gMediaControlImageMap) + gMediaControlImageMap = new MediaControlImageMap(); + if (Image* image = gMediaControlImageMap->get(name)) + return image; + if (Image* image = Image::loadPlatformResource(name).releaseRef()) { + gMediaControlImageMap->set(name, image); + return image; + } + ASSERT_NOT_REACHED(); + return 0; +} + +static bool hasSource(const HTMLMediaElement* mediaElement) +{ + return mediaElement->networkState() != HTMLMediaElement::NETWORK_EMPTY + && mediaElement->networkState() != HTMLMediaElement::NETWORK_NO_SOURCE; +} + +static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Image* image) +{ + IntRect imageRect = image->rect(); + context->drawImage(image, DeviceColorSpace, rect); + return true; +} + +static bool paintMediaMuteButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + static Image* soundFull = platformResource("mediaSoundFull"); + static Image* soundNone = platformResource("mediaSoundNone"); + static Image* soundDisabled = platformResource("mediaSoundDisabled"); + + if (!hasSource(mediaElement) || !mediaElement->hasAudio()) + return paintMediaButton(paintInfo.context, rect, soundDisabled); + + return paintMediaButton(paintInfo.context, rect, mediaElement->muted() ? soundNone: soundFull); +} + +static bool paintMediaPlayButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + static Image* mediaPlay = platformResource("mediaPlay"); + static Image* mediaPause = platformResource("mediaPause"); + static Image* mediaPlayDisabled = platformResource("mediaPlayDisabled"); + + if (!hasSource(mediaElement)) + return paintMediaButton(paintInfo.context, rect, mediaPlayDisabled); + + return paintMediaButton(paintInfo.context, rect, mediaElement->paused() ? mediaPlay : mediaPause); +} + +static bool paintMediaSlider(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + RenderStyle* style = object->style(); + GraphicsContext* context = paintInfo.context; + + // Draw the border of the time bar. + // FIXME: this should be a rounded rect but need to fix GraphicsContextSkia first. + // https://bugs.webkit.org/show_bug.cgi?id=30143 + context->save(); + context->setShouldAntialias(true); + context->setStrokeStyle(SolidStroke); + context->setStrokeColor(style->borderLeftColor(), DeviceColorSpace); + context->setStrokeThickness(style->borderLeftWidth()); + context->setFillColor(style->backgroundColor(), DeviceColorSpace); + context->drawRect(rect); + context->restore(); + + // Draw the buffered ranges. + // FIXME: Draw multiple ranges if there are multiple buffered ranges. + IntRect bufferedRect = rect; + bufferedRect.inflate(-style->borderLeftWidth()); + bufferedRect.setWidth((bufferedRect.width() * mediaElement->percentLoaded())); + + // Don't bother drawing an empty area. + if (!bufferedRect.isEmpty()) { + IntPoint sliderTopLeft = bufferedRect.location(); + IntPoint sliderTopRight = sliderTopLeft; + sliderTopRight.move(0, bufferedRect.height()); + + RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight); + Color startColor = object->style()->color(); + gradient->addColorStop(0.0, startColor); + gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha())); + + context->save(); + context->setStrokeStyle(NoStroke); + context->setFillGradient(gradient); + context->fillRect(bufferedRect); + context->restore(); + } + + return true; +} + +static bool paintMediaSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + if (!object->parent()->isSlider()) + return false; + + HTMLMediaElement* mediaElement = toParentMediaElement(object->parent()); + if (!mediaElement) + return false; + + if (!hasSource(mediaElement)) + return true; + + static Image* mediaSliderThumb = platformResource("mediaSliderThumb"); + return paintMediaButton(paintInfo.context, rect, mediaSliderThumb); +} + +static bool paintMediaVolumeSlider(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + GraphicsContext* context = paintInfo.context; + Color originalColor = context->strokeColor(); + if (originalColor != Color::white) + context->setStrokeColor(Color::white, DeviceColorSpace); + + int x = rect.x() + rect.width() / 2; + context->drawLine(IntPoint(x, rect.y()), IntPoint(x, rect.y() + rect.height())); + + if (originalColor != Color::white) + context->setStrokeColor(originalColor, DeviceColorSpace); + return true; +} + +static bool paintMediaVolumeSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + if (!object->parent()->isSlider()) + return false; + + static Image* mediaVolumeSliderThumb = platformResource("mediaVolumeSliderThumb"); + return paintMediaButton(paintInfo.context, rect, mediaVolumeSliderThumb); +} + +static bool paintMediaTimelineContainer(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + if (!rect.isEmpty()) { + GraphicsContext* context = paintInfo.context; + Color originalColor = context->strokeColor(); + float originalThickness = context->strokeThickness(); + StrokeStyle originalStyle = context->strokeStyle(); + + context->setStrokeStyle(SolidStroke); + + // Draw the left border using CSS defined width and color. + context->setStrokeThickness(object->style()->borderLeftWidth()); + context->setStrokeColor(object->style()->borderLeftColor().rgb(), DeviceColorSpace); + context->drawLine(IntPoint(rect.x() + 1, rect.y()), + IntPoint(rect.x() + 1, rect.y() + rect.height())); + + // Draw the right border using CSS defined width and color. + context->setStrokeThickness(object->style()->borderRightWidth()); + context->setStrokeColor(object->style()->borderRightColor().rgb(), DeviceColorSpace); + context->drawLine(IntPoint(rect.x() + rect.width() - 1, rect.y()), + IntPoint(rect.x() + rect.width() - 1, rect.y() + rect.height())); + + context->setStrokeColor(originalColor, DeviceColorSpace); + context->setStrokeThickness(originalThickness); + context->setStrokeStyle(originalStyle); + } + return true; +} + +bool RenderMediaControlsChromium::shouldRenderMediaControlPart(ControlPart part, Element* e) +{ + UNUSED_PARAM(e); + + switch (part) { + case MediaMuteButtonPart: + case MediaPlayButtonPart: + case MediaSliderPart: + case MediaSliderThumbPart: + case MediaVolumeSliderContainerPart: + case MediaVolumeSliderPart: + case MediaVolumeSliderThumbPart: + case MediaControlsBackgroundPart: + case MediaCurrentTimePart: + case MediaTimeRemainingPart: + return true; + default: + ; + } + return false; +} + +bool RenderMediaControlsChromium::paintMediaControlsPart(MediaControlElementType part, RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + switch (part) { + case MediaMuteButton: + case MediaUnMuteButton: + return paintMediaMuteButton(object, paintInfo, rect); + case MediaPauseButton: + case MediaPlayButton: + return paintMediaPlayButton(object, paintInfo, rect); + case MediaSlider: + return paintMediaSlider(object, paintInfo, rect); + case MediaSliderThumb: + return paintMediaSliderThumb(object, paintInfo, rect); + case MediaVolumeSlider: + return paintMediaVolumeSlider(object, paintInfo, rect); + case MediaVolumeSliderThumb: + return paintMediaVolumeSliderThumb(object, paintInfo, rect); + case MediaTimelineContainer: + return paintMediaTimelineContainer(object, paintInfo, rect); + case MediaFullscreenButton: + case MediaSeekBackButton: + case MediaSeekForwardButton: + case MediaVolumeSliderContainer: + case MediaCurrentTimeDisplay: + case MediaTimeRemainingDisplay: + case MediaControlsPanel: + case MediaRewindButton: + case MediaReturnToRealtimeButton: + case MediaStatusDisplay: + case MediaShowClosedCaptionsButton: + case MediaHideClosedCaptionsButton: + ASSERT_NOT_REACHED(); + break; + } + return false; +} + +void RenderMediaControlsChromium::adjustMediaSliderThumbSize(RenderObject* object) +{ + static Image* mediaSliderThumb = platformResource("mediaSliderThumb"); + static Image* mediaVolumeSliderThumb = platformResource("mediaVolumeSliderThumb"); + + Image* thumbImage = 0; + if (object->style()->appearance() == MediaSliderThumbPart) + thumbImage = mediaSliderThumb; + else if (object->style()->appearance() == MediaVolumeSliderThumbPart) + thumbImage = mediaVolumeSliderThumb; + + float zoomLevel = object->style()->effectiveZoom(); + if (thumbImage) { + object->style()->setWidth(Length(static_cast<int>(thumbImage->width() * zoomLevel), Fixed)); + object->style()->setHeight(Length(static_cast<int>(thumbImage->height() * zoomLevel), Fixed)); + } +} + +#endif // #if ENABLE(VIDEO) + +} // namespace WebCore diff --git a/WebCore/rendering/RenderMediaControlsChromium.h b/WebCore/rendering/RenderMediaControlsChromium.h new file mode 100644 index 0000000..d6d986c --- /dev/null +++ b/WebCore/rendering/RenderMediaControlsChromium.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 Apple Inc. + * Copyright (C) 2009 Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderMediaControlsChromium_h +#define RenderMediaControlsChromium_h + +#include "RenderObject.h" +#include "MediaControlElements.h" + +namespace WebCore { + +class HTMLMediaElement; +class RenderMediaControlsChromium { +public: + static bool shouldRenderMediaControlPart(ControlPart, Element*); + static bool paintMediaControlsPart(MediaControlElementType, RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + static void adjustMediaSliderThumbSize(RenderObject*); +}; + +} // namespace WebCore + +#endif // RenderMediaControlsChromium_h diff --git a/WebCore/rendering/RenderMenuList.cpp b/WebCore/rendering/RenderMenuList.cpp index 9d2e7e0..05a9873 100644 --- a/WebCore/rendering/RenderMenuList.cpp +++ b/WebCore/rendering/RenderMenuList.cpp @@ -1,7 +1,7 @@ /* * This file is part of the select element renderer in WebCore. * - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * This library is free software; you can redistribute it and/or @@ -24,6 +24,7 @@ #include "config.h" #include "RenderMenuList.h" +#include "AXObjectCache.h" #include "CSSStyleSelector.h" #include "Frame.h" #include "FrameView.h" @@ -50,6 +51,7 @@ RenderMenuList::RenderMenuList(Element* element) , m_innerBlock(0) , m_optionsChanged(true) , m_optionsWidth(0) + , m_lastSelectedIndex(-1) , m_popup(0) , m_popupIsVisible(false) { @@ -292,7 +294,6 @@ void RenderMenuList::hidePopup() { if (m_popup) m_popup->hide(); - m_popupIsVisible = false; } void RenderMenuList::valueChanged(unsigned listIndex, bool fireOnChange) @@ -307,10 +308,25 @@ void RenderMenuList::valueChanged(unsigned listIndex, bool fireOnChange) select->setSelectedIndexByUser(select->listToOptionIndex(listIndex), true, fireOnChange); } +void RenderMenuList::didSetSelectedIndex() +{ + int index = selectedIndex(); + if (m_lastSelectedIndex == index) + return; + + m_lastSelectedIndex = index; + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->postNotification(this, AXObjectCache::AXMenuListValueChanged, true, PostSynchronously); +} + String RenderMenuList::itemText(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return String(); + Element* element = listItems[listIndex]; if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) return optionGroupElement->groupLabelText(); else if (OptionElement* optionElement = toOptionElement(element)) @@ -321,14 +337,20 @@ String RenderMenuList::itemText(unsigned listIndex) const String RenderMenuList::itemToolTip(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return String(); + Element* element = listItems[listIndex]; return element->title(); } bool RenderMenuList::itemIsEnabled(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return false; + Element* element = listItems[listIndex]; if (!isOptionElement(element)) return false; @@ -346,7 +368,18 @@ bool RenderMenuList::itemIsEnabled(unsigned listIndex) const PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) { + // If we are making an out of bounds access, then we want to use the style + // of a different option element (index 0). However, if there isn't an option element + // before at index 0, we fall back to the menu's style. + if (!listIndex) + return menuStyle(); + + // Try to retrieve the style of an option element we know exists (index 0). + listIndex = 0; + } + Element* element = listItems[listIndex]; RenderStyle* style = element->renderStyle() ? element->renderStyle() : element->computedStyle(); return style ? PopupMenuStyle(style->color(), itemBackgroundColor(listIndex), style->font(), style->visibility() == VISIBLE, style->textIndent(), style->direction()) : menuStyle(); @@ -355,7 +388,10 @@ PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const Color RenderMenuList::itemBackgroundColor(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return style()->backgroundColor(); + Element* element = listItems[listIndex]; Color backgroundColor; if (element->renderStyle()) @@ -375,7 +411,6 @@ Color RenderMenuList::itemBackgroundColor(unsigned listIndex) const PopupMenuStyle RenderMenuList::menuStyle() const { - RenderStyle* s = m_innerBlock ? m_innerBlock->style() : style(); return PopupMenuStyle(s->color(), s->backgroundColor(), s->font(), s->visibility() == VISIBLE, s->textIndent(), s->direction()); } @@ -411,8 +446,19 @@ int RenderMenuList::clientPaddingLeft() const return paddingLeft(); } +const int endOfLinePadding = 2; int RenderMenuList::clientPaddingRight() const { + if (style()->appearance() == MenulistPart || style()->appearance() == MenulistButtonPart) { + // For these appearance values, the theme applies padding to leave room for the + // drop-down button. But leaving room for the button inside the popup menu itself + // looks strange, so we return a small default padding to avoid having a large empty + // space appear on the side of the popup menu. + return endOfLinePadding; + } + + // If the appearance isn't MenulistPart, then the select is styled (non-native), so + // we want to return the user specified padding. return paddingRight(); } @@ -428,24 +474,38 @@ int RenderMenuList::selectedIndex() const return select->optionToListIndex(select->selectedIndex()); } +void RenderMenuList::popupDidHide() +{ + m_popupIsVisible = false; +} + bool RenderMenuList::itemIsSeparator(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return false; + Element* element = listItems[listIndex]; return element->hasTagName(hrTag); } bool RenderMenuList::itemIsLabel(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return false; + Element* element = listItems[listIndex]; return isOptionGroupElement(element); } bool RenderMenuList::itemIsSelected(unsigned listIndex) const { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - Element* element = select->listItems()[listIndex]; + const Vector<Element*>& listItems = select->listItems(); + if (listIndex >= listItems.size()) + return false; + Element* element = listItems[listIndex]; if (OptionElement* optionElement = toOptionElement(element)) return optionElement->selected(); return false; diff --git a/WebCore/rendering/RenderMenuList.h b/WebCore/rendering/RenderMenuList.h index 5c18e9e..a5aa041 100644 --- a/WebCore/rendering/RenderMenuList.h +++ b/WebCore/rendering/RenderMenuList.h @@ -1,7 +1,7 @@ /* * This file is part of the select element renderer in WebCore. * - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -49,6 +49,8 @@ public: void setOptionsChanged(bool changed) { m_optionsChanged = changed; } + void didSetSelectedIndex(); + String text() const; private: @@ -82,6 +84,7 @@ private: virtual int clientPaddingRight() const; virtual int listSize() const; virtual int selectedIndex() const; + virtual void popupDidHide(); virtual bool itemIsSeparator(unsigned listIndex) const; virtual bool itemIsLabel(unsigned listIndex) const; virtual bool itemIsSelected(unsigned listIndex) const; @@ -109,6 +112,8 @@ private: bool m_optionsChanged; int m_optionsWidth; + int m_lastSelectedIndex; + RefPtr<PopupMenu> m_popup; bool m_popupIsVisible; }; diff --git a/WebCore/rendering/RenderObject.cpp b/WebCore/rendering/RenderObject.cpp index 5137338..1d1e7c2 100644 --- a/WebCore/rendering/RenderObject.cpp +++ b/WebCore/rendering/RenderObject.cpp @@ -4,6 +4,8 @@ * (C) 2000 Dirk Mueller (mueller@kde.org) * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (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 @@ -26,6 +28,7 @@ #include "RenderObject.h" #include "AXObjectCache.h" +#include "Chrome.h" #include "CSSStyleSelector.h" #include "FloatQuad.h" #include "Frame.h" @@ -40,12 +43,15 @@ #include "RenderImageGeneratedContent.h" #include "RenderInline.h" #include "RenderListItem.h" +#include "RenderRuby.h" +#include "RenderRubyText.h" #include "RenderTableCell.h" #include "RenderTableCol.h" #include "RenderTableRow.h" #include "RenderTheme.h" #include "RenderView.h" #include "TransformState.h" +#include "htmlediting.h" #include <algorithm> #ifdef ANDROID_LAYOUT #include "Settings.h" @@ -62,6 +68,10 @@ #include "WMLNames.h" #endif +#if ENABLE(SVG) +#include "SVGRenderSupport.h" +#endif + using namespace std; namespace WebCore { @@ -104,56 +114,58 @@ RenderObject* RenderObject::createObject(Node* node, RenderStyle* style) return image; } - RenderObject* o = 0; +#if ENABLE(RUBY) + if (node->hasTagName(rubyTag)) { + if (style->display() == INLINE) + return new (arena) RenderRubyAsInline(node); + else + return new (arena) RenderRubyAsBlock(node); + } + // treat <rt> as ruby text ONLY if it still has its default treatment of block + if (node->hasTagName(rtTag) && style->display() == BLOCK) + return new (arena) RenderRubyText(node); +#endif switch (style->display()) { case NONE: - break; + return 0; case INLINE: - o = new (arena) RenderInline(node); - break; + return new (arena) RenderInline(node); case BLOCK: - o = new (arena) RenderBlock(node); - break; case INLINE_BLOCK: - o = new (arena) RenderBlock(node); - break; - case LIST_ITEM: - o = new (arena) RenderListItem(node); - break; case RUN_IN: case COMPACT: - o = new (arena) RenderBlock(node); - break; + return new (arena) RenderBlock(node); + case LIST_ITEM: + return new (arena) RenderListItem(node); case TABLE: case INLINE_TABLE: - o = new (arena) RenderTable(node); - break; + return new (arena) RenderTable(node); case TABLE_ROW_GROUP: case TABLE_HEADER_GROUP: case TABLE_FOOTER_GROUP: - o = new (arena) RenderTableSection(node); - break; + return new (arena) RenderTableSection(node); case TABLE_ROW: - o = new (arena) RenderTableRow(node); - break; + return new (arena) RenderTableRow(node); case TABLE_COLUMN_GROUP: case TABLE_COLUMN: - o = new (arena) RenderTableCol(node); - break; + return new (arena) RenderTableCol(node); case TABLE_CELL: - o = new (arena) RenderTableCell(node); - break; + return new (arena) RenderTableCell(node); case TABLE_CAPTION: - o = new (arena) RenderBlock(node); - break; +#if ENABLE(WCSS) + // As per the section 17.1 of the spec WAP-239-WCSS-20011026-a.pdf, + // the marquee box inherits and extends the characteristics of the + // principal block box ([CSS2] section 9.2.1). + case WAP_MARQUEE: +#endif + return new (arena) RenderBlock(node); case BOX: case INLINE_BOX: - o = new (arena) RenderFlexibleBox(node); - break; + return new (arena) RenderFlexibleBox(node); } - return o; + return 0; } #ifndef NDEBUG @@ -200,7 +212,6 @@ RenderObject::RenderObject(Node* node) , m_selectionState(SelectionNone) , m_hasColumns(false) , m_cellWidthChanged(false) - , m_replacedHasOverflow(false) { #ifndef NDEBUG renderObjectCounter.increment(); @@ -303,7 +314,7 @@ void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) // Just add it... children->insertChildNode(this, newChild, beforeChild); } - + RenderCounter::rendererSubtreeAttached(newChild); if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalText(); if (textToTransform) @@ -362,19 +373,14 @@ RenderObject* RenderObject::nextInPreOrderAfterChildren(RenderObject* stayWithin if (this == stayWithin) return 0; - RenderObject* o; - if (!(o = nextSibling())) { - o = parent(); - while (o && !o->nextSibling()) { - if (o == stayWithin) - return 0; - o = o->parent(); - } - if (o) - o = o->nextSibling(); + const RenderObject* current = this; + RenderObject* next; + while (!(next = current->nextSibling())) { + current = current->parent(); + if (!current || current == stayWithin) + return 0; } - - return o; + return next; } RenderObject* RenderObject::previousInPreOrder() const @@ -621,6 +627,11 @@ RenderBlock* RenderObject::containingBlock() const // inline directly. if (o->style()->position() == RelativePosition && o->isInline() && !o->isReplaced()) return o->containingBlock(); +#if ENABLE(SVG) + if (o->isSVGForeignObject()) //foreignObject is the containing block for contents inside it + break; +#endif + o = o->parent(); } } else { @@ -634,7 +645,7 @@ RenderBlock* RenderObject::containingBlock() const } if (!o || !o->isRenderBlock()) - return 0; // Probably doesn't happen any more, but leave just in case. -dwh + return 0; // This can still happen in case of an orphaned tree return toRenderBlock(o); } @@ -653,10 +664,10 @@ static bool mustRepaintFillLayers(const RenderObject* renderer, const FillLayer* if (!layer->xPosition().isZero() || !layer->yPosition().isZero()) return true; - if (layer->isSizeSet()) { - if (layer->size().width().isPercent() || layer->size().height().isPercent()) + if (layer->size().type == SizeLength) { + if (layer->size().size.width().isPercent() || layer->size().size.height().isPercent()) return true; - } else if (img->usesImageContainerSize()) + } else if (layer->size().type == Contain || layer->size().type == Cover || img->usesImageContainerSize()) return true; return false; @@ -710,7 +721,7 @@ void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, return; case DOTTED: case DASHED: - graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeColor(c, m_style->colorSpace()); graphicsContext->setStrokeThickness(width); graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke); @@ -731,7 +742,7 @@ void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, if (adjbw1 == 0 && adjbw2 == 0) { graphicsContext->setStrokeStyle(NoStroke); - graphicsContext->setFillColor(c); + graphicsContext->setFillColor(c, m_style->colorSpace()); switch (s) { case BSTop: case BSBottom: @@ -844,7 +855,7 @@ void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, // fall through case SOLID: { graphicsContext->setStrokeStyle(NoStroke); - graphicsContext->setFillColor(c); + graphicsContext->setFillColor(c, m_style->colorSpace()); ASSERT(x2 >= x1); ASSERT(y2 >= y1); if (!adjbw1 && !adjbw2) { @@ -904,7 +915,7 @@ void RenderObject::drawArcForBoxSide(GraphicsContext* graphicsContext, int x, in return; case DOTTED: case DASHED: - graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeColor(c, m_style->colorSpace()); graphicsContext->setStrokeStyle(style == DOTTED ? DottedStroke : DashedStroke); graphicsContext->setStrokeThickness(thickness); graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); @@ -926,7 +937,7 @@ void RenderObject::drawArcForBoxSide(GraphicsContext* graphicsContext, int x, in } graphicsContext->setStrokeStyle(SolidStroke); - graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeColor(c, m_style->colorSpace()); graphicsContext->setStrokeThickness(third); graphicsContext->strokeArc(IntRect(x, outerY, radius.width() * 2, outerHeight), angleStart, angleSpan); graphicsContext->setStrokeThickness(innerThird > 2 ? innerThird - 1 : innerThird); @@ -945,13 +956,13 @@ void RenderObject::drawArcForBoxSide(GraphicsContext* graphicsContext, int x, in } graphicsContext->setStrokeStyle(SolidStroke); - graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeColor(c, m_style->colorSpace()); 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->setStrokeColor(c2, m_style->colorSpace()); graphicsContext->setStrokeThickness(halfThickness > 2 ? halfThickness - 1 : halfThickness); graphicsContext->strokeArc(IntRect(x + shiftForInner, y + shiftForInner, (radius.width() - shiftForInner) * 2, (radius.height() - shiftForInner) * 2), angleStart, angleSpan); @@ -965,7 +976,7 @@ void RenderObject::drawArcForBoxSide(GraphicsContext* graphicsContext, int x, in c = c.dark(); case SOLID: graphicsContext->setStrokeStyle(SolidStroke); - graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeColor(c, m_style->colorSpace()); graphicsContext->setStrokeThickness(thickness); graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); break; @@ -1002,13 +1013,12 @@ void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty if (style->outlineStyleIsAuto() || hasOutlineAnnotation()) { if (!theme()->supportsFocusRing(style)) { // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. - graphicsContext->initFocusRing(ow, offset); - addFocusRingRects(graphicsContext, tx, ty); + Vector<IntRect> focusRingRects; + addFocusRingRects(focusRingRects, tx, ty); if (style->outlineStyleIsAuto()) - graphicsContext->drawFocusRing(oc); + graphicsContext->drawFocusRing(focusRingRects, ow, offset, oc); else - addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); - graphicsContext->clearFocusRing(); + addPDFURLRect(graphicsContext, unionRect(focusRingRects)); } } @@ -1066,6 +1076,23 @@ IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) return result; } +void RenderObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads) +{ + Vector<IntRect> rects; + // FIXME: addFocusRingRects() needs to be passed this transform-unaware + // localToAbsolute() offset here because RenderInline::addFocusRingRects() + // implicitly assumes that. This doesn't work correctly with transformed + // descendants. + FloatPoint absolutePoint = localToAbsolute(); + addFocusRingRects(rects, absolutePoint.x(), absolutePoint.y()); + size_t count = rects.size(); + for (size_t i = 0; i < count; ++i) { + IntRect rect = rects[i]; + rect.move(-absolutePoint.x(), -absolutePoint.y()); + quads.append(localToAbsoluteQuad(FloatQuad(rect))); + } +} + void RenderObject::addAbsoluteRectForLayer(IntRect& result) { if (hasLayer()) @@ -1329,6 +1356,50 @@ void RenderObject::showTreeForThis() const node()->showTreeForThis(); } +void RenderObject::showRenderObject() const +{ + showRenderObject(0); +} + +void RenderObject::showRenderObject(int printedCharacters) const +{ + // As this function is intended to be used when debugging, the + // this pointer may be 0. + if (!this) { + fputs("(null)\n", stderr); + return; + } + + printedCharacters += fprintf(stderr, "%s %p", renderName(), this); + + if (node()) { + if (printedCharacters) + for (; printedCharacters < 39; printedCharacters++) + fputc(' ', stderr); + fputc('\t', stderr); + node()->showNode(); + } else + fputc('\n', stderr); +} + +void RenderObject::showRenderTreeAndMark(const RenderObject* markedObject1, const char* markedLabel1, const RenderObject* markedObject2, const char* markedLabel2, int depth) const +{ + int printedCharacters = 0; + if (markedObject1 == this && markedLabel1) + printedCharacters += fprintf(stderr, "%s", markedLabel1); + if (markedObject2 == this && markedLabel2) + printedCharacters += fprintf(stderr, "%s", markedLabel2); + for (; printedCharacters < depth * 2; printedCharacters++) + fputc(' ', stderr); + + showRenderObject(printedCharacters); + if (!this) + return; + + for (const RenderObject* child = firstChild(); child; child = child->nextSibling()) + child->showRenderTreeAndMark(markedObject1, markedLabel1, markedObject2, markedLabel2, depth + 1); +} + #endif // NDEBUG Color RenderObject::selectionBackgroundColor() const @@ -1365,6 +1436,7 @@ Color RenderObject::selectionForegroundColor() const return color; } +#if ENABLE(DRAG_SUPPORT) Node* RenderObject::draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const { if (!dhtmlOK && !uaOK) @@ -1399,6 +1471,7 @@ Node* RenderObject::draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& d } return 0; } +#endif // ENABLE(DRAG_SUPPORT) void RenderObject::selectionStartEnd(int& spos, int& epos) const { @@ -1557,7 +1630,7 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS // For changes in float styles, we need to conceivably remove ourselves // from the floating objects list. toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); - else if (isPositioned() && (newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition)) + else if (isPositioned() && (m_style->position() != newStyle->position())) // For changes in positioning styles, we need to conceivably remove ourselves // from the positioned objects list. toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); @@ -1582,8 +1655,20 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS if (view()->frameView()) { // FIXME: A better solution would be to only invalidate the fixed regions when scrolling. It's overkill to // prevent the entire view from blitting on a scroll. - bool newStyleSlowScroll = newStyle && (newStyle->position() == FixedPosition || newStyle->hasFixedBackgroundImage()); - bool oldStyleSlowScroll = m_style && (m_style->position() == FixedPosition || m_style->hasFixedBackgroundImage()); + + bool shouldBlitOnFixedBackgroundImage = false; +#if ENABLE(FAST_MOBILE_SCROLLING) + // On low-powered/mobile devices, preventing blitting on a scroll can cause noticeable delays + // when scrolling a page with a fixed background image. As an optimization, assuming there are + // no fixed positoned elements on the page, we can acclerate scrolling (via blitting) if we + // ignore the CSS property "background-attachment: fixed". + shouldBlitOnFixedBackgroundImage = true; +#endif + + bool newStyleSlowScroll = newStyle && (newStyle->position() == FixedPosition + || (!shouldBlitOnFixedBackgroundImage && newStyle->hasFixedBackgroundImage())); + bool oldStyleSlowScroll = m_style && (m_style->position() == FixedPosition + || (!shouldBlitOnFixedBackgroundImage && m_style->hasFixedBackgroundImage())); if (oldStyleSlowScroll != newStyleSlowScroll) { if (oldStyleSlowScroll) view()->frameView()->removeSlowRepaintObject(); @@ -1593,7 +1678,7 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS } } -void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle*) +void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { if (s_affectsParentBlock) handleDynamicFloatPositionChange(); @@ -1601,9 +1686,19 @@ void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle*) if (!m_parent) return; - if (diff == StyleDifferenceLayout) + if (diff == StyleDifferenceLayout) { + RenderCounter::rendererStyleChanged(this, oldStyle, m_style.get()); + + // If the object already needs layout, then setNeedsLayout won't do + // any work. But if the containing block has changed, then we may need + // to mark the new containing blocks for layout. The change that can + // directly affect the containing block of this object is a change to + // the position style. + if (m_needsLayout && oldStyle->position() != m_style->position()) + markContainingBlocksForLayout(); + setNeedsLayoutAndPrefWidthsRecalc(); - else if (diff == StyleDifferenceLayoutPositionedMovementOnly) + } else if (diff == StyleDifferenceLayoutPositionedMovementOnly) setNeedsPositionedMovementLayout(); // Don't check for repaint here; we need to wait until the layer has been @@ -1612,15 +1707,20 @@ void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle*) void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers) { - // FIXME: This will be slow when a large number of images is used. Fix by using a dict. - for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) { - if (currOld->image() && (!newLayers || !newLayers->containsImage(currOld->image()))) - currOld->image()->removeClient(this); - } + // Optimize the common case + if (oldLayers && !oldLayers->next() && newLayers && !newLayers->next() && (oldLayers->image() == newLayers->image())) + return; + + // Go through the new layers and addClients first, to avoid removing all clients of an image. for (const FillLayer* currNew = newLayers; currNew; currNew = currNew->next()) { - if (currNew->image() && (!oldLayers || !oldLayers->containsImage(currNew->image()))) + if (currNew->image()) currNew->image()->addClient(this); } + + for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) { + if (currOld->image()) + currOld->image()->removeClient(this); + } } void RenderObject::updateImage(StyleImage* oldImage, StyleImage* newImage) @@ -1739,6 +1839,23 @@ IntSize RenderObject::offsetFromContainer(RenderObject* o) const return offset; } +IntSize RenderObject::offsetFromAncestorContainer(RenderObject* container) const +{ + IntSize offset; + const RenderObject* currContainer = this; + do { + RenderObject* nextContainer = currContainer->container(); + ASSERT(nextContainer); // This means we reached the top without finding container. + if (!nextContainer) + break; + ASSERT(!currContainer->hasTransform()); + offset += currContainer->offsetFromContainer(nextContainer); + currContainer = nextContainer; + } while (currContainer != container); + + return offset; +} + IntRect RenderObject::localCaretRect(InlineBox*, int, int* extraWidthToEndOfLine) { if (extraWidthToEndOfLine) @@ -1772,8 +1889,11 @@ bool RenderObject::hasOutlineAnnotation() const return node() && node()->isLink() && document()->printing(); } -RenderObject* RenderObject::container() const +RenderObject* RenderObject::container(RenderBoxModelObject* repaintContainer, bool* repaintContainerSkipped) const { + if (repaintContainerSkipped) + *repaintContainerSkipped = false; + // This method is extremely similar to containingBlock(), but with a few notable // exceptions. // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when @@ -1798,14 +1918,20 @@ RenderObject* RenderObject::container() const // we'll just return 0). // FIXME: The definition of view() has changed to not crawl up the render tree. It might // be safe now to use it. - while (o && o->parent() && !(o->hasTransform() && o->isRenderBlock())) + while (o && o->parent() && !(o->hasTransform() && o->isRenderBlock())) { + if (repaintContainerSkipped && o == repaintContainer) + *repaintContainerSkipped = true; o = o->parent(); + } } else if (pos == AbsolutePosition) { // Same goes here. We technically just want our containing block, but // we may not have one if we're part of an uninstalled subtree. We'll // climb as high as we can though. - while (o && o->style()->position() == StaticPosition && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) + while (o && o->style()->position() == StaticPosition && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) { + if (repaintContainerSkipped && o == repaintContainer) + *repaintContainerSkipped = true; o = o->parent(); + } } return o; @@ -1825,7 +1951,12 @@ void RenderObject::destroy() children->destroyLeftoverChildren(); // If this renderer is being autoscrolled, stop the autoscroll timer - if (document()->frame()->eventHandler()->autoscrollRenderer() == this) + + // FIXME: RenderObject::destroy should not get called with a renderer whose document + // has a null frame, so we assert this. However, we don't want release builds to crash which is why we + // check that the frame is not null. + ASSERT(document()->frame()); + if (document()->frame() && document()->frame()->eventHandler()->autoscrollRenderer() == this) document()->frame()->eventHandler()->stopAutoscrollTimer(true); if (m_hasCounterNodeMap) @@ -2322,9 +2453,20 @@ RenderBoxModelObject* RenderObject::offsetParent() const VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity) { - // If this is a non-anonymous renderer, then it's simple. - if (Node* node = this->node()) + // If this is a non-anonymous renderer in an editable area, then it's simple. + if (Node* node = this->node()) { + if (!node->isContentEditable()) { + // If it can be found, we prefer a visually equivalent position that is editable. + Position position(node, offset); + Position candidate = position.downstream(Position::CanCrossEditingBoundary); + if (candidate.node()->isContentEditable()) + return VisiblePosition(candidate, affinity); + candidate = position.upstream(Position::CanCrossEditingBoundary); + if (candidate.node()->isContentEditable()) + return VisiblePosition(candidate, affinity); + } 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 @@ -2372,6 +2514,11 @@ VisiblePosition RenderObject::createVisiblePosition(const Position& position) } #if ENABLE(SVG) +const SVGRenderBase* RenderObject::toSVGRenderBase() const +{ + ASSERT_NOT_REACHED(); + return 0; +} FloatRect RenderObject::objectBoundingBox() const { @@ -2387,25 +2534,16 @@ FloatRect RenderObject::repaintRectInLocalCoordinates() const return FloatRect(); } -TransformationMatrix RenderObject::localTransform() const -{ - return TransformationMatrix(); -} - -TransformationMatrix RenderObject::localToParentTransform() const +AffineTransform RenderObject::localTransform() const { - // FIXME: This double virtual call indirection is temporary until I can land the - // rest of the of the localToParentTransform() support for SVG. - return localTransform(); + static const AffineTransform identity; + return identity; } -TransformationMatrix RenderObject::absoluteTransform() const +const AffineTransform& RenderObject::localToParentTransform() const { - // FIXME: This should use localToParentTransform(), but much of the SVG code - // depends on RenderBox::absoluteTransform() being the sum of the localTransform()s of all parent renderers. - if (parent()) - return localTransform() * parent()->absoluteTransform(); - return localTransform(); + static const AffineTransform identity; + return identity; } bool RenderObject::nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint&, HitTestAction) @@ -2426,4 +2564,19 @@ void showTree(const WebCore::RenderObject* ro) ro->showTreeForThis(); } +void showRenderTree(const WebCore::RenderObject* object1) +{ + showRenderTree(object1, 0); +} + +void showRenderTree(const WebCore::RenderObject* object1, const WebCore::RenderObject* object2) +{ + if (object1) { + const WebCore::RenderObject* root = object1; + while (root->parent()) + root = root->parent(); + root->showRenderTreeAndMark(object1, "*", object2, "-", 0); + } +} + #endif diff --git a/WebCore/rendering/RenderObject.h b/WebCore/rendering/RenderObject.h index 911169d..6764818 100644 --- a/WebCore/rendering/RenderObject.h +++ b/WebCore/rendering/RenderObject.h @@ -4,6 +4,7 @@ * (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. + * Copyright (C) 2009 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,6 +26,7 @@ #ifndef RenderObject_h #define RenderObject_h +#include "AffineTransform.h" #include "CachedResourceClient.h" #include "Document.h" #include "Element.h" @@ -51,6 +53,9 @@ class RenderLayer; class RenderTheme; class TransformState; class VisiblePosition; +#if ENABLE(SVG) +class SVGRenderBase; +#endif /* * The painting of a layer occurs in three distinct phases. Each phase involves @@ -76,11 +81,13 @@ enum PaintPhase { PaintPhaseMask }; -enum PaintRestriction { - PaintRestrictionNone, - PaintRestrictionSelectionOnly, - PaintRestrictionSelectionOnlyBlackText +enum PaintBehaviorFlags { + PaintBehaviorNormal = 0, + PaintBehaviorSelectionOnly = 1 << 0, + PaintBehaviorForceBlackText = 1 << 1, + PaintBehaviorFlattenCompositingLayers = 1 << 2 }; +typedef unsigned PaintBehavior; enum HitTestFilter { HitTestAll, @@ -228,6 +235,12 @@ private: public: #ifndef NDEBUG void showTreeForThis() const; + + void showRenderObject() const; + // We don't make printedCharacters an optional parameter so that + // showRenderObject can be called from gdb easily. + void showRenderObject(int printedCharacters) const; + void showRenderTreeAndMark(const RenderObject* markedObject1 = 0, const char* markedLabel1 = 0, const RenderObject* markedObject2 = 0, const char* markedLabel2 = 0, int depth = 0) const; #endif static RenderObject* createObject(Node*, RenderStyle*); @@ -251,7 +264,9 @@ public: virtual bool isBlockFlow() const { return false; } virtual bool isBoxModelObject() const { return false; } virtual bool isCounter() const { return false; } + virtual bool isEmbeddedObject() const { return false; } virtual bool isFieldset() const { return false; } + virtual bool isFileUploadControl() const { return false; } virtual bool isFrame() const { return false; } virtual bool isFrameSet() const { return false; } virtual bool isImage() const { return false; } @@ -267,6 +282,13 @@ public: virtual bool isRenderInline() const { return false; } virtual bool isRenderPart() const { return false; } virtual bool isRenderView() const { return false; } + virtual bool isReplica() const { return false; } +#if ENABLE(RUBY) + virtual bool isRuby() const { return false; } + virtual bool isRubyBase() const { return false; } + virtual bool isRubyRun() const { return false; } + virtual bool isRubyText() const { return false; } +#endif virtual bool isSlider() const { return false; } virtual bool isTable() const { return false; } virtual bool isTableCell() const { return false; } @@ -278,6 +300,7 @@ public: virtual bool isTextField() const { return false; } virtual bool isVideo() const { return false; } virtual bool isWidget() const { return false; } + virtual bool isCanvas() const { return false; } bool isRoot() const { return document()->documentElement() == m_node; } bool isBody() const; @@ -285,6 +308,9 @@ public: bool isHTMLMarquee() const; + inline bool isAfterContent() const; + static inline bool isAfterContent(const RenderObject* obj) { return obj && obj->isAfterContent(); } + bool childrenInline() const { return m_childrenInline; } void setChildrenInline(bool b = true) { m_childrenInline = b; } bool hasColumns() const { return m_hasColumns; } @@ -292,6 +318,10 @@ public: bool cellWidthChanged() const { return m_cellWidthChanged; } void setCellWidthChanged(bool b = true) { m_cellWidthChanged = b; } +#if ENABLE(MATHML) + virtual bool isRenderMathMLBlock() const { return false; } +#endif // ENABLE(MATHML) + #if ENABLE(SVG) // FIXME: Until all SVG renders can be subclasses of RenderSVGModelObject we have // to add SVG renderer methods to RenderObject with an ASSERT_NOT_REACHED() default implementation. @@ -301,6 +331,9 @@ public: virtual bool isRenderPath() const { return false; } virtual bool isSVGText() const { return false; } virtual bool isSVGImage() const { return false; } + virtual bool isSVGForeignObject() const { return false; } + + virtual const SVGRenderBase* toSVGRenderBase() const; // Per SVG 1.1 objectBoundingBox ignores clipping, masking, filter effects, opacity and stroke-width. // This is used for all computation of objectBoundingBox relative units and by SVGLocateable::getBBox(). @@ -317,16 +350,11 @@ public: // FIXME: This accessor is deprecated and mostly around for SVGRenderTreeAsText. // This only returns the transform="" value from the element // most callsites want localToParentTransform() instead. - virtual TransformationMatrix localTransform() const; + virtual AffineTransform localTransform() const; // Returns the full transform mapping from local coordinates to local coords for the parent SVG renderer // This includes any viewport transforms and x/y offsets as well as the transform="" value off the element. - virtual TransformationMatrix localToParentTransform() const; - - // Walks up the parent chain to create a transform which maps from local to document coords - // NOTE: This method is deprecated! It doesn't respect scroll offsets or repaint containers. - // FIXME: This is only virtual so that RenderSVGHiddenContainer can override it to match old LayoutTest results. - virtual TransformationMatrix absoluteTransform() const; + virtual const AffineTransform& localToParentTransform() const; // SVG uses FloatPoint precise hit testing, and passes the point in parent // coordinates instead of in repaint container coordinates. Eventually the @@ -398,11 +426,11 @@ public: bool hasOutlineAnnotation() const; bool hasOutline() const { return style()->hasOutline() || hasOutlineAnnotation(); } - /** - * returns the object containing this one. can be different from parent for - * positioned elements - */ - RenderObject* container() const; + // Returns the object containing this one. Can be different from parent for positioned elements. + // If repaintContainer and repaintContainerSkipped are not null, on return *repaintContainerSkipped + // is true if the renderer returned is an ancestor of repaintContainer. + RenderObject* container(RenderBoxModelObject* repaintContainer = 0, bool* repaintContainerSkipped = 0) const; + virtual RenderObject* hoverAncestor() const { return parent(); } // IE Extension that can be called on any RenderObject. See the implementation for the details. @@ -536,6 +564,8 @@ public: // Return the offset from the container() renderer (excluding transforms) virtual IntSize offsetFromContainer(RenderObject*) const; + // Return the offset from an object up the container() chain. Asserts that none of the intermediate objects have transforms. + IntSize offsetFromAncestorContainer(RenderObject*) const; virtual void absoluteRects(Vector<IntRect>&, int, int) { } // FIXME: useTransforms should go away eventually @@ -544,6 +574,8 @@ public: // Build an array of quads in absolute coords for line boxes virtual void absoluteQuads(Vector<FloatQuad>&) { } + void absoluteFocusRingQuads(Vector<FloatQuad>&); + // the rect that will be painted if this object is passed as the paintingRoot IntRect paintingRootRect(IntRect& topLevelRect); @@ -658,7 +690,9 @@ public: // Whether or not a given block needs to paint selection gaps. virtual bool shouldPaintSelectionGaps() const { return false; } +#if ENABLE(DRAG_SUPPORT) Node* draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const; +#endif /** * Returns the local coordinates of the caret within this render object. @@ -727,16 +761,13 @@ public: bool shouldUseTransformFromContainer(const RenderObject* container) const; void getTransformFromContainer(const RenderObject* container, const IntSize& offsetInContainer, TransformationMatrix&) const; - virtual void addFocusRingRects(GraphicsContext*, int /*tx*/, int /*ty*/) { }; + virtual void addFocusRingRects(Vector<IntRect>&, int /*tx*/, int /*ty*/) { }; IntRect absoluteOutlineBounds() const { 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(StyleDifference, const RenderStyle* newStyle); @@ -845,9 +876,6 @@ private: // from RenderTableCell bool m_cellWidthChanged : 1; - // from RenderReplaced - bool m_replacedHasOverflow : 1; - private: // Store state between styleWillChange and styleDidChange static bool s_affectsParentBlock; @@ -858,6 +886,16 @@ inline bool RenderObject::documentBeingDestroyed() const return !document()->renderer(); } +inline bool RenderObject::isAfterContent() const +{ + if (style()->styleType() != AFTER) + return false; + // Text nodes don't have their own styles, so ignore the style on a text node. + if (isText() && !isBR()) + return false; + return true; +} + inline void RenderObject::setNeedsLayout(bool b, bool markParents) { bool alreadyNeededLayout = m_needsLayout; @@ -974,11 +1012,51 @@ inline void makeMatrixRenderable(TransformationMatrix& matrix, bool has3DRenderi #endif } +inline int adjustForAbsoluteZoom(int value, RenderObject* renderer) +{ + float zoomFactor = renderer->style()->effectiveZoom(); + if (zoomFactor == 1) + return value; + // Needed because computeLengthInt truncates (rather than rounds) when scaling up. + if (zoomFactor > 1) + value++; + return static_cast<int>(value / zoomFactor); +} + +inline void adjustIntRectForAbsoluteZoom(IntRect& rect, RenderObject* renderer) +{ + rect.setX(adjustForAbsoluteZoom(rect.x(), renderer)); + rect.setY(adjustForAbsoluteZoom(rect.y(), renderer)); + rect.setWidth(adjustForAbsoluteZoom(rect.width(), renderer)); + rect.setHeight(adjustForAbsoluteZoom(rect.height(), renderer)); +} + +inline FloatPoint adjustFloatPointForAbsoluteZoom(const FloatPoint& point, RenderObject* renderer) +{ + // The result here is in floats, so we don't need the truncation hack from the integer version above. + float zoomFactor = renderer->style()->effectiveZoom(); + if (zoomFactor == 1) + return point; + return FloatPoint(point.x() / zoomFactor, point.y() / zoomFactor); +} + +inline void adjustFloatQuadForAbsoluteZoom(FloatQuad& quad, RenderObject* renderer) +{ + quad.setP1(adjustFloatPointForAbsoluteZoom(quad.p1(), renderer)); + quad.setP2(adjustFloatPointForAbsoluteZoom(quad.p2(), renderer)); + quad.setP3(adjustFloatPointForAbsoluteZoom(quad.p3(), renderer)); + quad.setP4(adjustFloatPointForAbsoluteZoom(quad.p4(), renderer)); +} + } // namespace WebCore #ifndef NDEBUG // Outside the WebCore namespace for ease of invocation from gdb. void showTree(const WebCore::RenderObject*); +void showRenderTree(const WebCore::RenderObject* object1); +// We don't make object2 an optional parameter so that showRenderTree +// can be called from gdb easily. +void showRenderTree(const WebCore::RenderObject* object1, const WebCore::RenderObject* object2); #endif #endif // RenderObject_h diff --git a/WebCore/rendering/RenderObjectChildList.cpp b/WebCore/rendering/RenderObjectChildList.cpp index 23ab98f..d56a015 100644 --- a/WebCore/rendering/RenderObjectChildList.cpp +++ b/WebCore/rendering/RenderObjectChildList.cpp @@ -271,24 +271,29 @@ static RenderObject* findBeforeAfterParent(RenderObject* object) return beforeAfterParent; } -static void invalidateCountersInContainer(RenderObject* container) +static void invalidateCountersInContainer(RenderObject* container, const AtomicString& identifier) { if (!container) return; container = findBeforeAfterParent(container); if (!container) return; + // Sometimes the counter is attached directly on the container. + if (container->isCounter()) { + toRenderCounter(container)->invalidate(identifier); + return; + } for (RenderObject* content = container->firstChild(); content; content = content->nextSibling()) { if (content->isCounter()) - toRenderCounter(content)->invalidate(); + toRenderCounter(content)->invalidate(identifier); } } -void RenderObjectChildList::invalidateCounters(RenderObject* owner) +void RenderObjectChildList::invalidateCounters(RenderObject* owner, const AtomicString& identifier) { ASSERT(!owner->documentBeingDestroyed()); - invalidateCountersInContainer(beforeAfterContainer(owner, BEFORE)); - invalidateCountersInContainer(beforeAfterContainer(owner, AFTER)); + invalidateCountersInContainer(beforeAfterContainer(owner, BEFORE), identifier); + invalidateCountersInContainer(beforeAfterContainer(owner, AFTER), identifier); } void RenderObjectChildList::updateBeforeAfterContent(RenderObject* owner, PseudoId type, RenderObject* styledObject) @@ -369,9 +374,11 @@ void RenderObjectChildList::updateBeforeAfterContent(RenderObject* owner, 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); + } else { + // RenderListItem may insert a list marker here. We do not need to care about this case. + // Otherwise, genChild must be a first-letter container. updateFirstLetter() will take care of it. + ASSERT(genChild->isListMarker() || genChild->style()->styleType() == FIRST_LETTER); + } } } return; // We've updated the generated content. That's all we needed to do. diff --git a/WebCore/rendering/RenderObjectChildList.h b/WebCore/rendering/RenderObjectChildList.h index bf8800a..ba73c50 100644 --- a/WebCore/rendering/RenderObjectChildList.h +++ b/WebCore/rendering/RenderObjectChildList.h @@ -30,6 +30,7 @@ namespace WebCore { +class AtomicString; class RenderObject; class RenderObjectChildList { @@ -55,7 +56,7 @@ public: void insertChildNode(RenderObject* owner, RenderObject* child, RenderObject* before, bool fullInsert = true); void updateBeforeAfterContent(RenderObject* owner, PseudoId type, RenderObject* styledObject = 0); - void invalidateCounters(RenderObject* owner); + void invalidateCounters(RenderObject* owner, const AtomicString& identifier); private: RenderObject* m_firstChild; diff --git a/WebCore/rendering/RenderOverflow.h b/WebCore/rendering/RenderOverflow.h new file mode 100644 index 0000000..253a672 --- /dev/null +++ b/WebCore/rendering/RenderOverflow.h @@ -0,0 +1,153 @@ +/* + * 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 RenderOverflow_h +#define RenderOverflow_h + +#include "IntRect.h" + +namespace WebCore +{ +// RenderOverflow is a class for tracking content that spills out of a box. This class is used by RenderBox and +// InlineFlowBox. +// +// There are two types of overflow: layout overflow (which is expected to be reachable via scrolling mechanisms) and +// visual overflow (which is not expected to be reachable via scrolling mechanisms). +// +// Layout overflow examples include other boxes that spill out of our box, For example, in the inline case a tall image +// could spill out of a line box. + +// Examples of visual overflow are shadows, text stroke (and eventually outline and border-image). + +// This object is allocated only when some of these fields have non-default values in the owning box. +class RenderOverflow : public Noncopyable { +public: + RenderOverflow(const IntRect& defaultRect = IntRect()) + : m_topLayoutOverflow(defaultRect.y()) + , m_bottomLayoutOverflow(defaultRect.bottom()) + , m_leftLayoutOverflow(defaultRect.x()) + , m_rightLayoutOverflow(defaultRect.right()) + , m_topVisualOverflow(defaultRect.y()) + , m_bottomVisualOverflow(defaultRect.bottom()) + , m_leftVisualOverflow(defaultRect.x()) + , m_rightVisualOverflow(defaultRect.right()) + { + } + + int topLayoutOverflow() const { return m_topLayoutOverflow; } + int bottomLayoutOverflow() const { return m_bottomLayoutOverflow; } + int leftLayoutOverflow() const { return m_leftLayoutOverflow; } + int rightLayoutOverflow() const { return m_rightLayoutOverflow; } + IntRect layoutOverflowRect() const; + + int topVisualOverflow() const { return m_topVisualOverflow; } + int bottomVisualOverflow() const { return m_bottomVisualOverflow; } + int leftVisualOverflow() const { return m_leftVisualOverflow; } + int rightVisualOverflow() const { return m_rightVisualOverflow; } + IntRect visualOverflowRect() const; + + IntRect visibleOverflowRect() const; + + void setTopLayoutOverflow(int overflow) { m_topLayoutOverflow = overflow; } + void setBottomLayoutOverflow(int overflow) { m_bottomLayoutOverflow = overflow; } + void setLeftLayoutOverflow(int overflow) { m_leftLayoutOverflow = overflow; } + void setRightLayoutOverflow(int overflow) { m_rightLayoutOverflow = overflow; } + + void setTopVisualOverflow(int overflow) { m_topVisualOverflow = overflow; } + void setBottomVisualOverflow(int overflow) { m_bottomVisualOverflow = overflow; } + void setLeftVisualOverflow(int overflow) { m_leftVisualOverflow = overflow; } + void setRightVisualOverflow(int overflow) { m_rightVisualOverflow = overflow; } + + void move(int dx, int dy); + + void addLayoutOverflow(const IntRect&); + void addVisualOverflow(const IntRect&); + + void resetLayoutOverflow(const IntRect& defaultRect); + +private: + int m_topLayoutOverflow; + int m_bottomLayoutOverflow; + int m_leftLayoutOverflow; + int m_rightLayoutOverflow; + + int m_topVisualOverflow; + int m_bottomVisualOverflow; + int m_leftVisualOverflow; + int m_rightVisualOverflow; +}; + +inline IntRect RenderOverflow::layoutOverflowRect() const +{ + return IntRect(m_leftLayoutOverflow, m_topLayoutOverflow, m_rightLayoutOverflow - m_leftLayoutOverflow, m_bottomLayoutOverflow - m_topLayoutOverflow); +} + +inline IntRect RenderOverflow::visualOverflowRect() const +{ + return IntRect(m_leftVisualOverflow, m_topVisualOverflow, m_rightVisualOverflow - m_leftVisualOverflow, m_bottomVisualOverflow - m_topVisualOverflow); +} + +inline IntRect RenderOverflow::visibleOverflowRect() const +{ + IntRect combinedRect(layoutOverflowRect()); + combinedRect.unite(visualOverflowRect()); + return combinedRect; +} + +inline void RenderOverflow::move(int dx, int dy) +{ + m_topLayoutOverflow += dy; + m_bottomLayoutOverflow += dy; + m_leftLayoutOverflow += dx; + m_rightLayoutOverflow += dx; + + m_topVisualOverflow += dy; + m_bottomVisualOverflow += dy; + m_leftVisualOverflow += dx; + m_rightVisualOverflow += dx; +} + +inline void RenderOverflow::addLayoutOverflow(const IntRect& rect) +{ + m_topLayoutOverflow = std::min(rect.y(), m_topLayoutOverflow); + m_bottomLayoutOverflow = std::max(rect.bottom(), m_bottomLayoutOverflow); + m_leftLayoutOverflow = std::min(rect.x(), m_leftLayoutOverflow); + m_rightLayoutOverflow = std::max(rect.right(), m_rightLayoutOverflow); +} + +inline void RenderOverflow::addVisualOverflow(const IntRect& rect) +{ + m_topVisualOverflow = std::min(rect.y(), m_topVisualOverflow); + m_bottomVisualOverflow = std::max(rect.bottom(), m_bottomVisualOverflow); + m_leftVisualOverflow = std::min(rect.x(), m_leftVisualOverflow); + m_rightVisualOverflow = std::max(rect.right(), m_rightVisualOverflow); +} + +inline void RenderOverflow::resetLayoutOverflow(const IntRect& rect) +{ + m_topLayoutOverflow = rect.y(); + m_bottomLayoutOverflow = rect.bottom(); + m_leftLayoutOverflow = rect.x(); + m_rightLayoutOverflow = rect.right(); +} + +} // namespace WebCore + +#endif // RenderOverflow_h diff --git a/WebCore/rendering/RenderPart.cpp b/WebCore/rendering/RenderPart.cpp index cb56c0c..5c4a6ec 100644 --- a/WebCore/rendering/RenderPart.cpp +++ b/WebCore/rendering/RenderPart.cpp @@ -31,6 +31,7 @@ namespace WebCore { RenderPart::RenderPart(Element* node) : RenderWidget(node) + , m_hasFallbackContent(false) { // init RenderObject attributes setInline(false); diff --git a/WebCore/rendering/RenderPart.h b/WebCore/rendering/RenderPart.h index 08abf99..8303543 100644 --- a/WebCore/rendering/RenderPart.h +++ b/WebCore/rendering/RenderPart.h @@ -27,6 +27,10 @@ namespace WebCore { +// Renderer for frames via RenderPartObject, and plug-ins via RenderEmbeddedObject. + +// FIXME: This class is subclassed in RenderPartObject for iframes, which is in turn +// subclassed in RenderEmbeddedObject for object and embed. This class itself could be removed. class RenderPart : public RenderWidget { public: RenderPart(Element*); diff --git a/WebCore/rendering/RenderPartObject.cpp b/WebCore/rendering/RenderPartObject.cpp index 72298c6..617c8bf 100644 --- a/WebCore/rendering/RenderPartObject.cpp +++ b/WebCore/rendering/RenderPartObject.cpp @@ -33,8 +33,8 @@ #include "HTMLParamElement.h" #include "MIMETypeRegistry.h" #include "Page.h" -#include "PluginData.h" #include "RenderView.h" +#include "RenderWidgetProtector.h" #include "Text.h" #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) @@ -48,300 +48,6 @@ using namespace HTMLNames; RenderPartObject::RenderPartObject(Element* element) : RenderPart(element) { - // init RenderObject attributes - setInline(true); - m_hasFallbackContent = false; - - if (element->hasTagName(embedTag) || element->hasTagName(objectTag)) - view()->frameView()->setIsVisuallyNonEmpty(); -} - -RenderPartObject::~RenderPartObject() -{ - if (frameView()) - frameView()->removeWidgetToUpdate(this); -} - -static bool isURLAllowed(Document* doc, const String& url) -{ - if (doc->frame()->page()->frameCount() >= 200) - return false; - - // We allow one level of self-reference because some sites depend on that. - // But we don't allow more than one. - KURL completeURL = doc->completeURL(url); - bool foundSelfReference = false; - for (Frame* frame = doc->frame(); frame; frame = frame->tree()->parent()) { - if (equalIgnoringFragmentIdentifier(frame->loader()->url(), completeURL)) { - if (foundSelfReference) - return false; - foundSelfReference = true; - } - } - return true; -} - -typedef HashMap<String, String, CaseFoldingHash> ClassIdToTypeMap; - -static ClassIdToTypeMap* createClassIdToTypeMap() -{ - ClassIdToTypeMap* map = new ClassIdToTypeMap; - map->add("clsid:D27CDB6E-AE6D-11CF-96B8-444553540000", "application/x-shockwave-flash"); - map->add("clsid:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA", "audio/x-pn-realaudio-plugin"); - map->add("clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B", "video/quicktime"); - map->add("clsid:166B1BCA-3F9C-11CF-8075-444553540000", "application/x-director"); -#if ENABLE(ACTIVEX_TYPE_CONVERSION_WMPLAYER) - map->add("clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6", "application/x-mplayer2"); - map->add("clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95", "application/x-mplayer2"); -#endif - return map; -} - -static const String& activeXType() -{ - DEFINE_STATIC_LOCAL(String, activeXType, ("application/x-oleobject")); - return activeXType; -} - -static inline bool havePlugin(const PluginData* pluginData, const String& type) -{ - return pluginData && !type.isEmpty() && pluginData->supportsMimeType(type); -} - -static String serviceTypeForClassId(const String& classId, const PluginData* pluginData) -{ - // Return early if classId is empty (since we won't do anything below). - // Furthermore, if classId is null, calling get() below will crash. - if (classId.isEmpty()) - return String(); - - static ClassIdToTypeMap* map = createClassIdToTypeMap(); - String type = map->get(classId); - - // If we do have a plug-in that supports generic ActiveX content and don't have a plug-in - // for the MIME type we came up with, ignore the MIME type we came up with and just use - // the ActiveX type. - if (havePlugin(pluginData, activeXType()) && !havePlugin(pluginData, type)) - return activeXType(); - - return type; -} - -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 -} - -static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues) -{ - // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP - // require "src" attribute). - int srcIndex = -1, dataIndex = -1; - for (unsigned int i = 0; i < paramNames->size(); ++i) { - if (equalIgnoringCase((*paramNames)[i], "src")) - srcIndex = i; - else if (equalIgnoringCase((*paramNames)[i], "data")) - dataIndex = i; - } - - if (srcIndex == -1 && dataIndex != -1) { - paramNames->append("src"); - paramValues->append((*paramValues)[dataIndex]); - } -} - -void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins) -{ - String url; - String serviceType; - Vector<String> paramNames; - Vector<String> paramValues; - Frame* frame = frameView()->frame(); - - if (node()->hasTagName(objectTag)) { - HTMLObjectElement* o = static_cast<HTMLObjectElement*>(node()); - - o->setNeedWidgetUpdate(false); - if (!o->isFinishedParsingChildren()) - return; - - // Check for a child EMBED tag. - HTMLEmbedElement* embed = 0; - const PluginData* pluginData = frame->page()->pluginData(); - if (shouldUseEmbedDescendant(o, pluginData)) { - for (Node* child = o->firstChild(); child; ) { - if (child->hasTagName(embedTag)) { - embed = static_cast<HTMLEmbedElement*>(child); - break; - } else if (child->hasTagName(objectTag)) - child = child->nextSibling(); // Don't descend into nested OBJECT tags - else - child = child->traverseNextNode(o); // Otherwise descend (EMBEDs may be inside COMMENT tags) - } - } - - // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT. - HTMLElement *embedOrObject; - if (embed) { - embedOrObject = (HTMLElement *)embed; - url = embed->url(); - serviceType = embed->serviceType(); - } else - embedOrObject = (HTMLElement *)o; - - // If there was no URL or type defined in EMBED, try the OBJECT tag. - if (url.isEmpty()) - url = o->url(); - if (serviceType.isEmpty()) - serviceType = o->serviceType(); - - HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; - - // Scan the PARAM children. - // Get the URL and type from the params if we don't already have them. - // Get the attributes from the params if there is no EMBED tag. - Node *child = o->firstChild(); - while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) { - if (child->hasTagName(paramTag)) { - HTMLParamElement* p = static_cast<HTMLParamElement*>(child); - String name = p->name(); - if (url.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) - url = p->value(); - if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { - serviceType = p->value(); - int pos = serviceType.find(";"); - if (pos != -1) - serviceType = serviceType.left(pos); - } - if (!embed && !name.isEmpty()) { - uniqueParamNames.add(name.impl()); - paramNames.append(p->name()); - paramValues.append(p->value()); - } - } - child = child->nextSibling(); - } - - // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag - // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is - // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means - // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, - // else our Java plugin will misinterpret it. [4004531] - String codebase; - if (!embed && MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { - codebase = "codebase"; - uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already - } - - // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values. - NamedNodeMap* attributes = embedOrObject->attributes(); - if (attributes) { - for (unsigned i = 0; i < attributes->length(); ++i) { - Attribute* it = attributes->attributeItem(i); - const AtomicString& name = it->name().localName(); - if (embed || !uniqueParamNames.contains(name.impl())) { - paramNames.append(name.string()); - paramValues.append(it->value().string()); - } - } - } - - mapDataParamToSrc(¶mNames, ¶mValues); - - // If we still don't have a type, try to map from a specific CLASSID to a type. - if (serviceType.isEmpty()) - serviceType = serviceTypeForClassId(o->classId(), pluginData); - - if (!isURLAllowed(document(), url)) - return; - - // Find out if we support fallback content. - m_hasFallbackContent = false; - for (Node *child = o->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) { - if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) || // Discount <embed> and <param> - (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace())) - m_hasFallbackContent = true; - } - - if (onlyCreateNonNetscapePlugins) { - KURL completedURL; - if (!url.isEmpty()) - completedURL = frame->loader()->completeURL(url); - - if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) - return; - } - - bool success = frame->loader()->requestObject(this, url, AtomicString(o->name()), serviceType, paramNames, paramValues); - if (!success && m_hasFallbackContent) - o->renderFallbackContent(); - } else if (node()->hasTagName(embedTag)) { - HTMLEmbedElement *o = static_cast<HTMLEmbedElement*>(node()); - o->setNeedWidgetUpdate(false); - url = o->url(); - serviceType = o->serviceType(); - - if (url.isEmpty() && serviceType.isEmpty()) - return; - if (!isURLAllowed(document(), url)) - return; - - // add all attributes set on the embed object - NamedNodeMap* a = o->attributes(); - if (a) { - for (unsigned i = 0; i < a->length(); ++i) { - Attribute* it = a->attributeItem(i); - paramNames.append(it->name().localName().string()); - paramValues.append(it->value().string()); - } - } - - if (onlyCreateNonNetscapePlugins) { - KURL completedURL; - if (!url.isEmpty()) - completedURL = frame->loader()->completeURL(url); - - if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) - return; - - } - - 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() @@ -351,10 +57,15 @@ void RenderPartObject::layout() #ifdef FLATTEN_IFRAME RenderPart::calcWidth(); RenderPart::calcHeight(); + // Calculate the styled dimensions by subtracting the border and padding. + int extraWidth = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + int extraHeight = paddingTop() + paddingBottom() + borderTop() + borderBottom(); + int styleWidth = width() - extraWidth; + int styleHeight = height() - extraHeight; // Some IFrames have a width and/or height of 1 when they are meant to be // hidden. If that is the case, do not try to expand. - if (node()->hasTagName(iframeTag) && widget() && widget()->isFrameView() - && width() > 1 && height() > 1) { + if (node()->hasTagName(iframeTag) && widget() && widget()->isFrameView() && + styleWidth > 1 && styleHeight > 1) { HTMLIFrameElement* element = static_cast<HTMLIFrameElement*>(node()); bool scrolling = element->scrollingMode() != ScrollbarAlwaysOff; bool widthIsFixed = style()->width().isFixed(); @@ -365,15 +76,11 @@ void RenderPartObject::layout() if (scrolling || !widthIsFixed || !heightIsFixed) { FrameView* view = static_cast<FrameView*>(widget()); RenderView* root = view ? view->frame()->contentRenderer() : NULL; - RenderPart* owner = view->frame()->ownerRenderer(); - if (root && style()->visibility() != HIDDEN - && (!owner || owner->style()->visibility() != HIDDEN)) { + if (root && style()->visibility() != HIDDEN) { // Update the dimensions to get the correct minimum preferred // width updateWidgetPosition(); - int extraWidth = paddingLeft() + paddingRight() + borderLeft() + borderRight(); - int extraHeight = paddingTop() + paddingBottom() + borderTop() + borderBottom(); // Use the preferred width if it is larger and only if // scrollbars are visible or the width style is not fixed. if (scrolling || !widthIsFixed) @@ -406,6 +113,10 @@ void RenderPartObject::layout() // Update one last time updateWidgetPosition(); + // Layout one more time to ensure all objects have the correct + // height. + view->layout(); + #if !ASSERT_DISABLED ASSERT(!view->layoutPending()); ASSERT(!root->needsLayout()); @@ -415,9 +126,6 @@ void RenderPartObject::layout() ASSERT(!c->needsLayout()); c = c->nextInPreOrder(); } - Node* body = document()->body(); - if (body) - ASSERT(!body->renderer()->needsLayout()); #endif } } @@ -427,12 +135,10 @@ void RenderPartObject::layout() calcHeight(); #endif - adjustOverflowForBoxShadowAndReflect(); - RenderPart::layout(); - if (!widget() && frameView()) - frameView()->addWidgetToUpdate(this); + m_overflow.clear(); + addShadowOverflow(); setNeedsLayout(false); } diff --git a/WebCore/rendering/RenderPartObject.h b/WebCore/rendering/RenderPartObject.h index 092395d..1175a31 100644 --- a/WebCore/rendering/RenderPartObject.h +++ b/WebCore/rendering/RenderPartObject.h @@ -27,20 +27,20 @@ namespace WebCore { +// Renderer for iframes. Is subclassed in RenderEmbeddedObject for object and embed. class RenderPartObject : public RenderPart { public: RenderPartObject(Element*); - virtual ~RenderPartObject(); - - void updateWidget(bool onlyCreateNonNetscapePlugins); - -private: - virtual const char* renderName() const { return "RenderPartObject"; } #ifdef FLATTEN_IFRAME +protected: virtual void calcWidth(); virtual void calcHeight(); #endif + +private: + virtual const char* renderName() const { return "RenderPartObject"; } + virtual void layout(); virtual void viewCleared(); diff --git a/WebCore/rendering/RenderPath.cpp b/WebCore/rendering/RenderPath.cpp index 4a7662f..7dbde42 100644 --- a/WebCore/rendering/RenderPath.cpp +++ b/WebCore/rendering/RenderPath.cpp @@ -3,6 +3,7 @@ 2004, 2005, 2008 Rob Buis <buis@kde.org> 2005, 2007 Eric Seidel <eric@webkit.org> 2009 Google, Inc. + 2009 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -68,12 +69,12 @@ RenderPath::RenderPath(SVGStyledTransformableElement* node) { } -TransformationMatrix RenderPath::localToParentTransform() const +const AffineTransform& RenderPath::localToParentTransform() const { return m_localTransform; } -TransformationMatrix RenderPath::localTransform() const +AffineTransform RenderPath::localTransform() const { return m_localTransform; } @@ -112,6 +113,34 @@ FloatRect RenderPath::objectBoundingBox() const return m_cachedLocalFillBBox; } +FloatRect RenderPath::strokeBoundingBox() const +{ + if (m_path.isEmpty()) + return FloatRect(); + + if (!m_cachedLocalStrokeBBox.isEmpty()) + return m_cachedLocalStrokeBBox; + + m_cachedLocalStrokeBBox = objectBoundingBox(); + if (style()->svgStyle()->hasStroke()) { + BoundingRectStrokeStyleApplier strokeStyle(this, style()); + m_cachedLocalStrokeBBox.unite(m_path.strokeBoundingRect(&strokeStyle)); + } + + return m_cachedLocalStrokeBBox; +} + +FloatRect RenderPath::markerBoundingBox() const +{ + if (m_path.isEmpty()) + return FloatRect(); + + if (m_cachedLocalMarkerBBox.isEmpty()) + calculateMarkerBoundsIfNeeded(); + + return m_cachedLocalMarkerBBox; +} + FloatRect RenderPath::repaintRectInLocalCoordinates() const { if (m_path.isEmpty()) @@ -121,16 +150,25 @@ FloatRect RenderPath::repaintRectInLocalCoordinates() const if (!m_cachedLocalRepaintRect.isEmpty()) return m_cachedLocalRepaintRect; - if (!style()->svgStyle()->hasStroke()) - m_cachedLocalRepaintRect = objectBoundingBox(); + // FIXME: We need to be careful here. We assume that there is no filter, + // clipper, marker or masker if the rects are empty. + FloatRect rect = filterBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect = rect; else { - BoundingRectStrokeStyleApplier strokeStyle(this, style()); - m_cachedLocalRepaintRect = m_path.strokeBoundingRect(&strokeStyle); + m_cachedLocalRepaintRect = strokeBoundingBox(); + m_cachedLocalRepaintRect.unite(markerBoundingBox()); } - // Markers and filters can paint outside of the stroke path - m_cachedLocalRepaintRect.unite(m_markerBounds); - m_cachedLocalRepaintRect.unite(filterBoundingBoxForRenderer(this)); + rect = clipperBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect.intersect(rect); + + rect = maskerBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect.intersect(rect); + + style()->svgStyle()->inflateForShadow(m_cachedLocalRepaintRect); return m_cachedLocalRepaintRect; } @@ -139,12 +177,9 @@ void RenderPath::setPath(const Path& newPath) { m_path = newPath; m_cachedLocalRepaintRect = FloatRect(); + m_cachedLocalStrokeBBox = FloatRect(); m_cachedLocalFillBBox = FloatRect(); -} - -const Path& RenderPath::path() const -{ - return m_path; + m_cachedLocalMarkerBBox = FloatRect(); } void RenderPath::layout() @@ -180,39 +215,48 @@ void RenderPath::paint(PaintInfo& paintInfo, int, int) { if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty()) return; - - paintInfo.context->save(); - paintInfo.context->concatCTM(localToParentTransform()); - - SVGResourceFilter* filter = 0; FloatRect boundingBox = repaintRectInLocalCoordinates(); - if (paintInfo.phase == PaintPhaseForeground) { - PaintInfo savedInfo(paintInfo); + FloatRect nonLocalBoundingBox = m_localTransform.mapRect(boundingBox); + // FIXME: The empty rect check is to deal with incorrect initial clip in renderSubtreeToImage + // unfortunately fixing that problem is fairly complex unless we were willing to just futz the + // rect to something "close enough" + if (!nonLocalBoundingBox.intersects(paintInfo.rect) && !paintInfo.rect.isEmpty()) + return; + + PaintInfo childPaintInfo(paintInfo); + childPaintInfo.context->save(); + applyTransformToPaintInfo(childPaintInfo, m_localTransform); + SVGResourceFilter* filter = 0; - prepareToRenderSVGContent(this, paintInfo, boundingBox, filter); - if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES) - paintInfo.context->setShouldAntialias(false); - fillAndStrokePath(m_path, paintInfo.context, style(), this); + if (childPaintInfo.phase == PaintPhaseForeground) { + PaintInfo savedInfo(childPaintInfo); - if (static_cast<SVGStyledElement*>(node())->supportsMarkers()) - m_markerBounds = drawMarkersIfNeeded(paintInfo.context, paintInfo.rect, m_path); + if (prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter)) { + if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES) + childPaintInfo.context->setShouldAntialias(false); + fillAndStrokePath(m_path, childPaintInfo.context, style(), this); - finishRenderSVGContent(this, paintInfo, filter, savedInfo.context); + if (static_cast<SVGStyledElement*>(node())->supportsMarkers()) + m_markerLayoutInfo.drawMarkers(childPaintInfo); + } + finishRenderSVGContent(this, childPaintInfo, filter, savedInfo.context); } - if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth()) - paintOutline(paintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()), + if ((childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth()) + paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()), static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()), style()); - paintInfo.context->restore(); + childPaintInfo.context->restore(); } // This method is called from inside paintOutline() since we call paintOutline() // while transformed to our coord system, return local coords -void RenderPath::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +void RenderPath::addFocusRingRects(Vector<IntRect>& rects, int, int) { - graphicsContext->addFocusRingRect(enclosingIntRect(repaintRectInLocalCoordinates())); + IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates()); + if (!rect.isEmpty()) + rects.append(rect); } bool RenderPath::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) @@ -221,7 +265,7 @@ bool RenderPath::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, if (hitTestAction != HitTestForeground) return false; - FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent); + FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent); PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, style()->pointerEvents()); @@ -237,143 +281,27 @@ bool RenderPath::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, return false; } -enum MarkerType { - Start, - Mid, - End -}; - -struct MarkerData { - FloatPoint origin; - FloatPoint subpathStart; - double strokeWidth; - FloatPoint inslopePoints[2]; - FloatPoint outslopePoints[2]; - MarkerType type; - SVGResourceMarker* marker; -}; - -struct DrawMarkersData { - DrawMarkersData(GraphicsContext*, SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, double strokeWidth); - GraphicsContext* context; - int elementIndex; - MarkerData previousMarkerData; - SVGResourceMarker* midMarker; -}; - -DrawMarkersData::DrawMarkersData(GraphicsContext* c, SVGResourceMarker *start, SVGResourceMarker *mid, double strokeWidth) - : context(c) - , elementIndex(0) - , midMarker(mid) -{ - previousMarkerData.origin = FloatPoint(); - previousMarkerData.subpathStart = FloatPoint(); - previousMarkerData.strokeWidth = strokeWidth; - previousMarkerData.marker = start; - previousMarkerData.type = Start; -} - -static void drawMarkerWithData(GraphicsContext* context, MarkerData &data) -{ - if (!data.marker) - return; - - FloatPoint inslopeChange = data.inslopePoints[1] - FloatSize(data.inslopePoints[0].x(), data.inslopePoints[0].y()); - FloatPoint outslopeChange = data.outslopePoints[1] - FloatSize(data.outslopePoints[0].x(), data.outslopePoints[0].y()); - - double inslope = rad2deg(atan2(inslopeChange.y(), inslopeChange.x())); - double outslope = rad2deg(atan2(outslopeChange.y(), outslopeChange.x())); - - double angle = 0.0; - switch (data.type) { - case Start: - angle = outslope; - break; - case Mid: - angle = (inslope + outslope) / 2; - break; - case End: - angle = inslope; - } - - data.marker->draw(context, FloatRect(), data.origin.x(), data.origin.y(), data.strokeWidth, angle); -} - -static inline void updateMarkerDataForElement(MarkerData& previousMarkerData, const PathElement* element) -{ - FloatPoint* points = element->points; - - switch (element->type) { - case PathElementAddQuadCurveToPoint: - // TODO - previousMarkerData.origin = points[1]; - break; - case PathElementAddCurveToPoint: - previousMarkerData.inslopePoints[0] = points[1]; - previousMarkerData.inslopePoints[1] = points[2]; - previousMarkerData.origin = points[2]; - break; - case PathElementMoveToPoint: - previousMarkerData.subpathStart = points[0]; - case PathElementAddLineToPoint: - previousMarkerData.inslopePoints[0] = previousMarkerData.origin; - previousMarkerData.inslopePoints[1] = points[0]; - previousMarkerData.origin = points[0]; - break; - case PathElementCloseSubpath: - previousMarkerData.inslopePoints[0] = previousMarkerData.origin; - previousMarkerData.inslopePoints[1] = points[0]; - previousMarkerData.origin = previousMarkerData.subpathStart; - previousMarkerData.subpathStart = FloatPoint(); - } -} - -static void drawStartAndMidMarkers(void* info, const PathElement* element) -{ - DrawMarkersData& data = *reinterpret_cast<DrawMarkersData*>(info); - - int elementIndex = data.elementIndex; - MarkerData& previousMarkerData = data.previousMarkerData; - - FloatPoint* points = element->points; - - // First update the outslope for the previous element - previousMarkerData.outslopePoints[0] = previousMarkerData.origin; - previousMarkerData.outslopePoints[1] = points[0]; - - // Draw the marker for the previous element - if (elementIndex != 0) - drawMarkerWithData(data.context, previousMarkerData); - - // Update our marker data for this element - updateMarkerDataForElement(previousMarkerData, element); - - if (elementIndex == 1) { - // After drawing the start marker, switch to drawing mid markers - previousMarkerData.marker = data.midMarker; - previousMarkerData.type = Mid; - } - - data.elementIndex++; -} - -FloatRect RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatRect&, const Path& path) const +void RenderPath::calculateMarkerBoundsIfNeeded() const { Document* doc = document(); SVGElement* svgElement = static_cast<SVGElement*>(node()); - ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); + ASSERT(svgElement && svgElement->document()); + if (!svgElement->isStyled()) + return; SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); - const SVGRenderStyle* svgStyle = style()->svgStyle(); + if (!styledElement->supportsMarkers()) + return; + const SVGRenderStyle* svgStyle = style()->svgStyle(); AtomicString startMarkerId(svgStyle->startMarker()); AtomicString midMarkerId(svgStyle->midMarker()); AtomicString endMarkerId(svgStyle->endMarker()); - SVGResourceMarker* startMarker = getMarkerById(doc, startMarkerId); - SVGResourceMarker* midMarker = getMarkerById(doc, midMarkerId); - SVGResourceMarker* endMarker = getMarkerById(doc, endMarkerId); + SVGResourceMarker* startMarker = getMarkerById(doc, startMarkerId, this); + SVGResourceMarker* midMarker = getMarkerById(doc, midMarkerId, this); + SVGResourceMarker* endMarker = getMarkerById(doc, endMarkerId, this); if (!startMarker && !startMarkerId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(startMarkerId, styledElement); @@ -391,32 +319,10 @@ FloatRect RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatR endMarker->addClient(styledElement); if (!startMarker && !midMarker && !endMarker) - return FloatRect(); - - double strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, svgStyle->strokeWidth(), 1.0f); - DrawMarkersData data(context, startMarker, midMarker, strokeWidth); - - path.apply(&data, drawStartAndMidMarkers); - - data.previousMarkerData.marker = endMarker; - data.previousMarkerData.type = End; - drawMarkerWithData(context, data.previousMarkerData); - - // We know the marker boundaries, only after they're drawn! - // Otherwhise we'd need to do all the marker calculation twice - // once here (through paint()) and once in absoluteClippedOverflowRect(). - FloatRect bounds; - - if (startMarker) - bounds.unite(startMarker->cachedBounds()); - - if (midMarker) - bounds.unite(midMarker->cachedBounds()); - - if (endMarker) - bounds.unite(endMarker->cachedBounds()); + return; - return bounds; + float strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, svgStyle->strokeWidth(), 1.0f); + m_cachedLocalMarkerBBox = m_markerLayoutInfo.calculateBoundaries(startMarker, midMarker, endMarker, strokeWidth, m_path); } } diff --git a/WebCore/rendering/RenderPath.h b/WebCore/rendering/RenderPath.h index 2ff179e..d530f3c 100644 --- a/WebCore/rendering/RenderPath.h +++ b/WebCore/rendering/RenderPath.h @@ -25,10 +25,10 @@ #define RenderPath_h #if ENABLE(SVG) - +#include "AffineTransform.h" #include "FloatRect.h" #include "RenderSVGModelObject.h" -#include "TransformationMatrix.h" +#include "SVGMarkerLayoutInfo.h" namespace WebCore { @@ -40,7 +40,7 @@ class RenderPath : public RenderSVGModelObject { public: RenderPath(SVGStyledTransformableElement*); - const Path& path() const; + const Path& path() const { return m_path; } private: // Hit-detection seperated for the fill and the stroke @@ -48,9 +48,11 @@ private: bool strokeContains(const FloatPoint&, bool requiresStroke = true) const; virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const; + virtual FloatRect markerBoundingBox() const; virtual FloatRect repaintRectInLocalCoordinates() const; - virtual TransformationMatrix localToParentTransform() const; + virtual const AffineTransform& localToParentTransform() const; void setPath(const Path&); @@ -59,20 +61,22 @@ private: virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); - FloatRect drawMarkersIfNeeded(GraphicsContext*, const FloatRect&, const Path&) const; + void calculateMarkerBoundsIfNeeded() const; private: - virtual TransformationMatrix localTransform() const; + virtual AffineTransform localTransform() const; mutable Path m_path; mutable FloatRect m_cachedLocalFillBBox; + mutable FloatRect m_cachedLocalStrokeBBox; mutable FloatRect m_cachedLocalRepaintRect; - FloatRect m_markerBounds; - TransformationMatrix m_localTransform; + mutable FloatRect m_cachedLocalMarkerBBox; + mutable SVGMarkerLayoutInfo m_markerLayoutInfo; + AffineTransform m_localTransform; }; inline RenderPath* toRenderPath(RenderObject* object) @@ -94,5 +98,3 @@ void toRenderPath(const RenderPath*); #endif // ENABLE(SVG) #endif - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderReplaced.cpp b/WebCore/rendering/RenderReplaced.cpp index 692a66e..ba579df 100644 --- a/WebCore/rendering/RenderReplaced.cpp +++ b/WebCore/rendering/RenderReplaced.cpp @@ -34,9 +34,6 @@ using namespace std; namespace WebCore { -typedef WTF::HashMap<const RenderReplaced*, IntRect> OverflowRectMap; -static OverflowRectMap* gOverflowRectMap = 0; - const int cDefaultWidth = 300; const int cDefaultHeight = 150; @@ -56,8 +53,6 @@ RenderReplaced::RenderReplaced(Node* node, const IntSize& intrinsicSize) RenderReplaced::~RenderReplaced() { - if (replacedHasOverflow()) - gOverflowRectMap->remove(this); } void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) @@ -66,7 +61,7 @@ void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* old bool hadStyle = (oldStyle != 0); float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom(); - if (hadStyle && style() && style()->effectiveZoom() != oldZoom) + if (style() && style()->effectiveZoom() != oldZoom) intrinsicSizeChanged(); } @@ -77,10 +72,12 @@ void RenderReplaced::layout() LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); setHeight(minimumReplacedHeight()); - + calcWidth(); calcHeight(); - adjustOverflowForBoxShadowAndReflect(); + + m_overflow.clear(); + addShadowOverflow(); repainter.repaintAfterLayout(); @@ -127,28 +124,36 @@ void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty) drawSelectionTint = false; } + bool completelyClippedOut = false; if (style()->hasBorderRadius()) { - // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. - paintInfo.context->save(); - - IntSize topLeft, topRight, bottomLeft, bottomRight; IntRect borderRect = IntRect(tx, ty, width(), height()); - style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); - paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); + if (borderRect.isEmpty()) + completelyClippedOut = true; + else { + // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. + paintInfo.context->save(); + + IntSize topLeft, topRight, bottomLeft, bottomRight; + style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); + + paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); + } } - paintReplaced(paintInfo, tx, ty); + if (!completelyClippedOut) { + paintReplaced(paintInfo, tx, ty); - if (style()->hasBorderRadius()) - paintInfo.context->restore(); + if (style()->hasBorderRadius()) + 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); - paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor()); + paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor(), style()->colorSpace()); } } @@ -169,8 +174,8 @@ bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty) int currentTY = ty + y(); // Early exit if the element touches the edges. - int top = currentTY + overflowTop(); - int bottom = currentTY + overflowHeight(); + int top = currentTY + topVisibleOverflow(); + int bottom = currentTY + bottomVisibleOverflow(); if (isSelected() && m_inlineBoxWrapper) { int selTop = ty + m_inlineBoxWrapper->root()->selectionTop(); int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight(); @@ -179,7 +184,7 @@ bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty) } int os = 2 * maximalOutlineSize(paintInfo.phase); - if (currentTX + overflowLeft() >= paintInfo.rect.right() + os || currentTX + overflowWidth() <= paintInfo.rect.x() - os) + if (currentTX + leftVisibleOverflow() >= paintInfo.rect.right() + os || currentTX + rightVisibleOverflow() <= paintInfo.rect.x() - os) return false; if (top >= paintInfo.rect.bottom() + os || bottom <= paintInfo.rect.y() - os) return false; @@ -231,8 +236,8 @@ VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point) RootInlineBox* root = box->root(); - int top = root->topOverflow(); - int bottom = root->nextRootBox() ? root->nextRootBox()->topOverflow() : root->bottomOverflow(); + int top = root->lineTop(); + int bottom = root->nextRootBox() ? root->nextRootBox()->lineTop() : root->lineBottom(); if (point.y() + y() < top) return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above @@ -327,82 +332,6 @@ void RenderReplaced::setIntrinsicSize(const IntSize& size) m_intrinsicSize = size; } -void RenderReplaced::adjustOverflowForBoxShadowAndReflect() -{ - IntRect overflow; - for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) { - if (boxShadow->style == Inset) - continue; - IntRect shadow = borderBoxRect(); - shadow.move(boxShadow->x, boxShadow->y); - shadow.inflate(boxShadow->blur + boxShadow->spread); - 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); - setReplacedHasOverflow(true); - } else if (replacedHasOverflow()) { - gOverflowRectMap->remove(this); - setReplacedHasOverflow(false); - } -} - -int RenderReplaced::overflowHeight(bool) const -{ - if (replacedHasOverflow()) { - IntRect *r = &gOverflowRectMap->find(this)->second; - return r->height() + r->y(); - } - - return height(); -} - -int RenderReplaced::overflowWidth(bool) const -{ - if (replacedHasOverflow()) { - IntRect *r = &gOverflowRectMap->find(this)->second; - return r->width() + r->x(); - } - - return width(); -} - -int RenderReplaced::overflowLeft(bool) const -{ - if (replacedHasOverflow()) - return gOverflowRectMap->get(this).x(); - - return 0; -} - -int RenderReplaced::overflowTop(bool) const -{ - if (replacedHasOverflow()) - return gOverflowRectMap->get(this).y(); - - return 0; -} - -IntRect RenderReplaced::overflowRect(bool) const -{ - if (replacedHasOverflow()) - return gOverflowRectMap->find(this)->second; - - return borderBoxRect(); -} - IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) @@ -410,7 +339,7 @@ IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repa // 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)); + IntRect r = unionRect(localSelectionRect(false), visibleOverflowRect()); RenderView* v = view(); if (v) { diff --git a/WebCore/rendering/RenderReplaced.h b/WebCore/rendering/RenderReplaced.h index 70ad4f2..bcf565d 100644 --- a/WebCore/rendering/RenderReplaced.h +++ b/WebCore/rendering/RenderReplaced.h @@ -46,6 +46,7 @@ protected: void setIntrinsicSize(const IntSize&); virtual void intrinsicSizeChanged(); + virtual void paint(PaintInfo&, int tx, int ty); bool shouldPaint(PaintInfo&, int& tx, int& ty); void adjustOverflowForBoxShadowAndReflect(); IntRect localSelectionRect(bool checkWhetherSelected = true) const; @@ -62,15 +63,8 @@ private: virtual int minimumReplacedHeight() const { return 0; } - virtual void paint(PaintInfo&, int tx, int ty); virtual void paintReplaced(PaintInfo&, int /*tx*/, int /*ty*/) { } - virtual int overflowHeight(bool includeInterior = true) const; - virtual int overflowWidth(bool includeInterior = true) const; - virtual int overflowLeft(bool includeInterior = true) const; - virtual int overflowTop(bool includeInterior = true) const; - virtual IntRect overflowRect(bool includeInterior = true) const; - virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual unsigned caretMaxRenderedOffset() const; diff --git a/WebCore/rendering/RenderReplica.cpp b/WebCore/rendering/RenderReplica.cpp index 5fa3c62..1d589ae 100644 --- a/WebCore/rendering/RenderReplica.cpp +++ b/WebCore/rendering/RenderReplica.cpp @@ -72,7 +72,7 @@ void RenderReplica::paint(PaintInfo& paintInfo, int tx, int ty) // computing using the wrong rootLayer layer()->parent()->paintLayer(layer()->transform() ? layer()->parent() : layer()->enclosingTransformedAncestor(), paintInfo.context, paintInfo.rect, - PaintRestrictionNone, 0, 0, + PaintBehaviorNormal, 0, 0, RenderLayer::PaintLayerHaveTransparency | RenderLayer::PaintLayerAppliedTransform | RenderLayer::PaintLayerTemporaryClipRects | RenderLayer::PaintLayerPaintingReflection); else if (paintInfo.phase == PaintPhaseMask) paintMask(paintInfo, tx, ty); diff --git a/WebCore/rendering/RenderReplica.h b/WebCore/rendering/RenderReplica.h index d5db3b7..48c64e4 100644 --- a/WebCore/rendering/RenderReplica.h +++ b/WebCore/rendering/RenderReplica.h @@ -46,6 +46,10 @@ public: virtual void calcPrefWidths(); virtual void paint(PaintInfo&, int tx, int ty); + +private: + virtual bool isReplica() const { return true; } + }; } // namespace WebCore diff --git a/WebCore/rendering/RenderRuby.cpp b/WebCore/rendering/RenderRuby.cpp new file mode 100644 index 0000000..f13e2b4 --- /dev/null +++ b/WebCore/rendering/RenderRuby.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(RUBY) +#include "RenderRuby.h" + +#include "RenderRubyRun.h" + +namespace WebCore { + +//=== generic helper functions to avoid excessive code duplication === + +static RenderRubyRun* lastRubyRun(const RenderObject* ruby) +{ + RenderObject* child = ruby->lastChild(); + if (child && ruby->isAfterContent(child)) + child = child->previousSibling(); + ASSERT(!child || child->isRubyRun()); + return static_cast<RenderRubyRun*>(child); +} + +static inline RenderRubyRun* findRubyRunParent(RenderObject* child) +{ + while (child && !child->isRubyRun()) + child = child->parent(); + return static_cast<RenderRubyRun*>(child); +} + +//=== ruby as inline object === + +RenderRubyAsInline::RenderRubyAsInline(Node* node) + : RenderInline(node) +{ +} + +RenderRubyAsInline::~RenderRubyAsInline() +{ +} + +bool RenderRubyAsInline::isChildAllowed(RenderObject* child, RenderStyle*) const +{ + return child->isRubyText() + || child->isRubyRun() + || child->isInline(); +} + +void RenderRubyAsInline::addChild(RenderObject* child, RenderObject* beforeChild) +{ + // Note: ':after' content is handled implicitely below + + // if child is a ruby run, just add it normally + if (child->isRubyRun()) { + RenderInline::addChild(child, beforeChild); + return; + } + + if (beforeChild && !isAfterContent(beforeChild)) { + // insert child into run + ASSERT(!beforeChild->isRubyRun()); + RenderRubyRun* run = findRubyRunParent(beforeChild); + ASSERT(run); // beforeChild should always have a run as parent + if (run) { + run->addChild(child, beforeChild); + return; + } + ASSERT(false); // beforeChild should always have a run as parent! + // Emergency fallback: fall through and just append. + } + + // If the new child would be appended, try to add the child to the previous run + // if possible, or create a new run otherwise. + // (The RenderRubyRun object will handle the details) + RenderRubyRun* lastRun = lastRubyRun(this); + if (!lastRun || lastRun->hasRubyText()) { + lastRun = RenderRubyRun::staticCreateRubyRun(this); + RenderInline::addChild(lastRun); + } + lastRun->addChild(child); +} + +void RenderRubyAsInline::removeChild(RenderObject* child) +{ + // If the child's parent is *this, i.e. a ruby run or ':after' content, + // just use the normal remove method. + if (child->parent() == this) { + ASSERT(child->isRubyRun() || child->isAfterContent()); + RenderInline::removeChild(child); + return; + } + + // Find the containing run + RenderRubyRun* run = findRubyRunParent(child); + ASSERT(run); + run->removeChild(child); +} + + +//=== ruby as block object === + +RenderRubyAsBlock::RenderRubyAsBlock(Node* node) + : RenderBlock(node) +{ +} + +RenderRubyAsBlock::~RenderRubyAsBlock() +{ +} + +bool RenderRubyAsBlock::isChildAllowed(RenderObject* child, RenderStyle*) const +{ + return child->isRubyText() + || child->isRubyRun() + || child->isInline(); +} + +void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild) +{ + // Note: ':after' content is handled implicitely below + + // if child is a ruby run, just add it normally + if (child->isRubyRun()) { + RenderBlock::addChild(child, beforeChild); + return; + } + + if (beforeChild && !isAfterContent(beforeChild)) { + // insert child into run + ASSERT(!beforeChild->isRubyRun()); + RenderObject* run = beforeChild; + while (run && !run->isRubyRun()) + run = run->parent(); + if (run) { + run->addChild(child, beforeChild); + return; + } + ASSERT(false); // beforeChild should always have a run as parent! + // Emergency fallback: fall through and just append. + } + + // If the new child would be appended, try to add the child to the previous run + // if possible, or create a new run otherwise. + // (The RenderRubyRun object will handle the details) + RenderRubyRun* lastRun = lastRubyRun(this); + if (!lastRun || lastRun->hasRubyText()) { + lastRun = RenderRubyRun::staticCreateRubyRun(this); + RenderBlock::addChild(lastRun); + } + lastRun->addChild(child); +} + +void RenderRubyAsBlock::removeChild(RenderObject* child) +{ + // If the child's parent is *this, just use the normal remove method. + if (child->parent() == this) { + // This should happen only during destruction of the whole ruby element, though. + RenderBlock::removeChild(child); + return; + } + + // Find the containing run + RenderRubyRun* run = findRubyRunParent(child); + ASSERT(run); + run->removeChild(child); +} + +} // namespace WebCore + +#endif diff --git a/WebCore/rendering/RenderRuby.h b/WebCore/rendering/RenderRuby.h new file mode 100644 index 0000000..a5dafe9 --- /dev/null +++ b/WebCore/rendering/RenderRuby.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderRuby_h +#define RenderRuby_h + +#if ENABLE(RUBY) + +#include "RenderBlock.h" +#include "RenderInline.h" + +namespace WebCore { + +// Following the HTML 5 spec, the box object model for a <ruby> element allows several runs of ruby +// bases with their respective ruby texts looks as follows: +// +// 1 RenderRuby object, corresponding to the whole <ruby> HTML element +// 1+ RenderRubyRun (anonymous) +// 0 or 1 RenderRubyText - shuffled to the front in order to re-use existing block layouting +// 0-n inline object(s) +// 0 or 1 RenderRubyBase - contains the inline objects that make up the ruby base +// 1-n inline object(s) +// +// Note: <rp> elements are defined as having 'display:none' and thus normally are not assigned a renderer. + +// <ruby> when used as 'display:inline' +class RenderRubyAsInline : public RenderInline { +public: + RenderRubyAsInline(Node*); + virtual ~RenderRubyAsInline(); + + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; + virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject* child); + +private: + virtual bool isRuby() const { return true; } + virtual const char* renderName() const { return "RenderRuby (inline)"; } + virtual bool createsAnonymousWrapper() const { return true; } + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { ASSERT_NOT_REACHED(); } +}; + +// <ruby> when used as 'display:block' or 'display:inline-block' +class RenderRubyAsBlock : public RenderBlock { +public: + RenderRubyAsBlock(Node*); + virtual ~RenderRubyAsBlock(); + + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; + virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject* child); + +private: + virtual bool isRuby() const { return true; } + virtual const char* renderName() const { return "RenderRuby (block)"; } + virtual bool createsAnonymousWrapper() const { return true; } + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { ASSERT_NOT_REACHED(); } +}; + +} // namespace WebCore + +#endif + +#endif // RenderRuby_h diff --git a/WebCore/rendering/RenderRubyBase.cpp b/WebCore/rendering/RenderRubyBase.cpp new file mode 100644 index 0000000..65f9bc0 --- /dev/null +++ b/WebCore/rendering/RenderRubyBase.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(RUBY) +#include "RenderRubyBase.h" + +namespace WebCore { + +RenderRubyBase::RenderRubyBase(Node* node) + : RenderBlock(node) +{ + setInline(false); +} + +RenderRubyBase::~RenderRubyBase() +{ +} + +bool RenderRubyBase::isChildAllowed(RenderObject* child, RenderStyle*) const +{ + return child->isInline(); +} + +bool RenderRubyBase::hasOnlyWrappedInlineChildren(RenderObject* beforeChild) const +{ + // Tests whether all children in the base before beforeChild are either floated/positioned, + // or inline objects wrapped in anonymous blocks. + // Note that beforeChild may be 0, in which case all children are looked at. + for (RenderObject* child = firstChild(); child != beforeChild; child = child->nextSibling()) { + if (!child->isFloatingOrPositioned() && !(child->isAnonymousBlock() && child->childrenInline())) + return false; + } + return true; +} + +void RenderRubyBase::moveChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) +{ + // This function removes all children that are before (!) beforeChild + // and appends them to toBase. + ASSERT(toBase); + + // First make sure that beforeChild (if set) is indeed a direct child of this. + // Inline children might be wrapped in an anonymous block if there's a continuation. + // Theoretically, in ruby bases, this can happen with only the first such a child, + // so it should be OK to just climb the tree. + while (fromBeforeChild && fromBeforeChild->parent() != this) + fromBeforeChild = fromBeforeChild->parent(); + + if (childrenInline()) + moveInlineChildren(toBase, fromBeforeChild); + else + moveBlockChildren(toBase, fromBeforeChild); + + setNeedsLayoutAndPrefWidthsRecalc(); + toBase->setNeedsLayoutAndPrefWidthsRecalc(); +} + +void RenderRubyBase::moveInlineChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) +{ + RenderBlock* toBlock; + + if (toBase->childrenInline()) { + // The standard and easy case: move the children into the target base + toBlock = toBase; + } else { + // We need to wrap the inline objects into an anonymous block. + // If toBase has a suitable block, we re-use it, otherwise create a new one. + RenderObject* lastChild = toBase->lastChild(); + if (lastChild && lastChild->isAnonymousBlock() && lastChild->childrenInline()) + toBlock = toRenderBlock(lastChild); + else { + toBlock = toBase->createAnonymousBlock(); + toBase->children()->appendChildNode(toBase, toBlock); + } + } + // Move our inline children into the target block we determined above. + for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild()) + moveChildTo(toBlock, toBlock->children(), child); +} + +void RenderRubyBase::moveBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) +{ + if (toBase->childrenInline()) { + // First check whether we move only wrapped inline objects. + if (hasOnlyWrappedInlineChildren(fromBeforeChild)) { + // The reason why the base is in block flow must be after beforeChild. + // We therefore can extract the inline objects and move them to toBase. + for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild()) { + if (child->isAnonymousBlock()) { + RenderBlock* anonBlock = toRenderBlock(child); + ASSERT(anonBlock->childrenInline()); + ASSERT(!anonBlock->inlineContinuation()); + anonBlock->moveAllChildrenTo(toBase, toBase->children()); + anonBlock->deleteLineBoxTree(); + anonBlock->destroy(); + } else { + ASSERT(child->isFloatingOrPositioned()); + moveChildTo(toBase, toBase->children(), child); + } + } + } else { + // Moving block children -> have to set toBase as block flow + toBase->makeChildrenNonInline(); + // Move children, potentially collapsing anonymous block wrappers. + mergeBlockChildren(toBase, fromBeforeChild); + + // Now we need to check if the leftover children are all inline. + // If so, make this base inline again. + if (hasOnlyWrappedInlineChildren()) { + RenderObject* next = 0; + for (RenderObject* child = firstChild(); child; child = next) { + next = child->nextSibling(); + if (child->isFloatingOrPositioned()) + continue; + ASSERT(child->isAnonymousBlock()); + + RenderBlock* anonBlock = toRenderBlock(child); + ASSERT(anonBlock->childrenInline()); + ASSERT(!anonBlock->inlineContinuation()); + // Move inline children out of anonymous block. + anonBlock->moveAllChildrenTo(this, children(), anonBlock); + anonBlock->deleteLineBoxTree(); + anonBlock->destroy(); + } + setChildrenInline(true); + } + } + } else + mergeBlockChildren(toBase, fromBeforeChild); +} + +void RenderRubyBase::mergeBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) +{ + // This function removes all children that are before fromBeforeChild and appends them to toBase. + ASSERT(!childrenInline()); + ASSERT(toBase); + ASSERT(!toBase->childrenInline()); + + // Quick check whether we have anything to do, to simplify the following code. + if (fromBeforeChild != firstChild()) + return; + + // If an anonymous block would be put next to another such block, then merge those. + RenderObject* firstChildHere = firstChild(); + RenderObject* lastChildThere = toBase->lastChild(); + if (firstChildHere && firstChildHere->isAnonymousBlock() && firstChildHere->childrenInline() + && lastChildThere && lastChildThere->isAnonymousBlock() && lastChildThere->childrenInline()) { + RenderBlock* anonBlockHere = toRenderBlock(firstChildHere); + RenderBlock* anonBlockThere = toRenderBlock(lastChildThere); + anonBlockHere->moveAllChildrenTo(anonBlockThere, anonBlockThere->children()); + anonBlockHere->deleteLineBoxTree(); + anonBlockHere->destroy(); + } + // Move all remaining children normally. + for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild()) + moveChildTo(toBase, toBase->children(), child); +} + +} // namespace WebCore + +#endif diff --git a/WebCore/rendering/RenderRubyBase.h b/WebCore/rendering/RenderRubyBase.h new file mode 100644 index 0000000..29c4858 --- /dev/null +++ b/WebCore/rendering/RenderRubyBase.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderRubyBase_h +#define RenderRubyBase_h + +#if ENABLE(RUBY) + +#include "RenderBlock.h" + +namespace WebCore { + +class RenderRubyBase : public RenderBlock { +public: + RenderRubyBase(Node*); + virtual ~RenderRubyBase(); + + virtual const char* renderName() const { return "RenderRubyBase (anonymous)"; } + + virtual bool isRubyBase() const { return true; } + + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; + +private: + bool hasOnlyWrappedInlineChildren(RenderObject* beforeChild = 0) const; + + void moveChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0); + void moveInlineChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0); + void moveBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0); + void mergeBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0); + + // Allow RenderRubyRun to manipulate the children within ruby bases. + friend class RenderRubyRun; +}; + +} // namespace WebCore + +#endif + +#endif // RenderRubyBase_h diff --git a/WebCore/rendering/RenderRubyRun.cpp b/WebCore/rendering/RenderRubyRun.cpp new file mode 100644 index 0000000..61be455 --- /dev/null +++ b/WebCore/rendering/RenderRubyRun.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(RUBY) +#include "RenderRubyRun.h" + +#include "RenderRubyBase.h" +#include "RenderRubyText.h" +#include "RenderView.h" + +using namespace std; + +namespace WebCore { + +RenderRubyRun::RenderRubyRun(Node* node) + : RenderBlock(node) + , m_beingDestroyed(false) +{ + setReplaced(true); + setInline(true); +} + +RenderRubyRun::~RenderRubyRun() +{ +} + +void RenderRubyRun::destroy() +{ + // Mark if the run is being destroyed to avoid trouble in removeChild(). + m_beingDestroyed = true; + RenderBlock::destroy(); +} + +bool RenderRubyRun::hasRubyText() const +{ + // The only place where a ruby text can be is in the first position + // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves. + return firstChild() && firstChild()->isRubyText(); +} + +bool RenderRubyRun::hasRubyBase() const +{ + // The only place where a ruby base can be is in the last position + // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves. + return lastChild() && lastChild()->isRubyBase(); +} + +bool RenderRubyRun::isEmpty() const +{ + return !hasRubyText() && !hasRubyBase(); +} + +RenderRubyText* RenderRubyRun::rubyText() const +{ + RenderObject* child = firstChild(); + return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0; +} + +RenderRubyBase* RenderRubyRun::rubyBase() const +{ + RenderObject* child = lastChild(); + return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0; +} + +RenderRubyBase* RenderRubyRun::rubyBaseSafe() +{ + RenderRubyBase* base = rubyBase(); + if (!base) { + base = createRubyBase(); + RenderBlock::addChild(base); + } + return base; +} + +RenderBlock* RenderRubyRun::firstLineBlock() const +{ + return 0; +} + +void RenderRubyRun::updateFirstLetter() +{ +} + +bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const +{ + return child->isRubyText() || child->isInline(); +} + +void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild) +{ + ASSERT(child); + + // If child is a ruby text + if (child->isRubyText()) { + if (!beforeChild) { + // RenderRuby has already ascertained that we can add the child here. + ASSERT(!hasRubyText()); + // prepend ruby texts as first child + RenderBlock::addChild(child, firstChild()); + } else if (beforeChild->isRubyText()) { + // New text is inserted just before another. + // In this case the new text takes the place of the old one, and + // the old text goes into a new run that is inserted as next sibling. + ASSERT(beforeChild->parent() == this); + RenderObject* ruby = parent(); + ASSERT(ruby->isRuby()); + RenderBlock* newRun = staticCreateRubyRun(ruby); + ruby->addChild(newRun, nextSibling()); + // Add the new ruby text and move the old one to the new run + // Note: Doing it in this order and not using RenderRubyRun's methods, + // in order to avoid automatic removal of the ruby run in case there is no + // other child besides the old ruby text. + RenderBlock::addChild(child, beforeChild); + RenderBlock::removeChild(beforeChild); + newRun->addChild(beforeChild); + } else { + ASSERT(hasRubyBase()); // Otherwise beforeChild would be borked. + // Insertion before a ruby base object. + // In this case we need insert a new run before the current one and split the base. + RenderObject* ruby = parent(); + RenderRubyRun* newRun = staticCreateRubyRun(ruby); + ruby->addChild(newRun, this); + newRun->addChild(child); + rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild); + } + } else { + // child is not a text -> insert it into the base + // (append it instead if beforeChild is the ruby text) + if (beforeChild && beforeChild->isRubyText()) + beforeChild = 0; + rubyBaseSafe()->addChild(child, beforeChild); + } +} + +void RenderRubyRun::removeChild(RenderObject* child) +{ + // If the child is a ruby text, then merge the ruby base with the base of + // the right sibling run, if possible. + if (!m_beingDestroyed && !documentBeingDestroyed() && child->isRubyText()) { + RenderRubyBase* base = rubyBase(); + RenderObject* rightNeighbour = nextSibling(); + if (base && rightNeighbour && rightNeighbour->isRubyRun()) { + // Ruby run without a base can happen only at the first run. + RenderRubyRun* rightRun = static_cast<RenderRubyRun*>(rightNeighbour); + ASSERT(rightRun->hasRubyBase()); + RenderRubyBase* rightBase = rightRun->rubyBaseSafe(); + // Collect all children in a single base, then swap the bases. + rightBase->moveChildren(base); + moveChildTo(rightRun, rightRun->children(), base); + rightRun->moveChildTo(this, children(), rightBase); + // The now empty ruby base will be removed below. + } + } + + RenderBlock::removeChild(child); + + if (!m_beingDestroyed && !documentBeingDestroyed()) { + // Check if our base (if any) is now empty. If so, destroy it. + RenderBlock* base = rubyBase(); + if (base && !base->firstChild()) { + RenderBlock::removeChild(base); + base->deleteLineBoxTree(); + base->destroy(); + } + + // If any of the above leaves the run empty, destroy it as well. + if (isEmpty()) { + parent()->removeChild(this); + deleteLineBoxTree(); + destroy(); + } + } +} + +RenderRubyBase* RenderRubyRun::createRubyBase() const +{ + RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(BLOCK); + newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER? + rb->setStyle(newStyle.release()); + return rb; +} + +RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby) +{ + ASSERT(parentRuby && parentRuby->isRuby()); + RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(parentRuby->style()); + newStyle->setDisplay(INLINE_BLOCK); + rr->setStyle(newStyle.release()); + return rr; +} + +} // namespace WebCore + +#endif diff --git a/WebCore/rendering/RenderRubyRun.h b/WebCore/rendering/RenderRubyRun.h new file mode 100644 index 0000000..acf359b --- /dev/null +++ b/WebCore/rendering/RenderRubyRun.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderRubyRun_h +#define RenderRubyRun_h + +#if ENABLE(RUBY) + +#include "RenderBlock.h" + +namespace WebCore { + +class RenderRubyBase; +class RenderRubyText; + +// RenderRubyRun are 'inline-block/table' like objects,and wrap a single pairing of a ruby base with its ruby text(s). +// See RenderRuby.h for further comments on the structure + +class RenderRubyRun : public RenderBlock { +public: + RenderRubyRun(Node*); + virtual ~RenderRubyRun(); + + virtual void destroy(); + + bool hasRubyText() const; + bool hasRubyBase() const; + bool isEmpty() const; + RenderRubyText* rubyText() const; + RenderRubyBase* rubyBase() const; + RenderRubyBase* rubyBaseSafe(); // creates the base if it doesn't already exist + + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; + virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject* child); + + virtual RenderBlock* firstLineBlock() const; + virtual void updateFirstLetter(); + + static RenderRubyRun* staticCreateRubyRun(const RenderObject* parentRuby); + +protected: + RenderRubyBase* createRubyBase() const; + +private: + virtual bool isRubyRun() const { return true; } + virtual const char* renderName() const { return "RenderRubyRun (anonymous)"; } + virtual bool createsAnonymousWrapper() const { return true; } + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { } + + bool m_beingDestroyed; +}; + +} // namespace WebCore + +#endif + +#endif // RenderRubyRun_h diff --git a/WebCore/rendering/RenderRubyText.cpp b/WebCore/rendering/RenderRubyText.cpp new file mode 100644 index 0000000..1cf2b9e --- /dev/null +++ b/WebCore/rendering/RenderRubyText.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(RUBY) +#include "RenderRubyText.h" + +namespace WebCore { + +RenderRubyText::RenderRubyText(Node* node) + : RenderBlock(node) +{ +} + +RenderRubyText::~RenderRubyText() +{ +} + +bool RenderRubyText::isChildAllowed(RenderObject* child, RenderStyle*) const +{ + return child->isInline(); +} + +} // namespace WebCore + +#endif diff --git a/WebCore/rendering/RenderRubyText.h b/WebCore/rendering/RenderRubyText.h new file mode 100644 index 0000000..865d179 --- /dev/null +++ b/WebCore/rendering/RenderRubyText.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderRubyText_h +#define RenderRubyText_h + +#if ENABLE(RUBY) + +#include "RenderBlock.h" + +namespace WebCore { + +class RenderRubyText : public RenderBlock { +public: + RenderRubyText(Node*); + virtual ~RenderRubyText(); + + virtual const char* renderName() const { return "RenderRubyText"; } + + virtual bool isRubyText() const { return true; } + + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; +}; + +} // namespace WebCore + +#endif + +#endif // RenderRubyText_h diff --git a/WebCore/rendering/RenderSVGBlock.h b/WebCore/rendering/RenderSVGBlock.h index a4ececb..0b0d107 100644 --- a/WebCore/rendering/RenderSVGBlock.h +++ b/WebCore/rendering/RenderSVGBlock.h @@ -35,6 +35,8 @@ class RenderSVGBlock : public RenderBlock, protected SVGRenderBase { public: RenderSVGBlock(SVGElement*); + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } + private: virtual void setStyle(PassRefPtr<RenderStyle>); }; diff --git a/WebCore/rendering/RenderSVGContainer.cpp b/WebCore/rendering/RenderSVGContainer.cpp index d7aec99..6d1b965 100644 --- a/WebCore/rendering/RenderSVGContainer.cpp +++ b/WebCore/rendering/RenderSVGContainer.cpp @@ -3,6 +3,7 @@ 2004, 2005, 2007, 2008 Rob Buis <buis@kde.org> 2007 Eric Seidel <eric@webkit.org> Copyright (C) 2009 Google, Inc. All rights reserved. + 2009 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -25,14 +26,11 @@ #if ENABLE(SVG) #include "RenderSVGContainer.h" -#include "AXObjectCache.h" -#include "FloatQuad.h" #include "GraphicsContext.h" #include "RenderView.h" #include "SVGRenderSupport.h" #include "SVGResourceFilter.h" #include "SVGStyledElement.h" -#include "SVGURIReference.h" namespace WebCore { @@ -62,17 +60,7 @@ void RenderSVGContainer::layout() LayoutRepainter repainter(*this, checkForRepaintDuringLayout() || selfWillPaint()); calculateLocalTransform(); // Allow RenderSVGTransformableContainer to update its transform - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - // Only force our kids to layout if we're being asked to relayout as a result of a parent changing - // FIXME: We should be able to skip relayout of non-relative kids when only bounds size has changed - // that's a possible future optimization using LayoutState - // http://bugs.webkit.org/show_bug.cgi?id=15391 - if (selfNeedsLayout()) - child->setNeedsLayout(true); - - child->layoutIfNeeded(); - ASSERT(!child->needsLayout()); - } + layoutChildren(this, selfNeedsLayout()); repainter.repaintAfterLayout(); setNeedsLayout(false); @@ -82,7 +70,7 @@ bool RenderSVGContainer::selfWillPaint() const { #if ENABLE(FILTERS) const SVGRenderStyle* svgStyle = style()->svgStyle(); - SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter()); + SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter(), this); if (filter) return true; #endif @@ -94,7 +82,7 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int) if (paintInfo.context->paintingDisabled() || !drawsContents()) return; - // Spec: groups w/o children still may render filter content. + // Spec: groups w/o children still may render filter content. if (!firstChild() && !selfWillPaint()) return; @@ -109,12 +97,16 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int) SVGResourceFilter* filter = 0; FloatRect boundingBox = repaintRectInLocalCoordinates(); + + bool continueRendering = true; if (childPaintInfo.phase == PaintPhaseForeground) - prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); + continueRendering = prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); - childPaintInfo.paintingRoot = paintingRootForChildren(childPaintInfo); - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) - child->paint(childPaintInfo, 0, 0); + if (continueRendering) { + childPaintInfo.paintingRoot = paintingRootForChildren(childPaintInfo); + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) + child->paint(childPaintInfo, 0, 0); + } if (paintInfo.phase == PaintPhaseForeground) finishRenderSVGContent(this, childPaintInfo, filter, paintInfo.context); @@ -132,10 +124,11 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int) } // addFocusRingRects is called from paintOutline and needs to be in the same coordinates as the paintOuline call -void RenderSVGContainer::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +void RenderSVGContainer::addFocusRingRects(Vector<IntRect>& rects, int, int) { IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRectInLocalCoordinates())); - graphicsContext->addFocusRingRect(paintRectInParent); + if (!paintRectInParent.isEmpty()) + rects.append(paintRectInParent); } FloatRect RenderSVGContainer::objectBoundingBox() const @@ -143,14 +136,30 @@ FloatRect RenderSVGContainer::objectBoundingBox() const return computeContainerBoundingBox(this, false); } +FloatRect RenderSVGContainer::strokeBoundingBox() const +{ + return computeContainerBoundingBox(this, true); +} + // RenderSVGContainer is used for <g> elements which do not themselves have a // width or height, so we union all of our child rects as our repaint rect. FloatRect RenderSVGContainer::repaintRectInLocalCoordinates() const { FloatRect repaintRect = computeContainerBoundingBox(this, true); - // A filter on this container can paint outside of the union of the child repaint rects - repaintRect.unite(filterBoundingBoxForRenderer(this)); + FloatRect rect = filterBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect = rect; + + rect = clipperBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect.intersect(rect); + + rect = maskerBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect.intersect(rect); + + style()->svgStyle()->inflateForShadow(repaintRect); return repaintRect; } @@ -178,5 +187,3 @@ bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTest } #endif // ENABLE(SVG) - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderSVGContainer.h b/WebCore/rendering/RenderSVGContainer.h index f2195e3..f681e50 100644 --- a/WebCore/rendering/RenderSVGContainer.h +++ b/WebCore/rendering/RenderSVGContainer.h @@ -42,10 +42,9 @@ public: void setDrawsContents(bool); bool drawsContents() const; -protected: virtual void paint(PaintInfo&, int parentX, int parentY); -private: +protected: virtual RenderObjectChildList* virtualChildren() { return children(); } virtual const RenderObjectChildList* virtualChildren() const { return children(); } @@ -54,9 +53,10 @@ private: virtual void layout(); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const; virtual FloatRect repaintRectInLocalCoordinates() const; virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); @@ -71,6 +71,7 @@ private: bool selfWillPaint() const; +private: RenderObjectChildList m_children; bool m_drawsContents : 1; }; @@ -78,14 +79,14 @@ private: inline RenderSVGContainer* toRenderSVGContainer(RenderObject* object) { // Note: isSVGContainer is also true for RenderSVGViewportContainer, which is not derived from this. - ASSERT(!object || object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer")); + ASSERT(!object || (object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer"))); return static_cast<RenderSVGContainer*>(object); } inline const RenderSVGContainer* toRenderSVGContainer(const RenderObject* object) { // Note: isSVGContainer is also true for RenderSVGViewportContainer, which is not derived from this. - ASSERT(!object || object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer")); + ASSERT(!object || (object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer"))); return static_cast<const RenderSVGContainer*>(object); } diff --git a/WebCore/rendering/RenderSVGGradientStop.cpp b/WebCore/rendering/RenderSVGGradientStop.cpp index b81e7f4..66391c8 100644 --- a/WebCore/rendering/RenderSVGGradientStop.cpp +++ b/WebCore/rendering/RenderSVGGradientStop.cpp @@ -49,7 +49,7 @@ void RenderSVGGradientStop::styleDidChange(StyleDifference diff, const RenderSty // <stop> elements should only be allowed to make renderers under gradient elements // but I can imagine a few cases we might not be catching, so let's not crash if our parent isn't a gradient. if (SVGGradientElement* gradient = gradientElement()) { - if (SVGResource* resource = gradient->canvasResource()) + if (SVGResource* resource = gradient->canvasResource(this)) resource->invalidate(); } } diff --git a/WebCore/rendering/RenderSVGHiddenContainer.cpp b/WebCore/rendering/RenderSVGHiddenContainer.cpp index d4b39d3..bb0a15d 100644 --- a/WebCore/rendering/RenderSVGHiddenContainer.cpp +++ b/WebCore/rendering/RenderSVGHiddenContainer.cpp @@ -38,17 +38,7 @@ RenderSVGHiddenContainer::RenderSVGHiddenContainer(SVGStyledElement* element) void RenderSVGHiddenContainer::layout() { ASSERT(needsLayout()); - - // Layout our kids to prevent a kid from being marked as needing layout - // then never being asked to layout. - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (selfNeedsLayout()) - child->setNeedsLayout(true); - - child->layoutIfNeeded(); - ASSERT(!child->needsLayout()); - } - + layoutChildren(this, selfNeedsLayout()); setNeedsLayout(false); } diff --git a/WebCore/rendering/RenderSVGHiddenContainer.h b/WebCore/rendering/RenderSVGHiddenContainer.h index 0ef0a43..fdbd2bc 100644 --- a/WebCore/rendering/RenderSVGHiddenContainer.h +++ b/WebCore/rendering/RenderSVGHiddenContainer.h @@ -51,9 +51,6 @@ namespace WebCore { virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); - // FIXME: This override only exists to match existing LayoutTest results. - virtual TransformationMatrix absoluteTransform() const { return TransformationMatrix(); } - virtual FloatRect objectBoundingBox() const; virtual FloatRect repaintRectInLocalCoordinates() const; diff --git a/WebCore/rendering/RenderSVGImage.cpp b/WebCore/rendering/RenderSVGImage.cpp index 7e0b40d..96eeaf9 100644 --- a/WebCore/rendering/RenderSVGImage.cpp +++ b/WebCore/rendering/RenderSVGImage.cpp @@ -4,6 +4,7 @@ Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> Copyright (C) 2007, 2008, 2009 Rob Buis <buis@kde.org> Copyright (C) 2009, Google, Inc. + Copyright (C) 2009 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -47,81 +48,6 @@ RenderSVGImage::RenderSVGImage(SVGImageElement* impl) { } -void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio* aspectRatio) -{ - float origDestWidth = destRect.width(); - float origDestHeight = destRect.height(); - if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET) { - float widthToHeightMultiplier = srcRect.height() / srcRect.width(); - if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) { - destRect.setHeight(origDestWidth * widthToHeightMultiplier); - switch (aspectRatio->align()) { - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: - destRect.setY(destRect.y() + origDestHeight / 2.0f - destRect.height() / 2.0f); - break; - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: - destRect.setY(destRect.y() + origDestHeight - destRect.height()); - break; - } - } - if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) { - destRect.setWidth(origDestHeight / widthToHeightMultiplier); - switch (aspectRatio->align()) { - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: - destRect.setX(destRect.x() + origDestWidth / 2.0f - destRect.width() / 2.0f); - break; - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: - destRect.setX(destRect.x() + origDestWidth - destRect.width()); - break; - } - } - } else if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) { - float widthToHeightMultiplier = srcRect.height() / srcRect.width(); - // if the destination height is less than the height of the image we'll be drawing - if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) { - float destToSrcMultiplier = srcRect.width() / destRect.width(); - srcRect.setHeight(destRect.height() * destToSrcMultiplier); - switch (aspectRatio->align()) { - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: - srcRect.setY(destRect.y() + image()->height() / 2.0f - srcRect.height() / 2.0f); - break; - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: - srcRect.setY(destRect.y() + image()->height() - srcRect.height()); - break; - } - } - // if the destination width is less than the width of the image we'll be drawing - if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) { - float destToSrcMultiplier = srcRect.height() / destRect.height(); - srcRect.setWidth(destRect.width() * destToSrcMultiplier); - switch (aspectRatio->align()) { - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: - srcRect.setX(destRect.x() + image()->width() / 2.0f - srcRect.width() / 2.0f); - break; - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: - case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: - srcRect.setX(destRect.x() + image()->width() - srcRect.width()); - break; - } - } - } -} - void RenderSVGImage::layout() { ASSERT(needsLayout()); @@ -138,6 +64,7 @@ void RenderSVGImage::layout() calcHeight(); m_localBounds = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image)); + m_cachedLocalRepaintRect = FloatRect(); repainter.repaintAfterLayout(); @@ -157,16 +84,16 @@ void RenderSVGImage::paint(PaintInfo& paintInfo, int, int) PaintInfo savedInfo(paintInfo); - prepareToRenderSVGContent(this, paintInfo, m_localBounds, filter); + if (prepareToRenderSVGContent(this, paintInfo, m_localBounds, filter)) { + FloatRect destRect = m_localBounds; + FloatRect srcRect(0, 0, image()->width(), image()->height()); - FloatRect destRect = m_localBounds; - FloatRect srcRect(0, 0, image()->width(), image()->height()); + SVGImageElement* imageElt = static_cast<SVGImageElement*>(node()); + if (imageElt->preserveAspectRatio().align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) + imageElt->preserveAspectRatio().transformRect(destRect, srcRect); - SVGImageElement* imageElt = static_cast<SVGImageElement*>(node()); - if (imageElt->preserveAspectRatio()->align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) - adjustRectsForAspectRatio(destRect, srcRect, imageElt->preserveAspectRatio()); - - paintInfo.context->drawImage(image(), destRect, srcRect); + paintInfo.context->drawImage(image(), DeviceColorSpace, destRect, srcRect); + } finishRenderSVGContent(this, paintInfo, filter, savedInfo.context); } @@ -212,12 +139,29 @@ FloatRect RenderSVGImage::objectBoundingBox() const FloatRect RenderSVGImage::repaintRectInLocalCoordinates() const { - FloatRect repaintRect = m_localBounds; + // If we already have a cached repaint rect, return that + if (!m_cachedLocalRepaintRect.isEmpty()) + return m_cachedLocalRepaintRect; + + m_cachedLocalRepaintRect = m_localBounds; + + // FIXME: We need to be careful here. We assume that there is no filter, + // clipper or masker if the rects are empty. + FloatRect rect = filterBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect = rect; + + rect = clipperBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect.intersect(rect); + + rect = maskerBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + m_cachedLocalRepaintRect.intersect(rect); - // Filters can paint outside the image content - repaintRect.unite(filterBoundingBoxForRenderer(this)); + style()->svgStyle()->inflateForShadow(m_cachedLocalRepaintRect); - return repaintRect; + return m_cachedLocalRepaintRect; } void RenderSVGImage::imageChanged(WrappedImagePtr image, const IntRect* rect) @@ -241,11 +185,12 @@ void RenderSVGImage::mapLocalToContainer(RenderBoxModelObject* repaintContainer, SVGRenderBase::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState); } -void RenderSVGImage::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, int, int) { // this is called from paint() after the localTransform has already been applied IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates()); - graphicsContext->addFocusRingRect(contentRect); + if (!contentRect.isEmpty()) + rects.append(contentRect); } void RenderSVGImage::absoluteRects(Vector<IntRect>& rects, int, int) diff --git a/WebCore/rendering/RenderSVGImage.h b/WebCore/rendering/RenderSVGImage.h index ef11719..8ed9146 100644 --- a/WebCore/rendering/RenderSVGImage.h +++ b/WebCore/rendering/RenderSVGImage.h @@ -24,28 +24,29 @@ #define RenderSVGImage_h #if ENABLE(SVG) - -#include "TransformationMatrix.h" +#include "AffineTransform.h" #include "FloatRect.h" #include "RenderImage.h" +#include "SVGPreserveAspectRatio.h" #include "SVGRenderSupport.h" namespace WebCore { class SVGImageElement; - class SVGPreserveAspectRatio; - class RenderSVGImage : public RenderImage, SVGRenderBase { + class RenderSVGImage : public RenderImage, protected SVGRenderBase { public: RenderSVGImage(SVGImageElement*); private: + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } virtual const char* renderName() const { return "RenderSVGImage"; } virtual bool isSVGImage() const { return true; } - virtual TransformationMatrix localToParentTransform() const { return m_localTransform; } + virtual const AffineTransform& localToParentTransform() const { return m_localTransform; } virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const { return m_localBounds; } virtual FloatRect repaintRectInLocalCoordinates() const; virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); @@ -55,10 +56,9 @@ namespace WebCore { virtual void absoluteRects(Vector<IntRect>&, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); - void adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio*); virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); @@ -68,10 +68,11 @@ namespace WebCore { virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); - virtual TransformationMatrix localTransform() const { return m_localTransform; } + virtual AffineTransform localTransform() const { return m_localTransform; } - TransformationMatrix m_localTransform; + AffineTransform m_localTransform; FloatRect m_localBounds; + mutable FloatRect m_cachedLocalRepaintRect; }; } // namespace WebCore diff --git a/WebCore/rendering/RenderSVGInline.h b/WebCore/rendering/RenderSVGInline.h index 9f9f3f5..53fd4b7 100644 --- a/WebCore/rendering/RenderSVGInline.h +++ b/WebCore/rendering/RenderSVGInline.h @@ -27,6 +27,8 @@ #if ENABLE(SVG) #include "RenderInline.h" +#include "SVGRenderSupport.h" + namespace WebCore { class RenderSVGInline : public RenderInline { @@ -38,6 +40,9 @@ public: // These are shared between RenderSVGTSpan and RenderSVGTextPath virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); + + virtual FloatRect objectBoundingBox() const { return FloatRect(); } + virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); } private: virtual InlineFlowBox* createInlineFlowBox(); diff --git a/WebCore/rendering/RenderSVGModelObject.h b/WebCore/rendering/RenderSVGModelObject.h index 0aa13ad..4c50734 100644 --- a/WebCore/rendering/RenderSVGModelObject.h +++ b/WebCore/rendering/RenderSVGModelObject.h @@ -49,6 +49,8 @@ class RenderSVGModelObject : public RenderObject, protected SVGRenderBase { public: RenderSVGModelObject(SVGStyledElement*); + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } + virtual bool requiresLayer() const { return false; } virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); diff --git a/WebCore/rendering/RenderSVGRoot.cpp b/WebCore/rendering/RenderSVGRoot.cpp index 0a39bf4..74172fc 100644 --- a/WebCore/rendering/RenderSVGRoot.cpp +++ b/WebCore/rendering/RenderSVGRoot.cpp @@ -86,23 +86,20 @@ void RenderSVGRoot::layout() LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout()); + int oldWidth = width(); calcWidth(); + + int oldHeight = height(); calcHeight(); SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); setWidth(static_cast<int>(width() * svg->currentScale())); setHeight(static_cast<int>(height() * svg->currentScale())); - calcViewport(); - - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (selfNeedsLayout()) // either bounds or transform changed, force kids to relayout - child->setNeedsLayout(true, false); - - child->layoutIfNeeded(); - ASSERT(!child->needsLayout()); - } + // RenderSVGRoot needs to take special care to propagate window size changes to the children, + // if the outermost <svg> is using relative x/y/width/height values. Hence the additonal parameters. + layoutChildren(this, selfNeedsLayout() || (svg->hasRelativeValues() && (width() != oldWidth || height() != oldHeight))); repainter.repaintAfterLayout(); view()->enableLayoutState(); @@ -113,7 +110,7 @@ bool RenderSVGRoot::selfWillPaint() const { #if ENABLE(FILTERS) const SVGRenderStyle* svgStyle = style()->svgStyle(); - SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter()); + SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter(), this); if (filter) return true; #endif @@ -132,7 +129,7 @@ void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) paintBoxDecorations(paintInfo, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y()); // An empty viewport disables rendering. FIXME: Should we still render filters? - if (viewportSize().isEmpty()) + if (m_viewportSize.isEmpty()) return; // Don't paint if we don't have kids, except if we have filters we should paint those. @@ -143,9 +140,8 @@ void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) RenderObject::PaintInfo childPaintInfo(paintInfo); childPaintInfo.context->save(); - // SVG does not support independent x/y clipping - if (style()->overflowX() != OVISIBLE) - childPaintInfo.context->clip(overflowClipRect(borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y())); + // Apply initial viewport clip - not affected by overflow handling + childPaintInfo.context->clip(overflowClipRect(borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y())); // Convert from container offsets (html renderers) to a relative transform (svg renderers). // Transform from our paint container's coordinate system to our local coords. @@ -153,10 +149,13 @@ void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) SVGResourceFilter* filter = 0; FloatRect boundingBox = repaintRectInLocalCoordinates(); + + bool continueRendering = true; if (childPaintInfo.phase == PaintPhaseForeground) - prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); + continueRendering = prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); - RenderBox::paint(childPaintInfo, 0, 0); + if (continueRendering) + RenderBox::paint(childPaintInfo, 0, 0); if (childPaintInfo.phase == PaintPhaseForeground) finishRenderSVGContent(this, childPaintInfo, filter, paintInfo.context); @@ -167,11 +166,6 @@ void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) paintOutline(paintInfo.context, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y(), width(), height(), style()); } -const FloatSize& RenderSVGRoot::viewportSize() const -{ - return m_viewportSize; -} - void RenderSVGRoot::calcViewport() { SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); @@ -196,13 +190,12 @@ void RenderSVGRoot::calcViewport() // RenderBox methods will expect coordinates w/o any transforms in coordinates // relative to our borderBox origin. This method gives us exactly that. -TransformationMatrix RenderSVGRoot::localToBorderBoxTransform() const +AffineTransform RenderSVGRoot::localToBorderBoxTransform() const { - TransformationMatrix ctm; IntSize borderAndPadding = borderOriginToContentBox(); - ctm.translate(borderAndPadding.width(), borderAndPadding.height()); SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); - ctm.scale(svg->currentScale()); + float scale = svg->currentScale(); + AffineTransform ctm(scale, 0, 0, scale, borderAndPadding.width(), borderAndPadding.height()); ctm.translate(svg->currentTranslate().x(), svg->currentTranslate().y()); return svg->viewBoxToViewTransform(width(), height()) * ctm; } @@ -217,28 +210,21 @@ IntSize RenderSVGRoot::borderOriginToContentBox() const return IntSize(borderLeft() + paddingLeft(), borderTop() + paddingTop()); } -TransformationMatrix RenderSVGRoot::localToRepaintContainerTransform(const IntPoint& parentOriginInContainer) const +AffineTransform RenderSVGRoot::localToRepaintContainerTransform(const IntPoint& parentOriginInContainer) const { - TransformationMatrix parentToContainer; - parentToContainer.translate(parentOriginInContainer.x(), parentOriginInContainer.y()); - return localToParentTransform() * parentToContainer; + AffineTransform parentToContainer(localToParentTransform()); + return parentToContainer.translateRight(parentOriginInContainer.x(), parentOriginInContainer.y()); } -TransformationMatrix RenderSVGRoot::localToParentTransform() const +const AffineTransform& RenderSVGRoot::localToParentTransform() const { IntSize parentToBorderBoxOffset = parentOriginToBorderBox(); - TransformationMatrix borderBoxOriginToParentOrigin; - borderBoxOriginToParentOrigin.translate(parentToBorderBoxOffset.width(), parentToBorderBoxOffset.height()); + AffineTransform borderBoxOriginToParentOrigin(localToBorderBoxTransform()); + borderBoxOriginToParentOrigin.translateRight(parentToBorderBoxOffset.width(), parentToBorderBoxOffset.height()); - return localToBorderBoxTransform() * borderBoxOriginToParentOrigin; -} - -// FIXME: This method should be removed as soon as callers to RenderBox::absoluteTransform() can be removed. -TransformationMatrix RenderSVGRoot::absoluteTransform() const -{ - // This would apply localTransform() twice if localTransform() were not the identity. - return localToParentTransform() * RenderBox::absoluteTransform(); + m_localToParentTransform = borderBoxOriginToParentOrigin; + return m_localToParentTransform; } FloatRect RenderSVGRoot::objectBoundingBox() const @@ -249,18 +235,26 @@ FloatRect RenderSVGRoot::objectBoundingBox() const FloatRect RenderSVGRoot::repaintRectInLocalCoordinates() const { // FIXME: This does not include the border but it should! - return computeContainerBoundingBox(this, true); + FloatRect repaintRect = computeContainerBoundingBox(this, true); + style()->svgStyle()->inflateForShadow(repaintRect); + return repaintRect; } -TransformationMatrix RenderSVGRoot::localTransform() const +AffineTransform RenderSVGRoot::localTransform() const { - return TransformationMatrix(); + return AffineTransform(); } void RenderSVGRoot::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) { - // Apply our local transforms (except for x/y translation) and call RenderBox's method to handle all the normal CSS Box model bits + // Apply our local transforms (except for x/y translation), then our shadow, + // and then call RenderBox's method to handle all the normal CSS Box model bits repaintRect = localToBorderBoxTransform().mapRect(repaintRect); + + // Apply initial viewport clip - not affected by overflow settings + repaintRect.intersect(enclosingIntRect(FloatRect(FloatPoint(), m_viewportSize))); + + style()->svgStyle()->inflateForShadow(repaintRect); RenderBox::computeRectForRepaint(repaintContainer, repaintRect, fixed); } @@ -283,14 +277,9 @@ bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& re IntPoint pointInBorderBox = pointInParent - parentOriginToBorderBox(); // Note: For now, we're ignoring hits to border and padding for <svg> - - if (style()->overflowX() == OHIDDEN) { - // SVG doesn't support independent x/y overflow - ASSERT(style()->overflowY() == OHIDDEN); - IntPoint pointInContentBox = pointInBorderBox - borderOriginToContentBox(); - if (!contentBoxRect().contains(pointInContentBox)) - return false; - } + IntPoint pointInContentBox = pointInBorderBox - borderOriginToContentBox(); + if (!contentBoxRect().contains(pointInContentBox)) + return false; IntPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent); @@ -310,5 +299,3 @@ bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& re } #endif // ENABLE(SVG) - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderSVGRoot.h b/WebCore/rendering/RenderSVGRoot.h index 08c3058..8ad5e40 100644 --- a/WebCore/rendering/RenderSVGRoot.h +++ b/WebCore/rendering/RenderSVGRoot.h @@ -31,9 +31,9 @@ namespace WebCore { class SVGStyledElement; -class TransformationMatrix; +class AffineTransform; -class RenderSVGRoot : public RenderBox, SVGRenderBase { +class RenderSVGRoot : public RenderBox, protected SVGRenderBase { public: RenderSVGRoot(SVGStyledElement*); @@ -54,17 +54,17 @@ private: virtual void layout(); virtual void paint(PaintInfo&, int parentX, int parentY); - virtual TransformationMatrix localToParentTransform() const; + virtual const AffineTransform& localToParentTransform() const; bool fillContains(const FloatPoint&) const; bool strokeContains(const FloatPoint&) const; virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const { return computeContainerBoundingBox(this, true); } virtual FloatRect repaintRectInLocalCoordinates() const; - // FIXME: Both of these overrides should be removed. - virtual TransformationMatrix localTransform() const; - virtual TransformationMatrix absoluteTransform() const; + // FIXME: This override should be removed. + virtual AffineTransform localTransform() const; virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); @@ -73,17 +73,17 @@ private: virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; void calcViewport(); - const FloatSize& viewportSize() const; bool selfWillPaint() const; IntSize parentOriginToBorderBox() const; IntSize borderOriginToContentBox() const; - TransformationMatrix localToRepaintContainerTransform(const IntPoint& parentOriginInContainer) const; - TransformationMatrix localToBorderBoxTransform() const; + AffineTransform localToRepaintContainerTransform(const IntPoint& parentOriginInContainer) const; + AffineTransform localToBorderBoxTransform() const; RenderObjectChildList m_children; FloatSize m_viewportSize; + mutable AffineTransform m_localToParentTransform; }; inline RenderSVGRoot* toRenderSVGRoot(RenderObject* object) diff --git a/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp b/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp new file mode 100644 index 0000000..9d3d26f --- /dev/null +++ b/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp @@ -0,0 +1,101 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#if ENABLE(SVG) +#include "RenderSVGShadowTreeRootContainer.h" + +#include "MouseEvent.h" +#include "SVGShadowTreeElements.h" +#include "SVGUseElement.h" + +namespace WebCore { + +RenderSVGShadowTreeRootContainer::RenderSVGShadowTreeRootContainer(SVGUseElement* node) + : RenderSVGTransformableContainer(node) + , m_recreateTree(false) +{ +} + +RenderSVGShadowTreeRootContainer::~RenderSVGShadowTreeRootContainer() +{ + if (m_shadowRoot && m_shadowRoot->attached()) + m_shadowRoot->detach(); +} + +void RenderSVGShadowTreeRootContainer::updateStyle(Node::StyleChange change) +{ + if (m_shadowRoot && m_shadowRoot->attached()) + m_shadowRoot->recalcStyle(change); +} + +void RenderSVGShadowTreeRootContainer::updateFromElement() +{ + bool hadExistingTree = m_shadowRoot; + + SVGUseElement* useElement = static_cast<SVGUseElement*>(node()); + if (!m_shadowRoot) { + ASSERT(!m_recreateTree); + m_shadowRoot = new SVGShadowTreeRootElement(document(), useElement); + useElement->buildPendingResource(); + } + + ASSERT(m_shadowRoot->shadowParentNode() == useElement); + + bool shouldRecreateTree = m_recreateTree; + if (m_recreateTree) { + ASSERT(hadExistingTree); + + if (m_shadowRoot->attached()) + m_shadowRoot->detach(); + + m_shadowRoot->removeAllChildren(); + m_recreateTree = false; + } + + // Only rebuild the shadow tree, if we a) never had a tree or b) we were specifically asked to do so + // If the use element is a pending resource, and a) or b) is true, do nothing, and wait for the use + // element to be asked to buildPendingResource(), this will call us again, with m_recreateTrue=true. + if ((shouldRecreateTree || !hadExistingTree) && !useElement->isPendingResource()) { + useElement->buildShadowAndInstanceTree(m_shadowRoot.get()); + + // Attach shadow root element + m_shadowRoot->attachElement(style(), renderArena()); + + // Attach subtree, as if it was a regular non-shadow tree + for (Node* child = m_shadowRoot->firstChild(); child; child = child->nextSibling()) + child->attach(); + } + + ASSERT(!m_recreateTree); + RenderSVGTransformableContainer::updateFromElement(); +} + +void RenderSVGShadowTreeRootContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderSVGTransformableContainer::styleDidChange(diff, oldStyle); + + if (RenderObject* shadowRootRenderer = m_shadowRoot ? m_shadowRoot->renderer() : 0) + shadowRootRenderer->setStyle(style()); +} + +} + +#endif diff --git a/WebCore/rendering/RenderSVGShadowTreeRootContainer.h b/WebCore/rendering/RenderSVGShadowTreeRootContainer.h new file mode 100644 index 0000000..01cd427 --- /dev/null +++ b/WebCore/rendering/RenderSVGShadowTreeRootContainer.h @@ -0,0 +1,50 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef RenderSVGShadowTreeRootContainer_h +#define RenderSVGShadowTreeRootContainer_h + +#if ENABLE(SVG) +#include "RenderSVGTransformableContainer.h" + +namespace WebCore { + +class SVGUseElement; +class SVGShadowTreeRootElement; + +class RenderSVGShadowTreeRootContainer : public RenderSVGTransformableContainer { +public: + RenderSVGShadowTreeRootContainer(SVGUseElement*); + virtual ~RenderSVGShadowTreeRootContainer(); + + void markShadowTreeForRecreation() { m_recreateTree = true; } + void updateStyle(Node::StyleChange); + virtual void updateFromElement(); + +private: + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + bool m_recreateTree; + RefPtr<SVGShadowTreeRootElement> m_shadowRoot; +}; + +} + +#endif +#endif diff --git a/WebCore/rendering/RenderSVGText.cpp b/WebCore/rendering/RenderSVGText.cpp index 3919d7f..e332c85 100644 --- a/WebCore/rendering/RenderSVGText.cpp +++ b/WebCore/rendering/RenderSVGText.cpp @@ -6,6 +6,7 @@ * 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> * 2007 Nikolas Zimmermann <zimmermann@kde.org> * 2008 Rob Buis <buis@kde.org> + * 2009 Dirk Schulze <krit@webkit.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -69,10 +70,6 @@ void RenderSVGText::mapLocalToContainer(RenderBoxModelObject* repaintContainer, void RenderSVGText::layout() { ASSERT(needsLayout()); - - // FIXME: This is a hack to avoid the RenderBlock::layout() partial repainting code which is not (yet) SVG aware - setNeedsLayout(true); - LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); // Best guess for a relative starting point @@ -184,13 +181,13 @@ FloatRect RenderSVGText::objectBoundingBox() const return boundingBox; } -FloatRect RenderSVGText::repaintRectInLocalCoordinates() const +FloatRect RenderSVGText::strokeBoundingBox() const { FloatRect repaintRect = objectBoundingBox(); // SVG needs to include the strokeWidth(), not the textStrokeWidth(). if (style()->svgStyle()->hasStroke()) { - float strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, style()->svgStyle()->strokeWidth(), 0.0f); + float strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, style()->svgStyle()->strokeWidth(), 1.0f); #if ENABLE(SVG_FONTS) const Font& font = style()->font(); @@ -205,7 +202,28 @@ FloatRect RenderSVGText::repaintRectInLocalCoordinates() const repaintRect.inflate(strokeWidth); } - repaintRect.unite(filterBoundingBoxForRenderer(this)); + return repaintRect; +} + +FloatRect RenderSVGText::repaintRectInLocalCoordinates() const +{ + FloatRect repaintRect = strokeBoundingBox(); + + // FIXME: We need to be careful here. We assume that there is no filter, + // clipper or masker if the rects are empty. + FloatRect rect = filterBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect = rect; + + rect = clipperBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect.intersect(rect); + + rect = maskerBoundingBoxForRenderer(this); + if (!rect.isEmpty()) + repaintRect.intersect(rect); + + style()->svgStyle()->inflateForShadow(repaintRect); return repaintRect; } diff --git a/WebCore/rendering/RenderSVGText.h b/WebCore/rendering/RenderSVGText.h index 9a2770b..9ae96a0 100644 --- a/WebCore/rendering/RenderSVGText.h +++ b/WebCore/rendering/RenderSVGText.h @@ -26,7 +26,7 @@ #if ENABLE(SVG) -#include "TransformationMatrix.h" +#include "AffineTransform.h" #include "RenderSVGBlock.h" namespace WebCore { @@ -40,9 +40,11 @@ public: private: virtual const char* renderName() const { return "RenderSVGText"; } + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } + virtual bool isSVGText() const { return true; } - virtual TransformationMatrix localToParentTransform() const { return m_localTransform; } + virtual const AffineTransform& localToParentTransform() const { return m_localTransform; } virtual void paint(PaintInfo&, int tx, int ty); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); @@ -60,14 +62,15 @@ private: virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; virtual FloatRect objectBoundingBox() const; + virtual FloatRect strokeBoundingBox() const; virtual FloatRect repaintRectInLocalCoordinates() const; // FIXME: This can be removed when localTransform() is removed from RenderObject - virtual TransformationMatrix localTransform() const { return m_localTransform; } + virtual AffineTransform localTransform() const { return m_localTransform; } virtual RootInlineBox* createRootInlineBox(); - TransformationMatrix m_localTransform; + AffineTransform m_localTransform; }; } diff --git a/WebCore/rendering/RenderSVGTransformableContainer.cpp b/WebCore/rendering/RenderSVGTransformableContainer.cpp index 2324eee..4bec7a7 100644 --- a/WebCore/rendering/RenderSVGTransformableContainer.cpp +++ b/WebCore/rendering/RenderSVGTransformableContainer.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org> + Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org> 2004, 2005, 2006 Rob Buis <buis@kde.org> 2009 Google, Inc. @@ -20,12 +20,12 @@ */ #include "config.h" -#if ENABLE(SVG) +#if ENABLE(SVG) #include "RenderSVGTransformableContainer.h" +#include "SVGShadowTreeElements.h" #include "SVGStyledTransformableElement.h" -#include "SVGTransformList.h" namespace WebCore { @@ -34,12 +34,12 @@ RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGStyledTransf { } -TransformationMatrix RenderSVGTransformableContainer::localToParentTransform() const +const AffineTransform& RenderSVGTransformableContainer::localToParentTransform() const { return m_localTransform; } -TransformationMatrix RenderSVGTransformableContainer::localTransform() const +AffineTransform RenderSVGTransformableContainer::localTransform() const { return m_localTransform; } @@ -47,6 +47,14 @@ TransformationMatrix RenderSVGTransformableContainer::localTransform() const void RenderSVGTransformableContainer::calculateLocalTransform() { m_localTransform = static_cast<SVGStyledTransformableElement*>(node())->animatedLocalTransform(); + if (!node()->hasTagName(SVGNames::gTag) || !static_cast<SVGGElement*>(node())->isShadowTreeContainerElement()) + return; + + FloatSize translation = static_cast<SVGShadowTreeContainerElement*>(node())->containerTranslation(); + if (translation.width() == 0 && translation.height() == 0) + return; + + m_localTransform.translate(translation.width(), translation.height()); } } diff --git a/WebCore/rendering/RenderSVGTransformableContainer.h b/WebCore/rendering/RenderSVGTransformableContainer.h index c929761..1de0b19 100644 --- a/WebCore/rendering/RenderSVGTransformableContainer.h +++ b/WebCore/rendering/RenderSVGTransformableContainer.h @@ -31,14 +31,14 @@ namespace WebCore { public: RenderSVGTransformableContainer(SVGStyledTransformableElement*); - virtual TransformationMatrix localToParentTransform() const; + virtual const AffineTransform& localToParentTransform() const; private: virtual void calculateLocalTransform(); // FIXME: This can be made non-virtual once SVGRenderTreeAsText stops using localTransform() - virtual TransformationMatrix localTransform() const; + virtual AffineTransform localTransform() const; - TransformationMatrix m_localTransform; + AffineTransform m_localTransform; }; } diff --git a/WebCore/rendering/RenderSVGViewportContainer.cpp b/WebCore/rendering/RenderSVGViewportContainer.cpp index a432ef3..103d9d2 100644 --- a/WebCore/rendering/RenderSVGViewportContainer.cpp +++ b/WebCore/rendering/RenderSVGViewportContainer.cpp @@ -38,23 +38,33 @@ RenderSVGViewportContainer::RenderSVGViewportContainer(SVGStyledElement* node) { } -void RenderSVGViewportContainer::paint(PaintInfo& paintInfo, int parentX, int parentY) +FloatRect RenderSVGViewportContainer::markerBoundaries(const AffineTransform& markerTransformation) const { - // FIXME: The if statement here evaluates to false. isEmpty() is exactly the same - // as what is on the right side, so it's basically !isEmpty && isEmpty. So this - // function does nothing. + FloatRect coordinates = repaintRectInLocalCoordinates(); - // A value of zero disables rendering of the element. - if (!m_viewport.isEmpty() && (m_viewport.width() <= 0. || m_viewport.height() <= 0.)) - return; + // Map repaint rect into parent coordinate space, in which the marker boundaries have to be evaluated + coordinates = localToParentTransform().mapRect(coordinates); - RenderSVGContainer::paint(paintInfo, parentX, parentY); + return markerTransformation.mapRect(coordinates); +} + +AffineTransform RenderSVGViewportContainer::markerContentTransformation(const AffineTransform& contentTransformation, const FloatPoint& origin, float strokeWidth) const +{ + // The 'origin' coordinate maps to SVGs refX/refY, given in coordinates relative to the viewport established by the marker + FloatPoint mappedOrigin = viewportTransform().mapPoint(origin); + + AffineTransform transformation = contentTransformation; + if (strokeWidth != -1) + transformation.scaleNonUniform(strokeWidth, strokeWidth); + + transformation.translate(-mappedOrigin.x(), -mappedOrigin.y()); + return transformation; } void RenderSVGViewportContainer::applyViewportClip(PaintInfo& paintInfo) { - if (style()->overflowX() != OVISIBLE) - paintInfo.context->clip(enclosingIntRect(m_viewport)); // FIXME: Eventually we'll want float-precision clipping + if (SVGRenderBase::isOverflowHidden(this)) + paintInfo.context->clip(m_viewport); } void RenderSVGViewportContainer::calcViewport() @@ -82,7 +92,7 @@ void RenderSVGViewportContainer::calcViewport() } } -TransformationMatrix RenderSVGViewportContainer::viewportTransform() const +AffineTransform RenderSVGViewportContainer::viewportTransform() const { if (node()->hasTagName(SVGNames::svgTag)) { SVGSVGElement* svg = static_cast<SVGSVGElement*>(node()); @@ -92,38 +102,27 @@ TransformationMatrix RenderSVGViewportContainer::viewportTransform() const return marker->viewBoxToViewTransform(m_viewport.width(), m_viewport.height()); } - return TransformationMatrix(); + return AffineTransform(); } -TransformationMatrix RenderSVGViewportContainer::localToParentTransform() const +const AffineTransform& RenderSVGViewportContainer::localToParentTransform() const { - TransformationMatrix viewportTranslation; - viewportTranslation.translate(m_viewport.x(), m_viewport.y()); - return viewportTransform() * viewportTranslation; + AffineTransform viewportTranslation(viewportTransform()); + m_localToParentTransform = viewportTranslation.translateRight(m_viewport.x(), m_viewport.y()); + return m_localToParentTransform; // If this class were ever given a localTransform(), then the above would read: // return viewportTransform() * localTransform() * viewportTranslation; } -// FIXME: This method should be removed as soon as callers to RenderBox::absoluteTransform() can be removed. -TransformationMatrix RenderSVGViewportContainer::absoluteTransform() const -{ - // This would apply localTransform() twice if localTransform() were not the identity. - return localToParentTransform() * RenderSVGContainer::absoluteTransform(); -} - bool RenderSVGViewportContainer::pointIsInsideViewportClip(const FloatPoint& pointInParent) { - // Respect the viewport clip (which is in parent coords). SVG does not support separate x/y overflow rules. - if (style()->overflowX() == OHIDDEN) { - ASSERT(style()->overflowY() == OHIDDEN); - if (!m_viewport.contains(pointInParent)) - return false; - } - return true; + // Respect the viewport clip (which is in parent coords) + if (!SVGRenderBase::isOverflowHidden(this)) + return true; + + return m_viewport.contains(pointInParent); } } #endif // ENABLE(SVG) - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderSVGViewportContainer.h b/WebCore/rendering/RenderSVGViewportContainer.h index b8b30b5..c4043ec 100644 --- a/WebCore/rendering/RenderSVGViewportContainer.h +++ b/WebCore/rendering/RenderSVGViewportContainer.h @@ -24,7 +24,6 @@ #define RenderSVGViewportContainer_h #if ENABLE(SVG) - #include "RenderSVGContainer.h" namespace WebCore { @@ -35,19 +34,19 @@ class RenderSVGViewportContainer : public RenderSVGContainer { public: RenderSVGViewportContainer(SVGStyledElement*); - // FIXME: This is only public for SVGResourceMarker::draw, likely the callsite should be changed. - TransformationMatrix viewportTransform() const; + // Calculates marker boundaries, mapped to the target element's coordinate space + FloatRect markerBoundaries(const AffineTransform& markerTransformation) const; - virtual void paint(PaintInfo&, int parentX, int parentY); + // Generates a transformation matrix usable to render marker content. Handles scaling the marker content + // acording to SVGs markerUnits="strokeWidth" concept, when a strokeWidth value != -1 is passed in. + AffineTransform markerContentTransformation(const AffineTransform& contentTransformation, const FloatPoint& origin, float strokeWidth = -1) const; private: virtual bool isSVGContainer() const { return true; } virtual const char* renderName() const { return "RenderSVGViewportContainer"; } - virtual TransformationMatrix localToParentTransform() const; - - // FIXME: This override should be removed once callers of RenderBox::absoluteTransform() can be removed. - virtual TransformationMatrix absoluteTransform() const; + AffineTransform viewportTransform() const; + virtual const AffineTransform& localToParentTransform() const; virtual void calcViewport(); @@ -55,6 +54,7 @@ private: virtual bool pointIsInsideViewportClip(const FloatPoint& pointInParent); FloatRect m_viewport; + mutable AffineTransform m_localToParentTransform; }; inline RenderSVGViewportContainer* toRenderSVGViewportContainer(RenderObject* object) @@ -70,5 +70,3 @@ void toRenderSVGViewportContainer(const RenderSVGViewportContainer*); #endif // ENABLE(SVG) #endif // RenderSVGViewportContainer_h - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderScrollbar.cpp b/WebCore/rendering/RenderScrollbar.cpp index b3c5369..63fce8d 100644 --- a/WebCore/rendering/RenderScrollbar.cpp +++ b/WebCore/rendering/RenderScrollbar.cpp @@ -40,6 +40,16 @@ RenderScrollbar::RenderScrollbar(ScrollbarClient* client, ScrollbarOrientation o : Scrollbar(client, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme()) , m_owner(renderer) { + // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created. + + // Update the scrollbar size. + updateScrollbarPart(ScrollbarBGPart); + RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart); + if (!part) + return; + + part->layout(); + setFrameRect(IntRect(0, 0, part->width(), part->height())); } RenderScrollbar::~RenderScrollbar() diff --git a/WebCore/rendering/RenderScrollbarPart.cpp b/WebCore/rendering/RenderScrollbarPart.cpp index 0f29aeb..c83248a 100644 --- a/WebCore/rendering/RenderScrollbarPart.cpp +++ b/WebCore/rendering/RenderScrollbarPart.cpp @@ -27,6 +27,7 @@ #include "RenderScrollbarPart.h" #include "RenderScrollbar.h" #include "RenderScrollbarTheme.h" +#include "RenderView.h" using namespace std; @@ -51,9 +52,6 @@ void RenderScrollbarPart::layout() else layoutVerticalPart(); - m_overflowWidth = max(width(), m_overflowWidth); - m_overflowHeight = max(height(), m_overflowHeight); - setNeedsLayout(false); } @@ -143,8 +141,16 @@ void RenderScrollbarPart::imageChanged(WrappedImagePtr image, const IntRect* rec { if (m_scrollbar && m_part != NoPart) m_scrollbar->theme()->invalidatePart(m_scrollbar, m_part); - else + else { + if (FrameView* frameView = view()->frameView()) { + if (frameView->isFrameViewScrollCorner(this)) { + frameView->invalidateScrollCorner(); + return; + } + } + RenderBlock::imageChanged(image, rect); + } } void RenderScrollbarPart::paintIntoRect(GraphicsContext* graphicsContext, int tx, int ty, const IntRect& rect) @@ -153,8 +159,6 @@ void RenderScrollbarPart::paintIntoRect(GraphicsContext* graphicsContext, int tx setLocation(rect.x() - tx, rect.y() - ty); setWidth(rect.width()); setHeight(rect.height()); - setOverflowWidth(max(rect.width(), overflowWidth())); - setOverflowHeight(max(rect.height(), overflowHeight())); if (graphicsContext->paintingDisabled()) return; diff --git a/WebCore/rendering/RenderScrollbarTheme.cpp b/WebCore/rendering/RenderScrollbarTheme.cpp index 06ca32a..19143cc 100644 --- a/WebCore/rendering/RenderScrollbarTheme.cpp +++ b/WebCore/rendering/RenderScrollbarTheme.cpp @@ -109,7 +109,7 @@ IntRect RenderScrollbarTheme::constrainTrackRectToTrackPieces(Scrollbar* scrollb void RenderScrollbarTheme::paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect) { // FIXME: Implement. - context->fillRect(cornerRect, Color::white); + context->fillRect(cornerRect, Color::white, DeviceColorSpace); } void RenderScrollbarTheme::paintScrollbarBackground(GraphicsContext* context, Scrollbar* scrollbar) diff --git a/WebCore/rendering/RenderSelectionInfo.h b/WebCore/rendering/RenderSelectionInfo.h index e7b7b78..c06a9ae 100644 --- a/WebCore/rendering/RenderSelectionInfo.h +++ b/WebCore/rendering/RenderSelectionInfo.h @@ -30,7 +30,7 @@ namespace WebCore { -class RenderSelectionInfoBase { +class RenderSelectionInfoBase : public Noncopyable { public: RenderSelectionInfoBase() : m_object(0) diff --git a/WebCore/rendering/RenderSlider.cpp b/WebCore/rendering/RenderSlider.cpp index 8a19d97..7398a2f 100644 --- a/WebCore/rendering/RenderSlider.cpp +++ b/WebCore/rendering/RenderSlider.cpp @@ -52,9 +52,10 @@ 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; + bool hasStep; + double step; double minimum; - double maximum; + double maximum; // maximum must be >= minimum. explicit SliderRange(HTMLInputElement*); double clampValue(double value); @@ -79,33 +80,40 @@ struct SliderRange { 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"); - - // 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); + if (element->hasAttribute(precisionAttr)) { + step = 1.0; + hasStep = !equalIgnoringCase(element->getAttribute(precisionAttr), "float"); + } else + hasStep = element->getAllowedValueStep(&step); + + maximum = element->maximum(); + minimum = element->minimum(); } double SliderRange::clampValue(double value) { double clampedValue = max(minimum, min(value, maximum)); - return isIntegral ? round(clampedValue) : clampedValue; + if (!hasStep) + return clampedValue; + // Rounds clampedValue to minimum + N * step. + clampedValue = minimum + round((clampedValue - minimum) / step) * step; + if (clampedValue > maximum) + clampedValue -= step; + ASSERT(clampedValue >= minimum); + ASSERT(clampedValue <= maximum); + return clampedValue; } double SliderRange::valueFromElement(HTMLInputElement* element, bool* wasClamped) { - String valueString = element->value(); - double oldValue = valueString.isNull() ? (minimum + maximum) / 2 : valueString.toDouble(); + double oldValue; + bool parseSuccess = HTMLInputElement::formStringToDouble(element->value(), &oldValue); + if (!parseSuccess) + oldValue = (minimum + maximum) / 2; double newValue = clampValue(oldValue); if (wasClamped) - *wasClamped = valueString.isNull() || newValue != oldValue; + *wasClamped = !parseSuccess || newValue != oldValue; return newValue; } @@ -285,6 +293,8 @@ PassRefPtr<RenderStyle> RenderSlider::createThumbStyle(const RenderStyle* parent style->setAppearance(SliderThumbHorizontalPart); else if (parentStyle->appearance() == MediaSliderPart) style->setAppearance(MediaSliderThumbPart); + else if (parentStyle->appearance() == MediaVolumeSliderPart) + style->setAppearance(MediaVolumeSliderThumbPart); return style.release(); } @@ -302,7 +312,7 @@ IntRect RenderSlider::thumbRect() double fraction = sliderPosition(static_cast<HTMLInputElement*>(node())); IntRect contentRect = contentBoxRect(); - if (style()->appearance() == SliderVerticalPart) { + if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart) { 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 { @@ -340,8 +350,6 @@ void RenderSlider::layout() calcWidth(); calcHeight(); - IntRect overflowRect(IntPoint(), size()); - if (thumb) { if (oldSize != size()) thumb->setChildNeedsLayout(true, false); @@ -358,19 +366,9 @@ void RenderSlider::layout() thumb->repaintDuringLayoutIfMoved(oldThumbRect); statePusher.pop(); - - IntRect thumbOverflowRect = thumb->overflowRect(); - thumbOverflowRect.move(thumb->x(), thumb->y()); - overflowRect.unite(thumbOverflowRect); + addOverflowFromChild(thumb); } - // 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); @@ -385,7 +383,7 @@ void RenderSlider::updateFromElement() bool clamped; double value = range.valueFromElement(element, &clamped); if (clamped) - element->setValueFromRenderer(String::number(value)); + element->setValueFromRenderer(HTMLInputElement::formStringFromDouble(value)); // Layout will take care of the thumb's size and position. if (!m_thumb) { @@ -406,7 +404,7 @@ bool RenderSlider::mouseEventIsInThumb(MouseEvent* evt) return false; #if ENABLE(VIDEO) - if (style()->appearance() == MediaSliderPart) { + if (style()->appearance() == MediaSliderPart || style()->appearance() == MediaVolumeSliderPart) { MediaControlInputElement *sliderThumb = static_cast<MediaControlInputElement*>(m_thumb->renderer()->node()); return sliderThumb->hitTest(evt->absoluteLocation()); } @@ -438,10 +436,10 @@ void RenderSlider::setValueForPosition(int position) // 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) + if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart) fraction = 1 - fraction; double value = range.clampValue(range.valueFromProportion(fraction)); - element->setValueFromRenderer(String::number(value)); + element->setValueFromRenderer(HTMLInputElement::formStringFromDouble(value)); // Also update the position if appropriate. if (position != currentPosition()) { @@ -459,7 +457,7 @@ int RenderSlider::positionForOffset(const IntPoint& p) return 0; int position; - if (style()->appearance() == SliderVerticalPart) + if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart) position = p.y() - m_thumb->renderBox()->height() / 2; else position = p.x() - m_thumb->renderBox()->width() / 2; @@ -472,7 +470,7 @@ int RenderSlider::currentPosition() ASSERT(m_thumb); ASSERT(m_thumb->renderer()); - if (style()->appearance() == SliderVerticalPart) + if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart) return toRenderBox(m_thumb->renderer())->y() - contentBoxRect().y(); return toRenderBox(m_thumb->renderer())->x() - contentBoxRect().x(); } @@ -482,7 +480,7 @@ int RenderSlider::trackSize() ASSERT(m_thumb); ASSERT(m_thumb->renderer()); - if (style()->appearance() == SliderVerticalPart) + if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart) return contentHeight() - m_thumb->renderBox()->height(); return contentWidth() - m_thumb->renderBox()->width(); } diff --git a/WebCore/rendering/RenderTable.cpp b/WebCore/rendering/RenderTable.cpp index 7599999..61e05ad 100644 --- a/WebCore/rendering/RenderTable.cpp +++ b/WebCore/rendering/RenderTable.cpp @@ -62,8 +62,8 @@ RenderTable::RenderTable(Node* node) , m_borderRight(0) { #ifdef ANDROID_LAYOUT - m_singleColumn = false; -#endif + m_singleColumn = false; +#endif m_columnPos.fill(0, 2); m_columns.fill(ColumnStruct(), 1); } @@ -208,7 +208,7 @@ void RenderTable::calcWidth() } } #endif - + if (isPositioned()) calcAbsoluteHorizontal(); @@ -248,7 +248,7 @@ void RenderTable::calcWidth() // in SSR mode, we ignore left/right margin for table if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) return; -#endif +#endif calcHorizontalMargins(style()->marginLeft(), style()->marginRight(), availableWidth); } @@ -265,21 +265,21 @@ void RenderTable::layout() LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y())); setHeight(0); - m_overflowHeight = 0; - m_overflowTop = 0; + m_overflow.clear(); + initMaxMarginValues(); #ifdef ANDROID_LAYOUT bool relayoutChildren = false; int oldVisibleWidth = m_visibleWidth; #endif - + int oldWidth = width(); calcWidth(); #ifdef ANDROID_LAYOUT if (oldVisibleWidth != m_visibleWidth - && document()->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) + && document()->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) relayoutChildren = true; 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, @@ -298,7 +298,7 @@ void RenderTable::layout() } if (shouldRenderAsSingleColumn) { - m_singleColumn = true; + m_singleColumn = true; if (width() > cw) setWidth(cw); if (m_minPrefWidth > cw) @@ -354,9 +354,6 @@ void RenderTable::layout() if (m_caption) m_caption->layoutIfNeeded(); - m_overflowWidth = width() + (collapsing ? outerBorderRight() - borderRight() : 0); - m_overflowLeft = collapsing ? borderLeft() - outerBorderLeft() : 0; - // If any table section moved vertically, we will just repaint everything from that // section down (it is quite unlikely that any of the following sections // did not shift). @@ -372,10 +369,6 @@ void RenderTable::layout() m_caption->repaintDuringLayoutIfMoved(captionRect); setHeight(height() + m_caption->height() + m_caption->marginTop() + m_caption->marginBottom()); - m_overflowLeft = min(m_overflowLeft, m_caption->x() + m_caption->overflowLeft(false)); - m_overflowWidth = max(m_overflowWidth, m_caption->x() + m_caption->overflowWidth(false)); - m_overflowTop = min(m_overflowTop, m_caption->y() + m_caption->overflowTop(false)); - m_overflowHeight = max(m_overflowHeight, m_caption->y() + m_caption->overflowHeight(false)); if (height() != oldTableTop) { sectionMoved = true; @@ -421,15 +414,11 @@ void RenderTable::layout() while (section) { if (!sectionMoved && section->y() != height()) { sectionMoved = true; - movedSectionTop = min(height(), section->y()) + section->overflowTop(false); + movedSectionTop = min(height(), section->y()) + section->topVisibleOverflow(); } section->setLocation(bl, height()); setHeight(height() + section->height()); - m_overflowLeft = min(m_overflowLeft, section->x() + section->overflowLeft(false)); - m_overflowWidth = max(m_overflowWidth, section->x() + section->overflowWidth(false)); - m_overflowTop = min(m_overflowTop, section->y() + section->overflowTop(false)); - m_overflowHeight = max(m_overflowHeight, section->y() + section->overflowHeight(false)); section = sectionBelow(section); } @@ -443,37 +432,34 @@ void RenderTable::layout() m_caption->repaintDuringLayoutIfMoved(captionRect); setHeight(height() + m_caption->height() + m_caption->marginTop() + m_caption->marginBottom()); - m_overflowLeft = min(m_overflowLeft, m_caption->x() + m_caption->overflowLeft(false)); - m_overflowWidth = max(m_overflowWidth, m_caption->x() + m_caption->overflowWidth(false)); } if (isPositioned()) calcHeight(); - m_overflowHeight = max(m_overflowHeight, height()); - // table can be containing block of positioned elements. // FIXME: Only pass true if width or height changed. layoutPositionedObjects(true); - if (!hasOverflowClip()) { - int shadowLeft; - int shadowRight; - int shadowTop; - int shadowBottom; - style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); - - m_overflowLeft = min(m_overflowLeft, shadowLeft); - m_overflowWidth = max(m_overflowWidth, width() + shadowRight); - m_overflowTop = min(m_overflowTop, shadowTop); - m_overflowHeight = max(m_overflowHeight, height() + shadowBottom); - - if (hasReflection()) { - IntRect reflection(reflectionBox()); - m_overflowTop = min(m_overflowTop, reflection.y()); - m_overflowHeight = max(m_overflowHeight, reflection.bottom()); - m_overflowLeft = min(m_overflowLeft, reflection.x()); - m_overflowHeight = max(m_overflowWidth, reflection.right()); + // Add overflow from borders. + int rightBorderOverflow = width() + (collapsing ? outerBorderRight() - borderRight() : 0); + int leftBorderOverflow = collapsing ? borderLeft() - outerBorderLeft() : 0; + int bottomBorderOverflow = height() + (collapsing ? outerBorderBottom() - borderBottom() : 0); + int topBorderOverflow = collapsing ? borderTop() - outerBorderTop() : 0; + addLayoutOverflow(IntRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow)); + + // Add visual overflow from box-shadow and reflections. + addShadowOverflow(); + + // Add overflow from our caption. + if (m_caption) + addOverflowFromChild(m_caption); + + // Add overflow from our sections. + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isTableSection()) { + RenderTableSection* section = toRenderTableSection(child); + addOverflowFromChild(section); } } @@ -482,7 +468,7 @@ void RenderTable::layout() bool didFullRepaint = repainter.repaintAfterLayout(); // Repaint with our new bounds if they are different from our old bounds. if (!didFullRepaint && sectionMoved) - repaintRectangle(IntRect(m_overflowLeft, movedSectionTop, m_overflowWidth - m_overflowLeft, m_overflowHeight - movedSectionTop)); + repaintRectangle(IntRect(leftVisibleOverflow(), movedSectionTop, rightVisibleOverflow() - leftVisibleOverflow(), bottomVisibleOverflow() - movedSectionTop)); setNeedsLayout(false); } @@ -503,9 +489,9 @@ void RenderTable::paint(PaintInfo& paintInfo, int tx, int ty) PaintPhase paintPhase = paintInfo.phase; int os = 2 * maximalOutlineSize(paintPhase); - if (ty + overflowTop(false) >= paintInfo.rect.bottom() + os || ty + overflowHeight(false) <= paintInfo.rect.y() - os) + if (ty + topVisibleOverflow() >= paintInfo.rect.bottom() + os || ty + bottomVisibleOverflow() <= paintInfo.rect.y() - os) return; - if (tx + overflowLeft(false) >= paintInfo.rect.right() + os || tx + overflowWidth(false) <= paintInfo.rect.x() - os) + if (tx + leftVisibleOverflow() >= paintInfo.rect.right() + os || tx + rightVisibleOverflow() <= paintInfo.rect.x() - os) return; bool pushedClip = pushContentsClip(paintInfo, tx, ty); @@ -566,6 +552,9 @@ void RenderTable::paintObject(PaintInfo& paintInfo, int tx, int ty) void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) { + if (!shouldPaintWithinRoot(paintInfo)) + return; + int w = width(); int h = height(); diff --git a/WebCore/rendering/RenderTableCell.cpp b/WebCore/rendering/RenderTableCell.cpp index 8b7a068..d97ae6e 100644 --- a/WebCore/rendering/RenderTableCell.cpp +++ b/WebCore/rendering/RenderTableCell.cpp @@ -168,6 +168,17 @@ void RenderTableCell::setOverrideSize(int size) RenderBlock::setOverrideSize(size); } +IntSize RenderTableCell::offsetFromContainer(RenderObject* o) const +{ + ASSERT(o == container()); + + IntSize offset = RenderBlock::offsetFromContainer(o); + if (parent()) + offset.expand(-parentBox()->x(), -parentBox()->y()); + + return offset; +} + IntRect RenderTableCell::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { // If the table grid is dirty, we cannot get reliable information about adjoining cells, @@ -207,9 +218,9 @@ IntRect RenderTableCell::clippedOverflowRectForRepaint(RenderBoxModelObject* rep right = max(right, below->borderHalfRight(true)); } } - left = max(left, -overflowLeft(false)); - top = max(top, -overflowTop(false)); - IntRect r(-left, - top, left + max(width() + right, overflowWidth(false)), top + max(height() + bottom, overflowHeight(false))); + left = max(left, -leftVisibleOverflow()); + top = max(top, -topVisibleOverflow()); + IntRect r(-left, - top, left + max(width() + right, rightVisibleOverflow()), top + max(height() + bottom, bottomVisibleOverflow())); if (RenderView* v = view()) { // FIXME: layoutDelta needs to be applied in parts before/after transforms and @@ -226,35 +237,11 @@ void RenderTableCell::computeRectForRepaint(RenderBoxModelObject* repaintContain return; r.setY(r.y()); RenderView* v = view(); - if ((!v || !v->layoutStateEnabled()) && parent()) + if ((!v || !v->layoutStateEnabled() || repaintContainer) && parent()) r.move(-parentBox()->x(), -parentBox()->y()); // Rows are in the same coordinate space, so don't add their offset in. RenderBlock::computeRectForRepaint(repaintContainer, r, fixed); } -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. - // FIXME: this is wrong with transforms - transformState.move(-parentBox()->x(), -parentBox()->y()); - } - RenderBlock::mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); -} - -void RenderTableCell::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const -{ - RenderBlock::mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); - if (parent()) { - // Rows are in the same coordinate space, so add their offset back in. - // FIXME: this is wrong with transforms - transformState.move(parentBox()->x(), parentBox()->y()); - } -} - int RenderTableCell::baselinePosition(bool firstLine, bool isRootLineBox) const { if (isRootLineBox) @@ -656,6 +643,9 @@ int RenderTableCell::borderHalfBottom(bool outer) const void RenderTableCell::paint(PaintInfo& paintInfo, int tx, int ty) { if (paintInfo.phase == PaintPhaseCollapsedTableBorders && style()->visibility() == VISIBLE) { + if (!shouldPaintWithinRoot(paintInfo)) + return; + tx += x(); ty += y(); int os = 2 * maximalOutlineSize(paintInfo.phase); @@ -827,6 +817,9 @@ void RenderTableCell::paintCollapsedBorder(GraphicsContext* graphicsContext, int void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, int ty, RenderObject* backgroundObject) { + if (!shouldPaintWithinRoot(paintInfo)) + return; + if (!backgroundObject) return; @@ -858,7 +851,7 @@ void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, i paintInfo.context->save(); paintInfo.context->clip(clipRect); } - paintFillLayers(paintInfo, c, bgLayer, tx, ty, w, h); + paintFillLayers(paintInfo, c, bgLayer, tx, ty, w, h, CompositeSourceOver, backgroundObject); if (shouldClip) paintInfo.context->restore(); } @@ -866,6 +859,9 @@ void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, i void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) { + if (!shouldPaintWithinRoot(paintInfo)) + return; + RenderTable* tableElt = table(); if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; diff --git a/WebCore/rendering/RenderTableCell.h b/WebCore/rendering/RenderTableCell.h index 9532bb6..0f8580d 100644 --- a/WebCore/rendering/RenderTableCell.h +++ b/WebCore/rendering/RenderTableCell.h @@ -106,13 +106,12 @@ public: virtual void setOverrideSize(int); + bool hasVisibleOverflow() const { return m_overflow; } + protected: virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); - virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; - virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const; - private: virtual const char* renderName() const { return isAnonymous() ? "RenderTableCell (anonymous)" : "RenderTableCell"; } @@ -127,6 +126,7 @@ private: virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); virtual void paintMask(PaintInfo&, int tx, int ty); + virtual IntSize offsetFromContainer(RenderObject*) const; virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); diff --git a/WebCore/rendering/RenderTableRow.cpp b/WebCore/rendering/RenderTableRow.cpp index bafadfc..a11a14b 100644 --- a/WebCore/rendering/RenderTableRow.cpp +++ b/WebCore/rendering/RenderTableRow.cpp @@ -1,6 +1,4 @@ /** - * This file is part of the DOM implementation for KDE. - * * Copyright (C) 1997 Martin Jones (mjones@kde.org) * (C) 1997 Torben Weis (weis@kde.org) * (C) 1998 Waldo Bastian (bastian@kde.org) diff --git a/WebCore/rendering/RenderTableSection.cpp b/WebCore/rendering/RenderTableSection.cpp index 5d47357..a2457e1 100644 --- a/WebCore/rendering/RenderTableSection.cpp +++ b/WebCore/rendering/RenderTableSection.cpp @@ -46,6 +46,14 @@ namespace WebCore { using namespace HTMLNames; +static inline void setRowHeightToRowStyleHeightIfNotRelative(RenderTableSection::RowStruct* row) +{ + ASSERT(row && row->rowRenderer); + row->height = row->rowRenderer->style()->height(); + if (row->height.isRelative()) + row->height = Length(); +} + RenderTableSection::RenderTableSection(Node* node) : RenderBox(node) , m_gridRows(0) @@ -55,10 +63,6 @@ RenderTableSection::RenderTableSection(Node* node) , m_outerBorderRight(0) , m_outerBorderTop(0) , m_outerBorderBottom(0) - , m_overflowLeft(0) - , m_overflowWidth(0) - , m_overflowTop(0) - , m_overflowHeight(0) , m_needsCellRecalc(false) , m_hasOverflowingCell(false) { @@ -130,11 +134,8 @@ void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild m_grid[m_cRow].rowRenderer = toRenderTableRow(child); - if (!beforeChild) { - m_grid[m_cRow].height = child->style()->height(); - if (m_grid[m_cRow].height.isRelative()) - m_grid[m_cRow].height = Length(); - } + if (!beforeChild) + setRowHeightToRowStyleHeightIfNotRelative(&m_grid[m_cRow]); // If the next renderer is actually wrapped in an anonymous table row, we need to go up and find that. while (beforeChild && beforeChild->parent() != this) @@ -266,12 +267,11 @@ void RenderTableSection::setCellWidths() if (view()->frameView()) { const Settings* settings = document()->settings(); ASSERT(settings); - if (settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) { + if (settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) visibleWidth = view()->frameView()->screenWidth(); - } } #endif - + for (int i = 0; i < m_gridRows; i++) { Row& row = *m_grid[i].row; int cols = row.size(); @@ -288,12 +288,13 @@ void RenderTableSection::setCellWidths() endCol++; } int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing(); -#ifdef ANDROID_LAYOUT - if (table()->isSingleColumn()) - w = table()->width()-(table()->borderLeft()+table()->borderRight()+ - (table()->collapseBorders()?0:(table()->paddingLeft()+table()->paddingRight()+ - 2*table()->hBorderSpacing()))); -#endif +#ifdef ANDROID_LAYOUT + if (table()->isSingleColumn()) { + int b = table()->collapseBorders() ? + 0 : table()->paddingLeft() + table()->paddingRight() + 2 * table()->hBorderSpacing(); + w = table()->width() - (table()->borderLeft() + table()->borderRight() + b); + } +#endif int oldWidth = cell->width(); #ifdef ANDROID_LAYOUT if (w != oldWidth || (visibleWidth > 0 && visibleWidth != cell->getVisibleWidth())) { @@ -328,9 +329,14 @@ int RenderTableSection::calcRowHeight() ASSERT(!needsLayout()); #ifdef ANDROID_LAYOUT - if (table()->isSingleColumn()) - return m_rowPos[m_gridRows]; -#endif + if (table()->isSingleColumn()) { + int height = 0; + int spacing = table()->vBorderSpacing(); + for (int r = 0; r < m_gridRows; r++) + height += m_grid[r].height.calcMinValue(0) + (m_grid[r].rowRenderer ? spacing : 0); + return height; + } +#endif RenderTableCell* cell; @@ -450,26 +456,26 @@ int RenderTableSection::layoutRows(int toAdd) int hspacing = table()->hBorderSpacing(); int vspacing = table()->vBorderSpacing(); int rHeight = vspacing; - + int leftOffset = hspacing; - + int nEffCols = table()->numEffCols(); for (int r = 0; r < totalRows; r++) { for (int c = 0; c < nEffCols; c++) { CellStruct current = cellAt(r, c); RenderTableCell* cell = current.cell; - + if (!cell || current.inColSpan) continue; if (r > 0 && (cellAt(r-1, c).cell == cell)) continue; - + // cell->setCellTopExtra(0); // cell->setCellBottomExtra(0); - + int oldCellX = cell->x(); int oldCellY = cell->y(); - + if (style()->direction() == RTL) { cell->setX(table()->width()); cell->setY(rHeight); @@ -489,7 +495,7 @@ int RenderTableSection::layoutRows(int toAdd) rHeight += cell->height() + vspacing; } } - + setHeight(rHeight); return height(); } @@ -501,10 +507,7 @@ int RenderTableSection::layoutRows(int toAdd) // Set the width of our section now. The rows will also be this width. setWidth(table()->contentWidth()); - m_overflowLeft = 0; - m_overflowWidth = width(); - m_overflowTop = 0; - m_overflowHeight = 0; + m_overflow.clear(); m_hasOverflowingCell = false; if (toAdd && totalRows && (m_rowPos[totalRows] || !nextSibling())) { @@ -705,12 +708,6 @@ int RenderTableSection::layoutRows(int toAdd) } else cell->setLocation(table()->columnPositions()[c] + hspacing, m_rowPos[rindx]); - m_overflowLeft = min(m_overflowLeft, cell->x() + cell->overflowLeft(false)); - m_overflowWidth = max(m_overflowWidth, cell->x() + cell->overflowWidth(false)); - m_overflowTop = min(m_overflowTop, cell->y() + cell->overflowTop(false)); - m_overflowHeight = max(m_overflowHeight, cell->y() + cell->overflowHeight(false)); - m_hasOverflowingCell |= cell->overflowLeft(false) || cell->overflowWidth(false) > cell->width() || cell->overflowTop(false) || cell->overflowHeight(false) > cell->height(); - // If the cell moved, we have to repaint it as well as any floating/positioned // descendants. An exception is if we need a layout. In this case, we know we're going to // repaint ourselves (and the cell) anyway. @@ -725,10 +722,22 @@ int RenderTableSection::layoutRows(int toAdd) ASSERT(!needsLayout()); - statePusher.pop(); - setHeight(m_rowPos[totalRows]); - m_overflowHeight = max(m_overflowHeight, height()); + + // Now that our height has been determined, add in overflow from cells. + for (int r = 0; r < totalRows; r++) { + for (int c = 0; c < nEffCols; c++) { + RenderTableCell* cell = cellAt(r, c).cell; + if (!cell) + continue; + if (r < totalRows - 1 && cell == cellAt(r + 1, c).cell) + continue; + addOverflowFromChild(cell); + m_hasOverflowingCell |= cell->hasVisibleOverflow(); + } + } + + statePusher.pop(); return height(); } @@ -1058,7 +1067,7 @@ void RenderTableSection::paintObject(PaintInfo& paintInfo, int tx, int ty) unsigned int startcol = 0; unsigned int endcol = totalCols; if (table()->isSingleColumn()) { - // FIXME: should we be smarter too? + // FIXME: should we be smarter too? } else { // FIXME: possible to rollback to the common tree. // rowPos size is set in calcRowHeight(), which is called from table layout(). @@ -1155,6 +1164,7 @@ void RenderTableSection::paintObject(PaintInfo& paintInfo, int tx, int ty) if (!row->hasSelfPaintingLayer()) cell->paintBackgroundsBehindCell(paintInfo, tx, ty, row); } + if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()) || paintInfo.phase == PaintPhaseCollapsedTableBorders) cell->paint(paintInfo, tx, ty); } @@ -1184,6 +1194,7 @@ void RenderTableSection::recalcCells() RenderTableRow* tableRow = toRenderTableRow(row); m_grid[m_cRow].rowRenderer = tableRow; + setRowHeightToRowStyleHeightIfNotRelative(&m_grid[m_cRow]); for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { if (cell->isTableCell()) diff --git a/WebCore/rendering/RenderTableSection.h b/WebCore/rendering/RenderTableSection.h index c0098bc..9f6d5ea 100644 --- a/WebCore/rendering/RenderTableSection.h +++ b/WebCore/rendering/RenderTableSection.h @@ -73,11 +73,6 @@ public: void appendColumn(int pos); void splitColumn(int pos, int newSize); - virtual int overflowWidth(bool includeInterior = true) const { return (!includeInterior && hasOverflowClip()) ? width() : m_overflowWidth; } - virtual int overflowLeft(bool includeInterior = true) const { return (!includeInterior && hasOverflowClip()) ? 0 : m_overflowLeft; } - virtual int overflowHeight(bool includeInterior = true) const { return (!includeInterior && hasOverflowClip()) ? height() : m_overflowHeight; } - virtual int overflowTop(bool includeInterior = true) const { return (!includeInterior && hasOverflowClip()) ? 0 : m_overflowTop; } - int calcOuterBorderTop() const; int calcOuterBorderBottom() const; int calcOuterBorderLeft(bool rtl) const; @@ -152,10 +147,6 @@ private: int m_outerBorderRight; int m_outerBorderTop; int m_outerBorderBottom; - int m_overflowLeft; - int m_overflowWidth; - int m_overflowTop; - int m_overflowHeight; bool m_needsCellRecalc; bool m_hasOverflowingCell; diff --git a/WebCore/rendering/RenderText.cpp b/WebCore/rendering/RenderText.cpp index ada3961..2e696a9 100644 --- a/WebCore/rendering/RenderText.cpp +++ b/WebCore/rendering/RenderText.cpp @@ -25,7 +25,9 @@ #include "config.h" #include "RenderText.h" +#include "AXObjectCache.h" #include "CharacterNames.h" +#include "EllipsisBox.h" #include "FloatQuad.h" #include "FrameView.h" #include "InlineTextBox.h" @@ -207,7 +209,7 @@ void RenderText::deleteTextBoxes() PassRefPtr<StringImpl> RenderText::originalText() const { Node* e = node(); - return e ? static_cast<Text*>(e)->string() : 0; + return e ? static_cast<Text*>(e)->dataImpl() : 0; } void RenderText::absoluteRects(Vector<IntRect>& rects, int tx, int ty) @@ -286,7 +288,7 @@ void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, } else { unsigned realEnd = min(box->end() + 1, end); IntRect r = box->selectionRect(0, 0, start, realEnd); - if (!r.isEmpty()) { + if (r.height()) { if (!useSelectionHeight) { // change the height and y position because selectionRect uses selection-specific values r.setHeight(box->height()); @@ -328,23 +330,23 @@ VisiblePosition RenderText::positionForPoint(const IntPoint& point) int offset; // FIXME: We should be able to roll these special cases into the general cases in the loop below. - if (firstTextBox() && point.y() < firstTextBox()->root()->bottomOverflow() && point.x() < firstTextBox()->m_x) { + if (firstTextBox() && point.y() < firstTextBox()->root()->lineBottom() && 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(point.x()); return createVisiblePosition(offset + firstTextBox()->start(), DOWNSTREAM); } - if (lastTextBox() && point.y() >= lastTextBox()->root()->topOverflow() && point.x() >= lastTextBox()->m_x + lastTextBox()->m_width) { + if (lastTextBox() && point.y() >= lastTextBox()->root()->lineTop() && 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(point.x()); - return createVisiblePosition(offset + lastTextBox()->start(), DOWNSTREAM); + return createVisiblePosition(offset + lastTextBox()->start(), VP_UPSTREAM_IF_POSSIBLE); } InlineTextBox* lastBoxAbove = 0; for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { - if (point.y() >= box->root()->topOverflow()) { - int bottom = box->root()->nextRootBox() ? box->root()->nextRootBox()->topOverflow() : box->root()->bottomOverflow(); + if (point.y() >= box->root()->lineTop()) { + int bottom = box->root()->nextRootBox() ? box->root()->nextRootBox()->lineTop() : box->root()->lineBottom(); if (point.y() < bottom) { offset = box->offsetForPosition(point.x()); @@ -387,8 +389,8 @@ IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* e InlineTextBox* box = static_cast<InlineTextBox*>(inlineBox); - int height = box->root()->bottomOverflow() - box->root()->topOverflow(); - int top = box->root()->topOverflow(); + int height = box->root()->lineBottom() - box->root()->lineTop(); + int top = box->root()->lineTop(); int left = box->positionForOffset(caretOffset); @@ -757,6 +759,17 @@ void RenderText::calcPrefWidths(int leadWidth, HashSet<const SimpleFontData*>& f setPrefWidthsDirty(false); } +bool RenderText::isAllCollapsibleWhitespace() +{ + int length = textLength(); + const UChar* text = characters(); + for (int i = 0; i < length; i++) { + if (!style()->isCollapsibleWhiteSpace(text[i])) + return false; + } + return true; +} + bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const { unsigned currPos; @@ -813,7 +826,9 @@ void RenderText::setSelectionState(SelectionState state) } } - containingBlock()->setSelectionState(state); + // The returned value can be null in case of an orphaned tree. + if (RenderBlock* cb = containingBlock()) + cb->setSelectionState(state); } void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force) @@ -1001,6 +1016,10 @@ void RenderText::setText(PassRefPtr<StringImpl> text, bool force) setTextInternal(text); setNeedsLayoutAndPrefWidthsRecalc(); m_knownNotToUseFallbackFonts = false; + + AXObjectCache* axObjectCache = document()->axObjectCache(); + if (axObjectCache->accessibilityEnabled()) + axObjectCache->contentChanged(this); } int RenderText::lineHeight(bool firstLine, bool) const @@ -1047,8 +1066,15 @@ void RenderText::positionLineBox(InlineBox* box) if (!s->len()) { // We want the box to be destroyed. s->remove(); + if (m_firstTextBox == s) + m_firstTextBox = s->nextTextBox(); + else + s->prevTextBox()->setNextLineBox(s->nextTextBox()); + if (m_lastTextBox == s) + m_lastTextBox = s->prevTextBox(); + else + s->nextTextBox()->setPreviousLineBox(s->prevTextBox()); s->destroy(renderArena()); - m_firstTextBox = m_lastTextBox = 0; return; } @@ -1151,9 +1177,25 @@ IntRect RenderText::selectionRectForRepaint(RenderBoxModelObject* repaintContain return IntRect(); IntRect rect; - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { rect.unite(box->selectionRect(0, 0, startPos, endPos)); + // Check if there are ellipsis which fall within the selection. + unsigned short truncation = box->truncation(); + if (truncation != cNoTruncation) { + if (EllipsisBox* ellipsis = box->root()->ellipsisBox()) { + int ePos = min<int>(endPos - box->start(), box->len()); + int sPos = max<int>(startPos - box->start(), 0); + // The ellipsis should be considered to be selected if the end of + // the selection is past the beginning of the truncation and the + // beginning of the selection is before or at the beginning of the + // truncation. + if (ePos >= truncation && sPos <= truncation) + rect.unite(ellipsis->selectionRect(0, 0)); + } + } + } + if (clipToVisibleContent) computeRectForRepaint(repaintContainer, rect); else { @@ -1349,7 +1391,7 @@ void RenderText::checkConsistency() const #ifdef CHECK_CONSISTENCY const InlineTextBox* prev = 0; for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) { - ASSERT(child->object() == this); + ASSERT(child->renderer() == this); ASSERT(child->prevTextBox() == prev); prev = child; } diff --git a/WebCore/rendering/RenderText.h b/WebCore/rendering/RenderText.h index 915ff40..d46bce9 100644 --- a/WebCore/rendering/RenderText.h +++ b/WebCore/rendering/RenderText.h @@ -121,7 +121,8 @@ public: void checkConsistency() const; virtual void calcPrefWidths(int leadWidth); - + bool isAllCollapsibleWhitespace(); + protected: virtual void styleWillChange(StyleDifference, const RenderStyle*) { } virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); diff --git a/WebCore/rendering/RenderTextControl.cpp b/WebCore/rendering/RenderTextControl.cpp index 70b6518..c0ba070 100644 --- a/WebCore/rendering/RenderTextControl.cpp +++ b/WebCore/rendering/RenderTextControl.cpp @@ -67,10 +67,11 @@ static Color disabledTextColor(const Color& textColor, const Color& backgroundCo return disabledColor; } -RenderTextControl::RenderTextControl(Node* node) +RenderTextControl::RenderTextControl(Node* node, bool placeholderVisible) : RenderBlock(node) - , m_edited(false) - , m_userEdited(false) + , m_placeholderVisible(placeholderVisible) + , m_wasChangedSinceLastChangeEvent(false) + , m_lastChangeWasUserEdit(false) { } @@ -92,14 +93,22 @@ void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* // Reset them now to avoid getting a spurious layout hint. textBlockRenderer->style()->setHeight(Length()); textBlockRenderer->style()->setWidth(Length()); - textBlockRenderer->setStyle(textBlockStyle); + setInnerTextStyle(textBlockStyle); + } + + setReplaced(isInline()); +} + +void RenderTextControl::setInnerTextStyle(PassRefPtr<RenderStyle> style) +{ + if (m_innerText) { + RefPtr<RenderStyle> textStyle = style; + m_innerText->renderer()->setStyle(textStyle); for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) { if (n->renderer()) - n->renderer()->setStyle(textBlockStyle); + n->renderer()->setStyle(textStyle); } } - - setReplaced(isInline()); } static inline bool updateUserModifyProperty(Node* node, RenderStyle* style) @@ -173,7 +182,7 @@ void RenderTextControl::setInnerTextValue(const String& innerTextValue) frame->editor()->clearUndoRedoOperations(); if (AXObjectCache::accessibilityEnabled()) - document()->axObjectCache()->postNotification(this, "AXValueChanged", false); + document()->axObjectCache()->postNotification(this, AXObjectCache::AXValueChanged, false); } } @@ -186,17 +195,17 @@ void RenderTextControl::setInnerTextValue(const String& innerTextValue) ASSERT(!ec); } - m_edited = false; - m_userEdited = false; + // We set m_lastChangeWasUserEdit to false since this change was not explicitly made by the user (say, via typing on the keyboard), see <rdar://problem/5359921>. + m_lastChangeWasUserEdit = false; } static_cast<Element*>(node())->setFormControlValueMatchesRenderer(true); } -void RenderTextControl::setUserEdited(bool isUserEdited) +void RenderTextControl::setLastChangeWasUserEdit(bool lastChangeWasUserEdit) { - m_userEdited = isUserEdited; - document()->setIgnoreAutofocus(isUserEdited); + m_lastChangeWasUserEdit = lastChangeWasUserEdit; + document()->setIgnoreAutofocus(lastChangeWasUserEdit); } int RenderTextControl::selectionStart() @@ -303,15 +312,12 @@ int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos) void RenderTextControl::subtreeHasChanged() { - m_edited = true; - m_userEdited = true; + m_wasChangedSinceLastChangeEvent = true; + m_lastChangeWasUserEdit = true; } String RenderTextControl::finishText(Vector<UChar>& result) const { - // ANDROID: This method was modified with a fix from WebKit r31081. This - // comment can be removed the next time we update. - // Remove one trailing newline; there's always one that's collapsed out by rendering. size_t size = result.size(); if (size && result[size - 1] == '\n') @@ -486,18 +492,6 @@ void RenderTextControl::calcPrefWidths() m_minPrefWidth += toAdd; m_maxPrefWidth += toAdd; - // FIXME: This causes cnn.com loading way slow. Comment it out for now -//#ifdef ANDROID_LAYOUT -#if 0 - Frame* frame = document()->frame(); - if (frame && frame->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) { - int maxWidth = frame->view()->visibleWidth() - 2 * ANDROID_FCTS_MARGIN_PADDING; - if (maxWidth > 0 && maxWidth < m_minPrefWidth) - m_minPrefWidth = maxWidth; - if (maxWidth > 0 && maxWidth < m_maxPrefWidth) - m_maxPrefWidth = maxWidth; - } -#endif setPrefWidthsDirty(false); } @@ -507,13 +501,14 @@ void RenderTextControl::selectionChanged(bool userTriggered) if (Frame* frame = document()->frame()) { if (frame->selection()->isRange() && userTriggered) - node()->dispatchEvent(eventNames().selectEvent, true, false); + node()->dispatchEvent(Event::create(eventNames().selectEvent, true, false)); } } -void RenderTextControl::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) { - graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); + if (width() && height()) + rects.append(IntRect(tx, ty, width(), height())); } HTMLElement* RenderTextControl::innerTextElement() const @@ -521,4 +516,18 @@ HTMLElement* RenderTextControl::innerTextElement() const return m_innerText.get(); } +void RenderTextControl::updatePlaceholderVisibility(bool placeholderShouldBeVisible, bool placeholderValueChanged) +{ + bool oldPlaceholderVisible = m_placeholderVisible; + m_placeholderVisible = placeholderShouldBeVisible; + if (oldPlaceholderVisible != m_placeholderVisible || placeholderValueChanged) { + // Sets the inner text style to the normal style or :placeholder style. + setInnerTextStyle(createInnerTextStyle(textBaseStyle())); + + // updateFromElement() of the subclasses updates the text content + // to the element's value(), placeholder(), or the empty string. + updateFromElement(); + } +} + } // namespace WebCore diff --git a/WebCore/rendering/RenderTextControl.h b/WebCore/rendering/RenderTextControl.h index c0fc343..d1f3749 100644 --- a/WebCore/rendering/RenderTextControl.h +++ b/WebCore/rendering/RenderTextControl.h @@ -34,11 +34,11 @@ class RenderTextControl : public RenderBlock { public: virtual ~RenderTextControl(); - bool isEdited() const { return m_edited; } - void setEdited(bool isEdited) { m_edited = isEdited; } + bool wasChangedSinceLastChangeEvent() const { return m_wasChangedSinceLastChangeEvent; } + void setChangedSinceLastChangeEvent(bool wasChangedSinceLastChangeEvent) { m_wasChangedSinceLastChangeEvent = wasChangedSinceLastChangeEvent; } - bool isUserEdited() const { return m_userEdited; } - void setUserEdited(bool isUserEdited); + bool lastChangeWasUserEdit() const { return m_lastChangeWasUserEdit; } + void setLastChangeWasUserEdit(bool lastChangeWasUserEdit); int selectionStart(); int selectionEnd(); @@ -56,8 +56,10 @@ public: VisiblePosition visiblePositionForIndex(int index); int indexForVisiblePosition(const VisiblePosition&); + void updatePlaceholderVisibility(bool, bool); + protected: - RenderTextControl(Node*); + RenderTextControl(Node*, bool); int scrollbarThickness() const; void adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const; @@ -76,6 +78,7 @@ protected: virtual void adjustControlHeightBasedOnLineHeight(int lineHeight) = 0; virtual void cacheSelection(int start, int end) = 0; virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const = 0; + virtual RenderStyle* textBaseStyle() const = 0; virtual void updateFromElement(); virtual void calcHeight(); @@ -83,6 +86,8 @@ protected: friend class TextIterator; HTMLElement* innerTextElement() const; + bool m_placeholderVisible; + private: virtual const char* renderName() const { return "RenderTextControl"; } virtual bool isTextControl() const { return true; } @@ -92,15 +97,16 @@ private: virtual void removeLeftoverAnonymousBlock(RenderBlock*) { } virtual bool canHaveChildren() const { return false; } virtual bool avoidsFloats() const { return true; } + void setInnerTextStyle(PassRefPtr<RenderStyle>); - virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty); virtual bool canBeProgramaticallyScrolled(bool) const { return true; } String finishText(Vector<UChar>&) const; - bool m_edited; - bool m_userEdited; + bool m_wasChangedSinceLastChangeEvent; + bool m_lastChangeWasUserEdit; RefPtr<TextControlInnerTextElement> m_innerText; }; diff --git a/WebCore/rendering/RenderTextControlMultiLine.cpp b/WebCore/rendering/RenderTextControlMultiLine.cpp index ac790e2..0b4c7a7 100644 --- a/WebCore/rendering/RenderTextControlMultiLine.cpp +++ b/WebCore/rendering/RenderTextControlMultiLine.cpp @@ -22,18 +22,20 @@ #include "config.h" #include "RenderTextControlMultiLine.h" +#include "Event.h" #include "EventNames.h" #include "Frame.h" -#include "HitTestResult.h" +#include "HTMLNames.h" #include "HTMLTextAreaElement.h" +#include "HitTestResult.h" #ifdef ANDROID_LAYOUT #include "Settings.h" #endif namespace WebCore { -RenderTextControlMultiLine::RenderTextControlMultiLine(Node* node) - : RenderTextControl(node) +RenderTextControlMultiLine::RenderTextControlMultiLine(Node* node, bool placeholderVisible) + : RenderTextControl(node, placeholderVisible) { } @@ -46,16 +48,17 @@ RenderTextControlMultiLine::~RenderTextControlMultiLine() void RenderTextControlMultiLine::subtreeHasChanged() { RenderTextControl::subtreeHasChanged(); - static_cast<Element*>(node())->setFormControlValueMatchesRenderer(false); + HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(node()); + textArea->setFormControlValueMatchesRenderer(false); + textArea->setNeedsValidityCheck(); if (!node()->focused()) return; - // Fire the "input" DOM event - node()->dispatchEvent(eventNames().inputEvent, true, false); + node()->dispatchEvent(Event::create(eventNames().inputEvent, true, false)); if (Frame* frame = document()->frame()) - frame->textDidChangeInTextArea(static_cast<Element*>(node())); + frame->textDidChangeInTextArea(textArea); } bool RenderTextControlMultiLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) @@ -63,7 +66,10 @@ bool RenderTextControlMultiLine::nodeAtPoint(const HitTestRequest& request, HitT if (!RenderTextControl::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction)) return false; - if (result.innerNode() == node() || result.innerNode() == innerTextElement()) + bool resultIsTextValueOrPlaceholder + = (!m_placeholderVisible && result.innerNode() == innerTextElement()) + || (m_placeholderVisible && result.innerNode()->isDescendantOf(innerTextElement())); + if (result.innerNode() == node() || resultIsTextValueOrPlaceholder) hitInnerTextElement(result, x, y, tx, ty); return true; @@ -95,7 +101,11 @@ void RenderTextControlMultiLine::updateFromElement() createSubtreeIfNeeded(0); RenderTextControl::updateFromElement(); - setInnerTextValue(static_cast<HTMLTextAreaElement*>(node())->value()); + HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(node()); + if (m_placeholderVisible) + setInnerTextValue(textArea->getAttribute(HTMLNames::placeholderAttr)); + else + setInnerTextValue(textArea->value()); } void RenderTextControlMultiLine::cacheSelection(int start, int end) @@ -105,8 +115,15 @@ void RenderTextControlMultiLine::cacheSelection(int start, int end) PassRefPtr<RenderStyle> RenderTextControlMultiLine::createInnerTextStyle(const RenderStyle* startStyle) const { - RefPtr<RenderStyle> textBlockStyle = RenderStyle::create(); - textBlockStyle->inheritFrom(startStyle); + RefPtr<RenderStyle> textBlockStyle; + if (m_placeholderVisible) { + if (RenderStyle* pseudoStyle = getCachedPseudoStyle(INPUT_PLACEHOLDER)) + textBlockStyle = RenderStyle::clone(pseudoStyle); + } + if (!textBlockStyle) { + textBlockStyle = RenderStyle::create(); + textBlockStyle->inheritFrom(startStyle); + } adjustInnerTextStyle(startStyle, textBlockStyle.get()); textBlockStyle->setDisplay(BLOCK); @@ -114,4 +131,9 @@ PassRefPtr<RenderStyle> RenderTextControlMultiLine::createInnerTextStyle(const R return textBlockStyle.release(); } +RenderStyle* RenderTextControlMultiLine::textBaseStyle() const +{ + return style(); +} + } diff --git a/WebCore/rendering/RenderTextControlMultiLine.h b/WebCore/rendering/RenderTextControlMultiLine.h index 7296653..3371a8f 100644 --- a/WebCore/rendering/RenderTextControlMultiLine.h +++ b/WebCore/rendering/RenderTextControlMultiLine.h @@ -28,7 +28,7 @@ namespace WebCore { class RenderTextControlMultiLine : public RenderTextControl { public: - RenderTextControlMultiLine(Node*); + RenderTextControlMultiLine(Node*, bool); virtual ~RenderTextControlMultiLine(); void forwardEvent(Event*); @@ -47,6 +47,7 @@ private: virtual void updateFromElement(); virtual void cacheSelection(int start, int end); + virtual RenderStyle* textBaseStyle() const; virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const; }; diff --git a/WebCore/rendering/RenderTextControlSingleLine.cpp b/WebCore/rendering/RenderTextControlSingleLine.cpp index b448eb7..b68f004 100644 --- a/WebCore/rendering/RenderTextControlSingleLine.cpp +++ b/WebCore/rendering/RenderTextControlSingleLine.cpp @@ -48,9 +48,8 @@ namespace WebCore { using namespace HTMLNames; -RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node) - : RenderTextControl(node) - , m_placeholderVisible(false) +RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node, bool placeholderVisible) + : RenderTextControl(node, placeholderVisible) , m_searchPopupIsVisible(false) , m_shouldDrawCapsLockIndicator(false) , m_searchEventTimer(this, &RenderTextControlSingleLine::searchEventTimerFired) @@ -69,25 +68,9 @@ RenderTextControlSingleLine::~RenderTextControlSingleLine() m_innerBlock->detach(); } -bool RenderTextControlSingleLine::placeholderShouldBeVisible() const +RenderStyle* RenderTextControlSingleLine::textBaseStyle() const { - return inputElement()->placeholderShouldBeVisible(); -} - -void RenderTextControlSingleLine::updatePlaceholderVisibility() -{ - RenderStyle* parentStyle = m_innerBlock ? m_innerBlock->renderer()->style() : style(); - - RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(parentStyle); - HTMLElement* innerText = innerTextElement(); - innerText->renderer()->setStyle(textBlockStyle); - - for (Node* n = innerText->firstChild(); n; n = n->traverseNextNode(innerText)) { - if (RenderObject* renderer = n->renderer()) - renderer->setStyle(textBlockStyle); - } - - updateFromElement(); + return m_innerBlock ? m_innerBlock->renderer()->style() : style(); } void RenderTextControlSingleLine::addSearchResult() @@ -163,17 +146,19 @@ void RenderTextControlSingleLine::hidePopup() ASSERT(node()->isHTMLElement()); if (m_searchPopup) m_searchPopup->hide(); - - m_searchPopupIsVisible = false; } void RenderTextControlSingleLine::subtreeHasChanged() { - bool wasEdited = isEdited(); + bool wasChanged = wasChangedSinceLastChangeEvent(); RenderTextControl::subtreeHasChanged(); InputElement* input = inputElement(); - input->setValueFromRenderer(input->constrainValue(text())); + // We don't need to call sanitizeUserInputValue() function here because + // InputElement::handleBeforeTextInsertedEvent() has already called + // sanitizeUserInputValue(). + // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent. + input->setValueFromRenderer(input->sanitizeValue(text())); if (m_cancelButton) updateCancelButtonVisibility(); @@ -182,7 +167,7 @@ void RenderTextControlSingleLine::subtreeHasChanged() if (input->searchEventsShouldBeDispatched()) startSearchEventTimer(); - if (!wasEdited && node()->focused()) { + if (!wasChanged && node()->focused()) { if (Frame* frame = document()->frame()) frame->textFieldDidBeginEditing(static_cast<Element*>(node())); } @@ -480,18 +465,19 @@ void RenderTextControlSingleLine::updateFromElement() createSubtreeIfNeeded(); RenderTextControl::updateFromElement(); - bool placeholderVisibilityShouldChange = m_placeholderVisible != placeholderShouldBeVisible(); - m_placeholderVisible = placeholderShouldBeVisible(); - if (m_cancelButton) updateCancelButtonVisibility(); if (m_placeholderVisible) { ExceptionCode ec = 0; - innerTextElement()->setInnerText(inputElement()->placeholder(), ec); + innerTextElement()->setInnerText(static_cast<Element*>(node())->getAttribute(placeholderAttr), ec); ASSERT(!ec); - } else if (!static_cast<Element*>(node())->formControlValueMatchesRenderer() || placeholderVisibilityShouldChange) - setInnerTextValue(inputElement()->value()); + } else { + if (!inputElement()->suggestedValue().isNull()) + setInnerTextValue(inputElement()->suggestedValue()); + else + setInnerTextValue(inputElement()->value()); + } if (m_searchPopupIsVisible) m_searchPopup->updateFromElement(); @@ -505,7 +491,7 @@ void RenderTextControlSingleLine::cacheSelection(int start, int end) PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const { RefPtr<RenderStyle> textBlockStyle; - if (placeholderShouldBeVisible()) { + if (m_placeholderVisible) { if (RenderStyle* pseudoStyle = getCachedPseudoStyle(INPUT_PLACEHOLDER)) textBlockStyle = RenderStyle::clone(pseudoStyle); } @@ -535,7 +521,7 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const // After this, updateFromElement will immediately update the text displayed. // When the placeholder is no longer visible, updatePlaceholderVisiblity will reset the style, // and the text security mode will be set back to the computed value correctly. - if (placeholderShouldBeVisible()) + if (m_placeholderVisible) textBlockStyle->setTextSecurity(TSNONE); return textBlockStyle.release(); @@ -753,6 +739,11 @@ int RenderTextControlSingleLine::selectedIndex() const return -1; } +void RenderTextControlSingleLine::popupDidHide() +{ + m_searchPopupIsVisible = false; +} + bool RenderTextControlSingleLine::itemIsSeparator(unsigned listIndex) const { // The separator will be the second to last item in our list. @@ -832,12 +823,12 @@ void RenderTextControlSingleLine::setScrollTop(int newTop) innerTextElement()->setScrollTop(newTop); } -bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) +bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) { RenderLayer* layer = innerTextElement()->renderBox()->layer(); if (layer && layer->scroll(direction, granularity, multiplier)) return true; - return RenderBlock::scroll(direction, granularity, multiplier); + return RenderBlock::scroll(direction, granularity, multiplier, stopNode); } PassRefPtr<Scrollbar> RenderTextControlSingleLine::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) diff --git a/WebCore/rendering/RenderTextControlSingleLine.h b/WebCore/rendering/RenderTextControlSingleLine.h index da9838f..e30ff0d 100644 --- a/WebCore/rendering/RenderTextControlSingleLine.h +++ b/WebCore/rendering/RenderTextControlSingleLine.h @@ -36,19 +36,18 @@ class TextControlInnerElement; class RenderTextControlSingleLine : public RenderTextControl, private PopupMenuClient { public: - RenderTextControlSingleLine(Node*); + RenderTextControlSingleLine(Node*, bool); virtual ~RenderTextControlSingleLine(); bool placeholderIsVisible() const { return m_placeholderVisible; } bool placeholderShouldBeVisible() const; - void updatePlaceholderVisibility(); void addSearchResult(); void stopSearchEventTimer(); bool popupIsVisible() const { return m_searchPopupIsVisible; } void showPopup(); - virtual void hidePopup(); // PopupMenuClient method + void hidePopup(); void forwardEvent(Event*); @@ -73,7 +72,7 @@ private: virtual int scrollHeight() const; virtual void setScrollLeft(int); virtual void setScrollTop(int); - virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); + virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f, Node** stopNode = 0); int textBlockWidth() const; virtual int preferredContentWidth(float charWidth) const; @@ -84,6 +83,7 @@ private: virtual void cacheSelection(int start, int end); virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + virtual RenderStyle* textBaseStyle() const; virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const; PassRefPtr<RenderStyle> createInnerBlockStyle(const RenderStyle* startStyle) const; PassRefPtr<RenderStyle> createResultsButtonStyle(const RenderStyle* startStyle) const; @@ -109,6 +109,7 @@ private: virtual int clientPaddingRight() const; virtual int listSize() const; virtual int selectedIndex() const; + virtual void popupDidHide(); virtual bool itemIsSeparator(unsigned listIndex) const; virtual bool itemIsLabel(unsigned listIndex) const; virtual bool itemIsSelected(unsigned listIndex) const; @@ -121,7 +122,6 @@ private: InputElement* inputElement() const; - bool m_placeholderVisible; bool m_searchPopupIsVisible; bool m_shouldDrawCapsLockIndicator; diff --git a/WebCore/rendering/RenderTextFragment.cpp b/WebCore/rendering/RenderTextFragment.cpp index 7da9e5a..9ff1106 100644 --- a/WebCore/rendering/RenderTextFragment.cpp +++ b/WebCore/rendering/RenderTextFragment.cpp @@ -47,7 +47,7 @@ RenderTextFragment::RenderTextFragment(Node* node, StringImpl* str) PassRefPtr<StringImpl> RenderTextFragment::originalText() const { Node* e = node(); - RefPtr<StringImpl> result = (e ? static_cast<Text*>(e)->string() : contentString()); + RefPtr<StringImpl> result = (e ? static_cast<Text*>(e)->dataImpl() : contentString()); if (result && (start() > 0 || start() < result->length())) result = result->substring(start(), end()); return result.release(); @@ -76,7 +76,7 @@ UChar RenderTextFragment::previousCharacter() { if (start()) { Node* e = node(); - StringImpl* original = (e ? static_cast<Text*>(e)->string() : contentString()); + StringImpl* original = (e ? static_cast<Text*>(e)->dataImpl() : contentString()); if (original) return (*original)[start() - 1]; } diff --git a/WebCore/rendering/RenderTheme.cpp b/WebCore/rendering/RenderTheme.cpp index 63e7d45..f1e564b 100644 --- a/WebCore/rendering/RenderTheme.cpp +++ b/WebCore/rendering/RenderTheme.cpp @@ -1,7 +1,7 @@ /** * This file is part of the theme implementation for form controls in WebCore. * - * Copyright (C) 2005, 2006, 2007, 2008 Apple Computer, Inc. + * Copyright (C) 2005, 2006, 2007, 2008, 2009 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 @@ -30,6 +30,7 @@ #include "GraphicsContext.h" #include "HTMLInputElement.h" #include "HTMLNames.h" +#include "MediaControlElements.h" #include "Page.h" #include "RenderStyle.h" #include "RenderView.h" @@ -84,7 +85,10 @@ void RenderTheme::adjustStyle(CSSStyleSelector* selector, RenderStyle* style, El #if USE(NEW_THEME) switch (part) { + case ListButtonPart: case CheckboxPart: + case InnerSpinButtonPart: + case OuterSpinButtonPart: case RadioPart: case PushButtonPart: case SquareButtonPart: @@ -170,9 +174,14 @@ void RenderTheme::adjustStyle(CSSStyleSelector* selector, RenderStyle* style, El return adjustRadioStyle(selector, style, e); case PushButtonPart: case SquareButtonPart: + case ListButtonPart: case DefaultButtonPart: case ButtonPart: return adjustButtonStyle(selector, style, e); + case InnerSpinButtonPart: + return adjustInnerSpinButtonStyle(selector, style, e); + case OuterSpinButtonPart: + return adjustOuterSpinButtonStyle(selector, style, e); #endif case TextFieldPart: return adjustTextFieldStyle(selector, style, e); @@ -187,6 +196,7 @@ void RenderTheme::adjustStyle(CSSStyleSelector* selector, RenderStyle* style, El case MenulistButtonPart: return adjustMenuListButtonStyle(selector, style, e); case MediaSliderPart: + case MediaVolumeSliderPart: case SliderHorizontalPart: case SliderVerticalPart: return adjustSliderTrackStyle(selector, style, e); @@ -229,8 +239,11 @@ bool RenderTheme::paint(RenderObject* o, const RenderObject::PaintInfo& paintInf case RadioPart: case PushButtonPart: case SquareButtonPart: + case ListButtonPart: case DefaultButtonPart: case ButtonPart: + case InnerSpinButtonPart: + case OuterSpinButtonPart: m_theme->paint(part, controlStatesForRenderer(o), const_cast<GraphicsContext*>(paintInfo.context), r, o->style()->effectiveZoom(), o->view()->frameView()); return false; default: @@ -247,9 +260,14 @@ bool RenderTheme::paint(RenderObject* o, const RenderObject::PaintInfo& paintInf return paintRadio(o, paintInfo, r); case PushButtonPart: case SquareButtonPart: + case ListButtonPart: case DefaultButtonPart: case ButtonPart: return paintButton(o, paintInfo, r); + case InnerSpinButtonPart: + return paintInnerSpinButton(o, paintInfo, r); + case OuterSpinButtonPart: + return paintOuterSpinButton(o, paintInfo, r); #endif case MenulistPart: return paintMenuList(o, paintInfo, r); @@ -276,12 +294,22 @@ bool RenderTheme::paint(RenderObject* o, const RenderObject::PaintInfo& paintInf return paintMediaRewindButton(o, paintInfo, r); case MediaReturnToRealtimeButtonPart: return paintMediaReturnToRealtimeButton(o, paintInfo, r); + case MediaToggleClosedCaptionsButtonPart: + return paintMediaToggleClosedCaptionsButton(o, paintInfo, r); case MediaSliderPart: return paintMediaSliderTrack(o, paintInfo, r); case MediaSliderThumbPart: if (o->parent()->isSlider()) return paintMediaSliderThumb(o, paintInfo, r); break; + case MediaVolumeSliderContainerPart: + return paintMediaVolumeSliderContainer(o, paintInfo, r); + case MediaVolumeSliderPart: + return paintMediaVolumeSliderTrack(o, paintInfo, r); + case MediaVolumeSliderThumbPart: + if (o->parent()->isSlider()) + return paintMediaVolumeSliderThumb(o, paintInfo, r); + break; case MediaTimeRemainingPart: return paintMediaTimeRemaining(o, paintInfo, r); case MediaCurrentTimePart: @@ -329,6 +357,7 @@ bool RenderTheme::paintBorderOnly(RenderObject* o, const RenderObject::PaintInfo case RadioPart: case PushButtonPart: case SquareButtonPart: + case ListButtonPart: case DefaultButtonPart: case ButtonPart: case MenulistPart: @@ -363,6 +392,7 @@ bool RenderTheme::paintDecorations(RenderObject* o, const RenderObject::PaintInf case RadioPart: case PushButtonPart: case SquareButtonPart: + case ListButtonPart: case DefaultButtonPart: case ButtonPart: case MenulistPart: @@ -391,6 +421,54 @@ bool RenderTheme::hitTestMediaControlPart(RenderObject* o, const IntPoint& absPo FloatPoint localPoint = o->absoluteToLocal(absPoint, false, true); // respect transforms return toRenderBox(o)->borderBoxRect().contains(roundedIntPoint(localPoint)); } + +bool RenderTheme::shouldRenderMediaControlPart(ControlPart part, Element* e) +{ + HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(e); + switch (part) { + case MediaMuteButtonPart: + return mediaElement->hasAudio(); + case MediaRewindButtonPart: + return mediaElement->movieLoadType() != MediaPlayer::LiveStream; + case MediaReturnToRealtimeButtonPart: + return mediaElement->movieLoadType() == MediaPlayer::LiveStream; + case MediaFullscreenButtonPart: + return mediaElement->supportsFullscreen(); + case MediaToggleClosedCaptionsButtonPart: + return mediaElement->hasClosedCaptions(); + default: + return true; + } +} + +String RenderTheme::formatMediaControlsTime(float time) const +{ + if (!isfinite(time)) + time = 0; + int seconds = (int)fabsf(time); + int hours = seconds / (60 * 60); + int minutes = (seconds / 60) % 60; + seconds %= 60; + if (hours) { + if (hours > 9) + return String::format("%s%02d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); + + return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); + } + + return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds); +} + +String RenderTheme::formatMediaControlsCurrentTime(float currentTime, float /*duration*/) const +{ + return formatMediaControlsTime(currentTime); +} + +String RenderTheme::formatMediaControlsRemainingTime(float currentTime, float duration) const +{ + return formatMediaControlsTime(currentTime - duration); +} + #endif Color RenderTheme::activeSelectionBackgroundColor() const @@ -552,7 +630,7 @@ bool RenderTheme::supportsFocusRing(const RenderStyle* style) const bool RenderTheme::stateChanged(RenderObject* o, ControlState state) const { - // Default implementation assumes the controls dont respond to changes in :hover state + // Default implementation assumes the controls don't respond to changes in :hover state if (state == HoverState && !supportsHover(o->style())) return false; @@ -672,6 +750,10 @@ bool RenderTheme::isHovered(const RenderObject* o) const bool RenderTheme::isDefault(const RenderObject* o) const { + // A button should only have the default appearance if the page is active + if (!isActive(o)) + return false; + if (!o->document()) return false; @@ -725,6 +807,14 @@ void RenderTheme::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Eleme setButtonSize(style); } +void RenderTheme::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustOuterSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + #endif void RenderTheme::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const diff --git a/WebCore/rendering/RenderTheme.h b/WebCore/rendering/RenderTheme.h index 4f37015..32ae5e5 100644 --- a/WebCore/rendering/RenderTheme.h +++ b/WebCore/rendering/RenderTheme.h @@ -172,6 +172,12 @@ public: #if ENABLE(VIDEO) // Media controls virtual bool hitTestMediaControlPart(RenderObject*, const IntPoint& absPoint); + virtual bool shouldRenderMediaControlPart(ControlPart, Element*); + virtual double mediaControlsFadeInDuration() { return 0.1; } + virtual double mediaControlsFadeOutDuration() { return 0.3; } + virtual String formatMediaControlsTime(float time) const; + virtual String formatMediaControlsCurrentTime(float currentTime, float duration) const; + virtual String formatMediaControlsRemainingTime(float currentTime, float duration) const; #endif protected: @@ -202,6 +208,11 @@ protected: virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; virtual bool paintButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } virtual void setButtonSize(RenderStyle*) const { } + + virtual void adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintInnerSpinButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual void adjustOuterSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintOuterSpinButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } #endif virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; @@ -247,8 +258,12 @@ protected: virtual bool paintMediaSeekForwardButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } virtual bool paintMediaSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } virtual bool paintMediaSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaVolumeSliderContainer(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaVolumeSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaVolumeSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } virtual bool paintMediaRewindButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } virtual bool paintMediaReturnToRealtimeButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaToggleClosedCaptionsButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } virtual bool paintMediaControlsBackground(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } virtual bool paintMediaCurrentTime(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } virtual bool paintMediaTimeRemaining(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } diff --git a/WebCore/rendering/RenderThemeChromiumLinux.cpp b/WebCore/rendering/RenderThemeChromiumLinux.cpp index c4020d3..13c9cd6 100644 --- a/WebCore/rendering/RenderThemeChromiumLinux.cpp +++ b/WebCore/rendering/RenderThemeChromiumLinux.cpp @@ -25,13 +25,27 @@ #include "config.h" #include "RenderThemeChromiumLinux.h" -#include "Color.h" #include "CSSValueKeywords.h" +#include "Color.h" #include "RenderObject.h" #include "UserAgentStyleSheets.h" namespace WebCore { +unsigned RenderThemeChromiumLinux::m_thumbInactiveColor = 0xf0ebe5; +unsigned RenderThemeChromiumLinux::m_thumbActiveColor = 0xfaf8f5; +unsigned RenderThemeChromiumLinux::m_trackColor = 0xe3ddd8; +unsigned RenderThemeChromiumLinux::m_activeSelectionBackgroundColor = + 0xff1e90ff; +unsigned RenderThemeChromiumLinux::m_activeSelectionForegroundColor = + Color::black; +unsigned RenderThemeChromiumLinux::m_inactiveSelectionBackgroundColor = + 0xffc8c8c8; +unsigned RenderThemeChromiumLinux::m_inactiveSelectionForegroundColor = + 0xff323232; + +double RenderThemeChromiumLinux::m_caretBlinkInterval; + PassRefPtr<RenderTheme> RenderThemeChromiumLinux::create() { return adoptRef(new RenderThemeChromiumLinux()); @@ -45,6 +59,7 @@ PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) RenderThemeChromiumLinux::RenderThemeChromiumLinux() { + m_caretBlinkInterval = RenderTheme::caretBlinkInterval(); } RenderThemeChromiumLinux::~RenderThemeChromiumLinux() @@ -91,9 +106,74 @@ Color RenderThemeChromiumLinux::inactiveListBoxSelectionForegroundColor() const return Color(0x32, 0x32, 0x32); } +Color RenderThemeChromiumLinux::platformActiveSelectionBackgroundColor() const +{ + return m_activeSelectionBackgroundColor; +} + +Color RenderThemeChromiumLinux::platformInactiveSelectionBackgroundColor() const +{ + return m_inactiveSelectionBackgroundColor; +} + +Color RenderThemeChromiumLinux::platformActiveSelectionForegroundColor() const +{ + return m_activeSelectionForegroundColor; +} + +Color RenderThemeChromiumLinux::platformInactiveSelectionForegroundColor() const +{ + return m_inactiveSelectionForegroundColor; +} + +void RenderThemeChromiumLinux::adjustSliderThumbSize(RenderObject* o) const +{ + // These sizes match the sizes in Chromium Win. + const int sliderThumbAlongAxis = 11; + const int sliderThumbAcrossAxis = 21; + if (o->style()->appearance() == SliderThumbHorizontalPart) { + 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)); + } else + RenderThemeChromiumSkia::adjustSliderThumbSize(o); +} + bool RenderThemeChromiumLinux::supportsControlTints() const { return true; } +void RenderThemeChromiumLinux::setCaretBlinkInterval(double interval) +{ + m_caretBlinkInterval = interval; +} + +double RenderThemeChromiumLinux::caretBlinkIntervalInternal() const +{ + return m_caretBlinkInterval; +} + +void RenderThemeChromiumLinux::setSelectionColors( + unsigned activeBackgroundColor, + unsigned activeForegroundColor, + unsigned inactiveBackgroundColor, + unsigned inactiveForegroundColor) +{ + m_activeSelectionBackgroundColor = activeBackgroundColor; + m_activeSelectionForegroundColor = activeForegroundColor; + m_inactiveSelectionBackgroundColor = inactiveBackgroundColor; + m_inactiveSelectionForegroundColor = inactiveForegroundColor; +} + +void RenderThemeChromiumLinux::setScrollbarColors( + SkColor inactiveColor, SkColor activeColor, SkColor trackColor) +{ + m_thumbInactiveColor = inactiveColor; + m_thumbActiveColor = activeColor; + m_trackColor = trackColor; +} + } // namespace WebCore diff --git a/WebCore/rendering/RenderThemeChromiumLinux.h b/WebCore/rendering/RenderThemeChromiumLinux.h index e75ddd5..c60dec3 100644 --- a/WebCore/rendering/RenderThemeChromiumLinux.h +++ b/WebCore/rendering/RenderThemeChromiumLinux.h @@ -49,12 +49,45 @@ namespace WebCore { virtual Color inactiveListBoxSelectionBackgroundColor() const; virtual Color inactiveListBoxSelectionForegroundColor() const; + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color platformActiveSelectionForegroundColor() const; + virtual Color platformInactiveSelectionForegroundColor() const; + + virtual void adjustSliderThumbSize(RenderObject*) const; + + static void setCaretBlinkInterval(double interval); + virtual double caretBlinkIntervalInternal() const; + + static void setSelectionColors(unsigned activeBackgroundColor, + unsigned activeForegroundColor, + unsigned inactiveBackgroundColor, + unsigned inactiveForegroundColor); + + static void setScrollbarColors(unsigned inactive_color, + unsigned active_color, + unsigned track_color); + static unsigned thumbInactiveColor() { return m_thumbInactiveColor; } + static unsigned thumbActiveColor() { return m_thumbActiveColor; } + static unsigned trackColor() { return m_trackColor; } + private: RenderThemeChromiumLinux(); virtual ~RenderThemeChromiumLinux(); // A general method asking if any control tinting is supported at all. virtual bool supportsControlTints() const; + + static double m_caretBlinkInterval; + + static unsigned m_activeSelectionBackgroundColor; + static unsigned m_activeSelectionForegroundColor; + static unsigned m_inactiveSelectionBackgroundColor; + static unsigned m_inactiveSelectionForegroundColor; + + static unsigned m_thumbInactiveColor; + static unsigned m_thumbActiveColor; + static unsigned m_trackColor; }; } // namespace WebCore diff --git a/WebCore/rendering/RenderThemeChromiumMac.h b/WebCore/rendering/RenderThemeChromiumMac.h index ffb2548..8101038 100644 --- a/WebCore/rendering/RenderThemeChromiumMac.h +++ b/WebCore/rendering/RenderThemeChromiumMac.h @@ -3,7 +3,6 @@ * * Copyright (C) 2005 Apple Computer, Inc. * Copyright (C) 2008, 2009 Google, Inc. - * Copyright (C) 2009 Kenneth Rohde Christiansen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,7 +25,6 @@ #define RenderThemeChromiumMac_h #import "RenderTheme.h" -#import <AppKit/AppKit.h> #import <wtf/HashMap.h> #import <wtf/RetainPtr.h> @@ -36,185 +34,165 @@ class WebCoreRenderThemeNotificationObserver; #endif +// This file (and its associated .mm file) is a clone of RenderThemeMac.h. See +// the .mm file for details. + namespace WebCore { - class RenderStyle; +class RenderStyle; + +class RenderThemeChromiumMac : public RenderTheme { +public: + static PassRefPtr<RenderTheme> create(); + + // A method asking if the control changes its tint when the window has focus or not. + virtual bool controlSupportsTints(const RenderObject*) const; + + // A general method asking if any control tinting is supported at all. + virtual bool supportsControlTints() const { return true; } + + virtual void adjustRepaintRect(const RenderObject*, IntRect&); + + virtual bool isControlStyled(const RenderStyle*, const BorderData&, + const FillLayer&, const Color& backgroundColor) const; + + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color platformActiveListBoxSelectionBackgroundColor() const; + virtual Color platformActiveListBoxSelectionForegroundColor() const; + virtual Color platformInactiveListBoxSelectionBackgroundColor() const; + virtual Color platformInactiveListBoxSelectionForegroundColor() const; + virtual Color platformFocusRingColor() const; - class RenderThemeChromiumMac : public RenderTheme { - public: - static PassRefPtr<RenderTheme> create(); + virtual ScrollbarControlSize scrollbarControlSizeForPart(ControlPart) { return SmallScrollbar; } + + virtual void platformColorsDidChange(); + + // System fonts. + virtual void systemFont(int cssValueId, FontDescription&) const; + + virtual int minimumMenuListSize(RenderStyle*) const; + + virtual void adjustSliderThumbSize(RenderObject*) const; + + virtual int popupInternalPaddingLeft(RenderStyle*) const; + virtual int popupInternalPaddingRight(RenderStyle*) const; + virtual int popupInternalPaddingTop(RenderStyle*) const; + virtual int popupInternalPaddingBottom(RenderStyle*) const; + + virtual bool paintCapsLockIndicator(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline - // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of - // controls that need to do this. - virtual int baselinePosition(const RenderObject*) const; + virtual Color systemColor(int cssValueId) const; - // A method asking if the control changes its tint when the window has focus or not. - virtual bool controlSupportsTints(const RenderObject*) const; +protected: + virtual bool supportsSelectionForegroundColors() const { return false; } - // A general method asking if any control tinting is supported at all. - virtual bool supportsControlTints() const { return true; } + virtual bool paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual void adjustRepaintRect(const RenderObject*, IntRect&); + virtual bool paintTextArea(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual bool isControlStyled(const RenderStyle*, const BorderData&, - const FillLayer&, const Color& backgroundColor) const; + virtual bool paintMenuList(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual Color platformActiveSelectionBackgroundColor() const; - virtual Color platformInactiveSelectionBackgroundColor() const; - virtual Color platformActiveListBoxSelectionBackgroundColor() const; + virtual bool paintMenuListButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual Color platformFocusRingColor() const; - - virtual void platformColorsDidChange(); + virtual bool paintSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - // System fonts. - virtual void systemFont(int cssValueId, FontDescription&) const; + virtual bool paintSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual int minimumMenuListSize(RenderStyle*) const; + virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual void adjustSliderThumbSize(RenderObject*) const; - - virtual int popupInternalPaddingLeft(RenderStyle*) const; - virtual int popupInternalPaddingRight(RenderStyle*) const; - virtual int popupInternalPaddingTop(RenderStyle*) const; - virtual int popupInternalPaddingBottom(RenderStyle*) const; - - virtual ScrollbarControlSize scrollbarControlSizeForPart(ControlPart) { return SmallScrollbar; } + virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldCancelButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + +#if ENABLE(VIDEO) + virtual bool shouldRenderMediaControlPart(ControlPart, Element*); + virtual bool paintMediaPlayButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaMuteButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaVolumeSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaVolumeSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaControlsBackground(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + // Media controls + virtual String extraMediaControlsStyleSheet(); +#endif + +private: + RenderThemeChromiumMac(); + virtual ~RenderThemeChromiumMac(); + + IntRect inflateRect(const IntRect&, const IntSize&, const int* margins, float zoomLevel = 1.0f) const; + + FloatRect convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect& r) const; - virtual bool paintCapsLockIndicator(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + // Get the control size based off the font. Used by some of the controls (like buttons). + NSControlSize controlSizeForFont(RenderStyle*) const; + NSControlSize controlSizeForSystemFont(RenderStyle*) const; + void setControlSize(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel = 1.0f); + void setSizeFromFont(RenderStyle*, const IntSize* sizes) const; + IntSize sizeForFont(RenderStyle*, const IntSize* sizes) const; + IntSize sizeForSystemFont(RenderStyle*, const IntSize* sizes) const; + void setFontFromControlSize(CSSStyleSelector*, RenderStyle*, NSControlSize) const; + + void updateActiveState(NSCell*, const RenderObject*); + void updateCheckedState(NSCell*, const RenderObject*); + void updateEnabledState(NSCell*, const RenderObject*); + void updateFocusedState(NSCell*, const RenderObject*); + void updatePressedState(NSCell*, const RenderObject*); + + // Helpers for adjusting appearance and for painting + + void setPopupButtonCellState(const RenderObject*, const IntRect&); + const IntSize* popupButtonSizes() const; + const int* popupButtonMargins() const; + const int* popupButtonPadding(NSControlSize) const; + void paintMenuListButtonGradients(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + const IntSize* menuListSizes() const; + + const IntSize* searchFieldSizes() const; + const IntSize* cancelButtonSizes() const; + const IntSize* resultsButtonSizes() const; + void setSearchCellState(RenderObject*, const IntRect&); + void setSearchFieldSize(RenderStyle*) const; + + NSPopUpButtonCell* popupButton() const; + NSSearchFieldCell* search() const; + NSMenu* searchMenuTemplate() const; + NSSliderCell* sliderThumbHorizontal() const; + NSSliderCell* sliderThumbVertical() const; + +private: + mutable RetainPtr<NSPopUpButtonCell> m_popupButton; + mutable RetainPtr<NSSearchFieldCell> m_search; + mutable RetainPtr<NSMenu> m_searchMenuTemplate; + mutable RetainPtr<NSSliderCell> m_sliderThumbHorizontal; + mutable RetainPtr<NSSliderCell> m_sliderThumbVertical; - virtual Color systemColor(int cssValueId) const; + bool m_isSliderThumbHorizontalPressed; + bool m_isSliderThumbVerticalPressed; - protected: - virtual bool supportsSelectionForegroundColors() const { return false; } + mutable HashMap<int, RGBA32> m_systemColorCache; - // Methods for each appearance value. - virtual bool paintCheckbox(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void setCheckboxSize(RenderStyle*) const; - - virtual bool paintRadio(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void setRadioSize(RenderStyle*) const; - - virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, WebCore::Element*) const; - virtual bool paintButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void setButtonSize(RenderStyle*) const; - - virtual bool paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - - virtual bool paintTextArea(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - - virtual bool paintMenuList(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - - virtual bool paintMenuListButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - - virtual bool paintSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - - virtual bool paintSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - - virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - - virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldCancelButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldResultsDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - - virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; - virtual bool paintSearchFieldResultsButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - - virtual bool paintMediaFullscreenButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual bool paintMediaPlayButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual bool paintMediaMuteButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual bool paintMediaSeekBackButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual bool paintMediaSeekForwardButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual bool paintMediaSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual bool paintMediaSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - - private: - RenderThemeChromiumMac(); - virtual ~RenderThemeChromiumMac(); - - IntRect inflateRect(const IntRect&, const IntSize&, const int* margins, float zoomLevel = 1.0f) const; - - // Get the control size based off the font. Used by some of the controls (like buttons). - NSControlSize controlSizeForFont(RenderStyle*) const; - NSControlSize controlSizeForSystemFont(RenderStyle*) const; - void setControlSize(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel = 1.0f); - void setSizeFromFont(RenderStyle*, const IntSize* sizes) const; - IntSize sizeForFont(RenderStyle*, const IntSize* sizes) const; - IntSize sizeForSystemFont(RenderStyle*, const IntSize* sizes) const; - void setFontFromControlSize(CSSStyleSelector*, RenderStyle*, NSControlSize) const; - - void updateCheckedState(NSCell*, const RenderObject*); - void updateEnabledState(NSCell*, const RenderObject*); - void updateFocusedState(NSCell*, const RenderObject*); - void updatePressedState(NSCell*, const RenderObject*); - - // Helpers for adjusting appearance and for painting - const IntSize* checkboxSizes() const; - const int* checkboxMargins() const; - void setCheckboxCellState(const RenderObject*, const IntRect&); - - const IntSize* radioSizes() const; - const int* radioMargins() const; - void setRadioCellState(const RenderObject*, const IntRect&); - - void setButtonPaddingFromControlSize(RenderStyle*, NSControlSize) const; - const IntSize* buttonSizes() const; - const int* buttonMargins() const; - void setButtonCellState(const RenderObject*, const IntRect&); - - void setPopupButtonCellState(const RenderObject*, const IntRect&); - const IntSize* popupButtonSizes() const; - const int* popupButtonMargins() const; - const int* popupButtonPadding(NSControlSize) const; - void paintMenuListButtonGradients(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - const IntSize* menuListSizes() const; - - const IntSize* searchFieldSizes() const; - const IntSize* cancelButtonSizes() const; - const IntSize* resultsButtonSizes() const; - void setSearchCellState(RenderObject*, const IntRect&); - void setSearchFieldSize(RenderStyle*) const; - - NSButtonCell* checkbox() const; - NSButtonCell* radio() const; - NSButtonCell* button() const; - NSPopUpButtonCell* popupButton() const; - NSSearchFieldCell* search() const; - NSMenu* searchMenuTemplate() const; - NSSliderCell* sliderThumbHorizontal() const; - NSSliderCell* sliderThumbVertical() const; - - private: - mutable RetainPtr<NSButtonCell> m_checkbox; - mutable RetainPtr<NSButtonCell> m_radio; - mutable RetainPtr<NSButtonCell> m_button; - mutable RetainPtr<NSPopUpButtonCell> m_popupButton; - mutable RetainPtr<NSSearchFieldCell> m_search; - mutable RetainPtr<NSMenu> m_searchMenuTemplate; - mutable RetainPtr<NSSliderCell> m_sliderThumbHorizontal; - mutable RetainPtr<NSSliderCell> m_sliderThumbVertical; - - bool m_isSliderThumbHorizontalPressed; - bool m_isSliderThumbVerticalPressed; - - mutable HashMap<int, RGBA32> m_systemColorCache; - - RetainPtr<WebCoreRenderThemeNotificationObserver> m_notificationObserver; - }; + RetainPtr<WebCoreRenderThemeNotificationObserver> m_notificationObserver; +}; } // namespace WebCore -#endif +#endif // RenderThemeChromiumMac_h diff --git a/WebCore/rendering/RenderThemeChromiumMac.mm b/WebCore/rendering/RenderThemeChromiumMac.mm index 23201c9..e274b05 100644 --- a/WebCore/rendering/RenderThemeChromiumMac.mm +++ b/WebCore/rendering/RenderThemeChromiumMac.mm @@ -1,7 +1,6 @@ /* - * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2008, 2009 Google, Inc. - * Copyright (C) 2009 Kenneth Rohde Christiansen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -19,25 +18,16 @@ * Boston, MA 02110-1301, USA. */ -// FIXME: we still need to figure out if passing a null view to the cell -// drawing routines will work. I expect not, and if that's the case we'll have -// to figure out something else. For now, at least leave the lines commented -// in, but the procurement of the view if 0'd. - #import "config.h" #import "RenderThemeChromiumMac.h" -#import <Carbon/Carbon.h> -#import <Cocoa/Cocoa.h> -#import <math.h> - #import "BitmapImage.h" #import "ChromiumBridge.h" #import "ColorMac.h" #import "CSSStyleSelector.h" #import "CSSValueKeywords.h" +#import "Document.h" #import "Element.h" -#import "FoundationExtras.h" #import "FrameView.h" #import "GraphicsContext.h" #import "HTMLInputElement.h" @@ -47,11 +37,18 @@ #import "LocalCurrentGraphicsContext.h" #import "MediaControlElements.h" #import "RenderMedia.h" +#import "RenderMediaControlsChromium.h" #import "RenderSlider.h" #import "RenderView.h" #import "SharedBuffer.h" +#import "TimeRanges.h" #import "WebCoreSystemInterface.h" +#import "UserAgentStyleSheets.h" +#import <Carbon/Carbon.h> +#import <Cocoa/Cocoa.h> #import <wtf/RetainPtr.h> +#import <wtf/StdLibExtras.h> +#import <math.h> #ifdef BUILDING_ON_TIGER typedef int NSInteger; @@ -60,9 +57,28 @@ typedef unsigned NSUInteger; using std::min; +// This file (and its associated .h file) is a clone of RenderThemeMac.mm. +// Because the original file is designed to run in-process inside a Cocoa view, +// we must maintain a fork. Please maintain this file by performing parallel +// changes to it. +// +// The only changes from RenderThemeMac should be: +// - The classname change from RenderThemeMac to RenderThemeChromiumMac. +// - The introduction of RTCMFlippedView and FlippedView() and its use as the +// parent view for cell rendering. +// - In platformFocusRingColor(), the use of ChromiumBridge to determine if +// we're in layout test mode. +// - updateActiveState() and its use to update the cells' visual appearance. +// - All the paintMedia*() functions and extraMediaControlsStyleSheet() +// are forked from RenderThemeChromiumSkia instead of RenderThemeMac. +// +// For all other differences, if it was introduced in this file, then the +// maintainer forgot to include it in the list; otherwise it is an update that +// should have been applied to this file but was not. + // The methods in this file are specific to the Mac OS X platform. -// FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari. +// FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari. @interface WebCoreRenderThemeNotificationObserver : NSObject { @@ -80,52 +96,70 @@ using std::min; { [super init]; _theme = theme; - + return self; } -- (void)systemColorsDidChange:(NSNotification *)notification +- (void)systemColorsDidChange:(NSNotification *)unusedNotification { - ASSERT([[notification name] isEqualToString:NSSystemColorsDidChangeNotification]); + ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]); _theme->platformColorsDidChange(); } @end +@interface RTCMFlippedView : NSView +{} + +- (BOOL)isFlipped; +- (NSText *)currentEditor; + +@end + +@implementation RTCMFlippedView + +- (BOOL)isFlipped { + return [[NSGraphicsContext currentContext] isFlipped]; +} + +- (NSText *)currentEditor { + return nil; +} + +@end + namespace WebCore { using namespace HTMLNames; enum { - TopMargin, - RightMargin, - BottomMargin, - LeftMargin + topMargin, + rightMargin, + bottomMargin, + leftMargin }; enum { - TopPadding, - RightPadding, - BottomPadding, - LeftPadding + topPadding, + rightPadding, + bottomPadding, + leftPadding }; -// In our Mac port, we don't define PLATFORM(MAC) and thus don't pick up the -// |operator NSRect()| on WebCore::IntRect and FloatRect. This substitues for -// that missing conversion operator. -NSRect IntRectToNSRect(const IntRect & rect) -{ - return NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); -} - -NSRect FloatRectToNSRect(const FloatRect & rect) +// In Snow Leopard, many cells only check to see if the view they're passed is +// flipped, and if a nil view is passed, neglect to check if the current +// graphics context is flipped. Thus we pass a sham view to them, one whose +// flipped state just reflects the state of the context. +NSView* FlippedView() { - return NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); + static NSView* view = [[RTCMFlippedView alloc] init]; + return view; } -IntRect NSRectToIntRect(const NSRect & rect) +PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*) { - return IntRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); + static RenderTheme* rt = RenderThemeChromiumMac::create().releaseRef(); + return rt; } PassRefPtr<RenderTheme> RenderThemeChromiumMac::create() @@ -133,12 +167,6 @@ PassRefPtr<RenderTheme> RenderThemeChromiumMac::create() return adoptRef(new RenderThemeChromiumMac); } -PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) -{ - static RenderTheme* rt = RenderThemeChromiumMac::create().releaseRef(); - return rt; -} - RenderThemeChromiumMac::RenderThemeChromiumMac() : m_isSliderThumbHorizontalPressed(false) , m_isSliderThumbVerticalPressed(false) @@ -173,6 +201,16 @@ Color RenderThemeChromiumMac::platformActiveListBoxSelectionBackgroundColor() co return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); } +Color RenderThemeChromiumMac::platformActiveListBoxSelectionForegroundColor() const +{ + return Color::white; +} + +Color RenderThemeChromiumMac::platformInactiveListBoxSelectionForegroundColor() const +{ + return Color::black; +} + Color RenderThemeChromiumMac::platformFocusRingColor() const { if (ChromiumBridge::layoutTestMode()) @@ -181,6 +219,11 @@ Color RenderThemeChromiumMac::platformFocusRingColor() const return systemColor(CSSValueWebkitFocusRingColor); } +Color RenderThemeChromiumMac::platformInactiveListBoxSelectionBackgroundColor() const +{ + return platformInactiveSelectionBackgroundColor(); +} + static FontWeight toFontWeight(NSInteger appKitFontWeight) { ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15); @@ -210,51 +253,51 @@ static FontWeight toFontWeight(NSInteger appKitFontWeight) void RenderThemeChromiumMac::systemFont(int cssValueId, FontDescription& fontDescription) const { - static FontDescription systemFont; - static FontDescription smallSystemFont; - static FontDescription menuFont; - static FontDescription labelFont; - static FontDescription miniControlFont; - static FontDescription smallControlFont; - static FontDescription controlFont; + DEFINE_STATIC_LOCAL(FontDescription, systemFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, smallSystemFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, menuFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, labelFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, miniControlFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, smallControlFont, ()); + DEFINE_STATIC_LOCAL(FontDescription, controlFont, ()); FontDescription* cachedDesc; NSFont* font = nil; switch (cssValueId) { - case CSSValueSmallCaption: - cachedDesc = &smallSystemFont; - if (!smallSystemFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; - break; - case CSSValueMenu: - cachedDesc = &menuFont; - if (!menuFont.isAbsoluteSize()) - font = [NSFont menuFontOfSize:[NSFont systemFontSize]]; - break; - case CSSValueStatusBar: - cachedDesc = &labelFont; - if (!labelFont.isAbsoluteSize()) - font = [NSFont labelFontOfSize:[NSFont labelFontSize]]; - break; - case CSSValueWebkitMiniControl: - cachedDesc = &miniControlFont; - if (!miniControlFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]; - break; - case CSSValueWebkitSmallControl: - cachedDesc = &smallControlFont; - if (!smallControlFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; - break; - case CSSValueWebkitControl: - cachedDesc = &controlFont; - if (!controlFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; - break; - default: - cachedDesc = &systemFont; - if (!systemFont.isAbsoluteSize()) - font = [NSFont systemFontOfSize:[NSFont systemFontSize]]; + case CSSValueSmallCaption: + cachedDesc = &smallSystemFont; + if (!smallSystemFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; + break; + case CSSValueMenu: + cachedDesc = &menuFont; + if (!menuFont.isAbsoluteSize()) + font = [NSFont menuFontOfSize:[NSFont systemFontSize]]; + break; + case CSSValueStatusBar: + cachedDesc = &labelFont; + if (!labelFont.isAbsoluteSize()) + font = [NSFont labelFontOfSize:[NSFont labelFontSize]]; + break; + case CSSValueWebkitMiniControl: + cachedDesc = &miniControlFont; + if (!miniControlFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]; + break; + case CSSValueWebkitSmallControl: + cachedDesc = &smallControlFont; + if (!smallControlFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; + break; + case CSSValueWebkitControl: + cachedDesc = &controlFont; + if (!controlFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; + break; + default: + cachedDesc = &systemFont; + if (!systemFont.isAbsoluteSize()) + font = [NSFont systemFontOfSize:[NSFont systemFontSize]]; } if (font) { @@ -271,7 +314,7 @@ void RenderThemeChromiumMac::systemFont(int cssValueId, FontDescription& fontDes static RGBA32 convertNSColorToColor(NSColor *color) { - NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; if (colorInColorSpace) { static const double scaleFactor = nextafter(256.0, 0.0); return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]), @@ -279,7 +322,7 @@ static RGBA32 convertNSColorToColor(NSColor *color) static_cast<int>(scaleFactor * [colorInColorSpace blueComponent])); } - // This conversion above can fail if the NSColor in question is an NSPatternColor + // This conversion above can fail if the NSColor in question is an NSPatternColor // (as many system colors are). These colors are actually a repeating pattern // not just a solid color. To work around this we simply draw a 1x1 image of // the color and use that pixel's color. It might be better to use an average of @@ -291,7 +334,7 @@ static RGBA32 convertNSColorToColor(NSColor *color) samplesPerPixel:4 hasAlpha:YES isPlanar:NO - colorSpaceName:NSCalibratedRGBColorSpace + colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:4 bitsPerPixel:32]; @@ -318,7 +361,7 @@ static RGBA32 menuBackgroundColor() samplesPerPixel:4 hasAlpha:YES isPlanar:NO - colorSpaceName:NSCalibratedRGBColorSpace + colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:4 bitsPerPixel:32]; @@ -347,104 +390,104 @@ Color RenderThemeChromiumMac::systemColor(int cssValueId) const { if (m_systemColorCache.contains(cssValueId)) return m_systemColorCache.get(cssValueId); - + Color color; switch (cssValueId) { - case CSSValueActiveborder: - color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); - break; - case CSSValueActivecaption: - color = convertNSColorToColor([NSColor windowFrameTextColor]); - break; - case CSSValueAppworkspace: - color = convertNSColorToColor([NSColor headerColor]); - break; - case CSSValueBackground: - // Use theme independent default - break; - case CSSValueButtonface: - // We use this value instead of NSColor's controlColor to avoid website incompatibilities. - // We may want to change this to use the NSColor in future. - color = 0xFFC0C0C0; - break; - case CSSValueButtonhighlight: - color = convertNSColorToColor([NSColor controlHighlightColor]); - break; - case CSSValueButtonshadow: - color = convertNSColorToColor([NSColor controlShadowColor]); - break; - case CSSValueButtontext: - color = convertNSColorToColor([NSColor controlTextColor]); - break; - case CSSValueCaptiontext: - color = convertNSColorToColor([NSColor textColor]); - break; - case CSSValueGraytext: - color = convertNSColorToColor([NSColor disabledControlTextColor]); - break; - case CSSValueHighlight: - color = convertNSColorToColor([NSColor selectedTextBackgroundColor]); - break; - case CSSValueHighlighttext: - color = convertNSColorToColor([NSColor selectedTextColor]); - break; - case CSSValueInactiveborder: - color = convertNSColorToColor([NSColor controlBackgroundColor]); - break; - case CSSValueInactivecaption: - color = convertNSColorToColor([NSColor controlBackgroundColor]); - break; - case CSSValueInactivecaptiontext: - color = convertNSColorToColor([NSColor textColor]); - break; - case CSSValueInfobackground: - // There is no corresponding NSColor for this so we use a hard coded value. - color = 0xFFFBFCC5; - break; - case CSSValueInfotext: - color = convertNSColorToColor([NSColor textColor]); - break; - case CSSValueMenu: - color = menuBackgroundColor(); - break; - case CSSValueMenutext: - color = convertNSColorToColor([NSColor selectedMenuItemTextColor]); - break; - case CSSValueScrollbar: - color = convertNSColorToColor([NSColor scrollBarColor]); - break; - case CSSValueText: - color = convertNSColorToColor([NSColor textColor]); - break; - case CSSValueThreeddarkshadow: - color = convertNSColorToColor([NSColor controlDarkShadowColor]); - break; - case CSSValueThreedshadow: - color = convertNSColorToColor([NSColor shadowColor]); - break; - case CSSValueThreedface: - // We use this value instead of NSColor's controlColor to avoid website incompatibilities. - // We may want to change this to use the NSColor in future. - color = 0xFFC0C0C0; - break; - case CSSValueThreedhighlight: - color = convertNSColorToColor([NSColor highlightColor]); - break; - case CSSValueThreedlightshadow: - color = convertNSColorToColor([NSColor controlLightHighlightColor]); - break; - case CSSValueWebkitFocusRingColor: - color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); - break; - case CSSValueWindow: - color = convertNSColorToColor([NSColor windowBackgroundColor]); - break; - case CSSValueWindowframe: - color = convertNSColorToColor([NSColor windowFrameColor]); - break; - case CSSValueWindowtext: - color = convertNSColorToColor([NSColor windowFrameTextColor]); - break; + case CSSValueActiveborder: + color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); + break; + case CSSValueActivecaption: + color = convertNSColorToColor([NSColor windowFrameTextColor]); + break; + case CSSValueAppworkspace: + color = convertNSColorToColor([NSColor headerColor]); + break; + case CSSValueBackground: + // Use theme independent default + break; + case CSSValueButtonface: + // We use this value instead of NSColor's controlColor to avoid website incompatibilities. + // We may want to change this to use the NSColor in future. + color = 0xFFC0C0C0; + break; + case CSSValueButtonhighlight: + color = convertNSColorToColor([NSColor controlHighlightColor]); + break; + case CSSValueButtonshadow: + color = convertNSColorToColor([NSColor controlShadowColor]); + break; + case CSSValueButtontext: + color = convertNSColorToColor([NSColor controlTextColor]); + break; + case CSSValueCaptiontext: + color = convertNSColorToColor([NSColor textColor]); + break; + case CSSValueGraytext: + color = convertNSColorToColor([NSColor disabledControlTextColor]); + break; + case CSSValueHighlight: + color = convertNSColorToColor([NSColor selectedTextBackgroundColor]); + break; + case CSSValueHighlighttext: + color = convertNSColorToColor([NSColor selectedTextColor]); + break; + case CSSValueInactiveborder: + color = convertNSColorToColor([NSColor controlBackgroundColor]); + break; + case CSSValueInactivecaption: + color = convertNSColorToColor([NSColor controlBackgroundColor]); + break; + case CSSValueInactivecaptiontext: + color = convertNSColorToColor([NSColor textColor]); + break; + case CSSValueInfobackground: + // There is no corresponding NSColor for this so we use a hard coded value. + color = 0xFFFBFCC5; + break; + case CSSValueInfotext: + color = convertNSColorToColor([NSColor textColor]); + break; + case CSSValueMenu: + color = menuBackgroundColor(); + break; + case CSSValueMenutext: + color = convertNSColorToColor([NSColor selectedMenuItemTextColor]); + break; + case CSSValueScrollbar: + color = convertNSColorToColor([NSColor scrollBarColor]); + break; + case CSSValueText: + color = convertNSColorToColor([NSColor textColor]); + break; + case CSSValueThreeddarkshadow: + color = convertNSColorToColor([NSColor controlDarkShadowColor]); + break; + case CSSValueThreedshadow: + color = convertNSColorToColor([NSColor shadowColor]); + break; + case CSSValueThreedface: + // We use this value instead of NSColor's controlColor to avoid website incompatibilities. + // We may want to change this to use the NSColor in future. + color = 0xFFC0C0C0; + break; + case CSSValueThreedhighlight: + color = convertNSColorToColor([NSColor highlightColor]); + break; + case CSSValueThreedlightshadow: + color = convertNSColorToColor([NSColor controlLightHighlightColor]); + break; + case CSSValueWebkitFocusRingColor: + color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); + break; + case CSSValueWindow: + color = convertNSColorToColor([NSColor windowBackgroundColor]); + break; + case CSSValueWindowframe: + color = convertNSColorToColor([NSColor windowFrameColor]); + break; + case CSSValueWindowtext: + color = convertNSColorToColor([NSColor windowFrameTextColor]); + break; } if (!color.isValid()) @@ -457,11 +500,11 @@ Color RenderThemeChromiumMac::systemColor(int cssValueId) const } bool RenderThemeChromiumMac::isControlStyled(const RenderStyle* style, const BorderData& border, - const FillLayer& background, const Color& backgroundColor) const + const FillLayer& background, const Color& backgroundColor) const { if (style->appearance() == TextFieldPart || style->appearance() == TextAreaPart || style->appearance() == ListboxPart) return style->border() != border; - + // FIXME: This is horrible, but there is not much else that can be done. Menu lists cannot draw properly when // scaled. They can't really draw properly when transformed either. We can't detect the transform case at style // adjustment time so that will just have to stay broken. We can however detect that we're zooming. If zooming @@ -472,62 +515,33 @@ bool RenderThemeChromiumMac::isControlStyled(const RenderStyle* style, const Bor return RenderTheme::isControlStyled(style, border, background, backgroundColor); } -// FIXME: Use the code from the old upstream version, before it was converted to the new theme API in r37731. void RenderThemeChromiumMac::adjustRepaintRect(const RenderObject* o, IntRect& r) { - float zoomLevel = o->style()->effectiveZoom(); - - switch (o->style()->appearance()) { - case CheckboxPart: { - // Since we query the prototype cell, we need to update its state to match. - setCheckboxCellState(o, r); - - // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox - // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. - IntSize size = checkboxSizes()[[checkbox() controlSize]]; - size.setHeight(size.height() * zoomLevel); - size.setWidth(size.width() * zoomLevel); - r = inflateRect(r, size, checkboxMargins(), zoomLevel); - break; + ControlPart part = o->style()->appearance(); + +#if USE(NEW_THEME) + switch (part) { + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case ListButtonPart: + case DefaultButtonPart: + case ButtonPart: + return RenderTheme::adjustRepaintRect(o, r); + default: + break; } - case RadioPart: { - // Since we query the prototype cell, we need to update its state to match. - setRadioCellState(o, r); +#endif - // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox - // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. - IntSize size = radioSizes()[[radio() controlSize]]; - size.setHeight(size.height() * zoomLevel); - size.setWidth(size.width() * zoomLevel); - r = inflateRect(r, size, radioMargins(), zoomLevel); - break; - } - case PushButtonPart: - case DefaultButtonPart: - case ButtonPart: { - // Since we query the prototype cell, we need to update its state to match. - setButtonCellState(o, r); - - // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox - // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. - if ([button() bezelStyle] == NSRoundedBezelStyle) { - IntSize size = buttonSizes()[[button() controlSize]]; - size.setHeight(size.height() * zoomLevel); - size.setWidth(r.width()); - r = inflateRect(r, size, buttonMargins(), zoomLevel); - } - break; - } - case MenulistPart: { + float zoomLevel = o->style()->effectiveZoom(); + + if (part == MenulistPart) { setPopupButtonCellState(o, r); IntSize size = popupButtonSizes()[[popupButton() controlSize]]; size.setHeight(size.height() * zoomLevel); size.setWidth(r.width()); r = inflateRect(r, size, popupButtonMargins(), zoomLevel); - break; - } - default: - break; } } @@ -535,20 +549,61 @@ IntRect RenderThemeChromiumMac::inflateRect(const IntRect& r, const IntSize& siz { // Only do the inflation if the available width/height are too small. Otherwise try to // fit the glow/check space into the available box's width/height. - int widthDelta = r.width() - (size.width() + margins[LeftMargin] * zoomLevel + margins[RightMargin] * zoomLevel); - int heightDelta = r.height() - (size.height() + margins[TopMargin] * zoomLevel + margins[BottomMargin] * zoomLevel); + int widthDelta = r.width() - (size.width() + margins[leftMargin] * zoomLevel + margins[rightMargin] * zoomLevel); + int heightDelta = r.height() - (size.height() + margins[topMargin] * zoomLevel + margins[bottomMargin] * zoomLevel); IntRect result(r); if (widthDelta < 0) { - result.setX(result.x() - margins[LeftMargin] * zoomLevel); + result.setX(result.x() - margins[leftMargin] * zoomLevel); result.setWidth(result.width() - widthDelta); } if (heightDelta < 0) { - result.setY(result.y() - margins[TopMargin] * zoomLevel); + result.setY(result.y() - margins[topMargin] * zoomLevel); result.setHeight(result.height() - heightDelta); } return result; } +FloatRect RenderThemeChromiumMac::convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect& r) const +{ + FloatRect partRect(inputRect); + + // Compute an offset between the part renderer and the input renderer + FloatSize offsetFromInputRenderer; + const RenderObject* renderer = partRenderer; + while (renderer && renderer != inputRenderer) { + RenderObject* containingRenderer = renderer->container(); + offsetFromInputRenderer -= renderer->offsetFromContainer(containingRenderer); + renderer = containingRenderer; + } + // If the input renderer was not a container, something went wrong + ASSERT(renderer == inputRenderer); + // Move the rect into partRenderer's coords + partRect.move(offsetFromInputRenderer); + // Account for the local drawing offset (tx, ty) + partRect.move(r.x(), r.y()); + + return partRect; +} + +// Updates the control tint (a.k.a. active state) of |cell| (from |o|). +// In the Chromium port, the renderer runs as a background process and controls' +// NSCell(s) lack a parent NSView. Therefore controls don't have their tint +// color updated correctly when the application is activated/deactivated. +// FocusController's setActive() is called when the application is +// activated/deactivated, which causes a repaint at which time this code is +// called. +// This function should be called before drawing any NSCell-derived controls, +// unless you're sure it isn't needed. +void RenderThemeChromiumMac::updateActiveState(NSCell* cell, const RenderObject* o) +{ + NSControlTint oldTint = [cell controlTint]; + NSControlTint tint = isActive(o) ? [NSColor currentControlTint] : + NSClearControlTint; + + if (tint != oldTint) + [cell setControlTint:tint]; +} + void RenderThemeChromiumMac::updateCheckedState(NSCell* cell, const RenderObject* o) { bool oldIndeterminate = [cell state] == NSMixedState; @@ -589,19 +644,6 @@ void RenderThemeChromiumMac::updatePressedState(NSCell* cell, const RenderObject [cell setHighlighted:pressed]; } -// 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->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); -} - bool RenderThemeChromiumMac::controlSupportsTints(const RenderObject* o) const { // An alternate way to implement this would be to get the appropriate cell object @@ -673,7 +715,7 @@ void RenderThemeChromiumMac::setSizeFromFont(RenderStyle* style, const IntSize* style->setHeight(Length(size.height(), Fixed)); } -void RenderThemeChromiumMac::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const +void RenderThemeChromiumMac::setFontFromControlSize(CSSStyleSelector*, RenderStyle* style, NSControlSize controlSize) const { FontDescription fontDescription; fontDescription.setIsAbsoluteSize(true); @@ -701,349 +743,10 @@ NSControlSize RenderThemeChromiumMac::controlSizeForSystemFont(RenderStyle* styl return NSMiniControlSize; } -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -bool RenderThemeChromiumMac::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) -{ - LocalCurrentGraphicsContext localContext(paintInfo.context); - - // Determine the width and height needed for the control and prepare the cell for painting. - setCheckboxCellState(o, r); - - paintInfo.context->save(); - - float zoomLevel = o->style()->effectiveZoom(); - - // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox - // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. - NSButtonCell* checkbox = this->checkbox(); - IntSize size = checkboxSizes()[[checkbox controlSize]]; - size.setWidth(size.width() * zoomLevel); - size.setHeight(size.height() * zoomLevel); - IntRect inflatedRect = inflateRect(r, size, checkboxMargins(), zoomLevel); - - if (zoomLevel != 1.0f) { - inflatedRect.setWidth(inflatedRect.width() / zoomLevel); - inflatedRect.setHeight(inflatedRect.height() / zoomLevel); - paintInfo.context->translate(inflatedRect.x(), inflatedRect.y()); - paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); - paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y()); - } - - [checkbox drawWithFrame:NSRect(IntRectToNSRect(inflatedRect)) inView:nil]; - [checkbox setControlView:nil]; - - paintInfo.context->restore(); - - return false; -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -const IntSize* RenderThemeChromiumMac::checkboxSizes() const -{ - static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) }; - return sizes; -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -const int* RenderThemeChromiumMac::checkboxMargins() const -{ - static const int margins[3][4] = - { - { 3, 4, 4, 2 }, - { 4, 3, 3, 3 }, - { 4, 3, 3, 3 }, - }; - return margins[[checkbox() controlSize]]; -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -void RenderThemeChromiumMac::setCheckboxCellState(const RenderObject* o, const IntRect& r) -{ - NSButtonCell* checkbox = this->checkbox(); - - // Set the control size based off the rectangle we're painting into. - setControlSize(checkbox, checkboxSizes(), r.size(), o->style()->effectiveZoom()); - - // Update the various states we respond to. - updateCheckedState(checkbox, o); - updateEnabledState(checkbox, o); - updatePressedState(checkbox, o); - updateFocusedState(checkbox, o); -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -void RenderThemeChromiumMac::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; - - // Use the font size to determine the intrinsic width of the control. - setSizeFromFont(style, checkboxSizes()); -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -bool RenderThemeChromiumMac::paintRadio(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) -{ - LocalCurrentGraphicsContext localContext(paintInfo.context); - - // Determine the width and height needed for the control and prepare the cell for painting. - setRadioCellState(o, r); - - paintInfo.context->save(); - - float zoomLevel = o->style()->effectiveZoom(); - - // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox - // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. - NSButtonCell* radio = this->radio(); - IntSize size = radioSizes()[[radio controlSize]]; - size.setWidth(size.width() * zoomLevel); - size.setHeight(size.height() * zoomLevel); - IntRect inflatedRect = inflateRect(r, size, radioMargins(), zoomLevel); - - if (zoomLevel != 1.0f) { - inflatedRect.setWidth(inflatedRect.width() / zoomLevel); - inflatedRect.setHeight(inflatedRect.height() / zoomLevel); - paintInfo.context->translate(inflatedRect.x(), inflatedRect.y()); - paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); - paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y()); - } - - [radio drawWithFrame:NSRect(IntRectToNSRect(inflatedRect)) inView:nil]; - [radio setControlView:nil]; - - paintInfo.context->restore(); - - return false; -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -const IntSize* RenderThemeChromiumMac::radioSizes() const -{ - static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) }; - return sizes; -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -const int* RenderThemeChromiumMac::radioMargins() const -{ - static const int margins[3][4] = - { - { 2, 2, 4, 2 }, - { 3, 2, 3, 2 }, - { 1, 0, 2, 0 }, - }; - return margins[[radio() controlSize]]; -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -void RenderThemeChromiumMac::setRadioCellState(const RenderObject* o, const IntRect& r) -{ - NSButtonCell* radio = this->radio(); - - // Set the control size based off the rectangle we're painting into. - setControlSize(radio, radioSizes(), r.size(), o->style()->effectiveZoom()); - - // Update the various states we respond to. - updateCheckedState(radio, o); - updateEnabledState(radio, o); - updatePressedState(radio, o); - updateFocusedState(radio, o); -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -void RenderThemeChromiumMac::setRadioSize(RenderStyle* style) const -{ - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - // Use the font size to determine the intrinsic width of the control. - setSizeFromFont(style, radioSizes()); -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -void RenderThemeChromiumMac::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const -{ - // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large - // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is - // by definition constrained, since we select mini only for small cramped environments. - // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent - // padding. - const int padding = 8 * style->effectiveZoom(); - style->setPaddingLeft(Length(padding, Fixed)); - style->setPaddingRight(Length(padding, Fixed)); - style->setPaddingTop(Length(0, Fixed)); - style->setPaddingBottom(Length(0, Fixed)); -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -void RenderThemeChromiumMac::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const -{ - // There are three appearance constants for buttons. - // (1) Push-button is the constant for the default Aqua system button. Push buttons will not scale vertically and will not allow - // custom fonts or colors. <input>s use this constant. This button will allow custom colors and font weights/variants but won't - // scale vertically. - // (2) square-button is the constant for the square button. This button will allow custom fonts and colors and will scale vertically. - // (3) Button is the constant that means "pick the best button as appropriate." <button>s use this constant. This button will - // also scale vertically and allow custom fonts and colors. It will attempt to use Aqua if possible and will make this determination - // solely on the rectangle of the control. - - // Determine our control size based off our font. - NSControlSize controlSize = controlSizeForFont(style); - - if (style->appearance() == PushButtonPart) { - // Ditch the border. - style->resetBorder(); - - // Height is locked to auto. - style->setHeight(Length(Auto)); - - // White-space is locked to pre - style->setWhiteSpace(PRE); - - // Set the button's vertical size. - setButtonSize(style); - - // Add in the padding that we'd like to use. - setButtonPaddingFromControlSize(style, controlSize); - - // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out - // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate - // system font for the control size instead. - setFontFromControlSize(selector, style, controlSize); - } else { - // Set a min-height so that we can't get smaller than the mini button. - style->setMinHeight(Length(static_cast<int>(15 * style->effectiveZoom()), Fixed)); - - // Reset the top and bottom borders. - style->resetBorderTop(); - style->resetBorderBottom(); - } - - style->setBoxShadow(0); -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -const IntSize* RenderThemeChromiumMac::buttonSizes() const -{ - static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; - return sizes; -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -const int* RenderThemeChromiumMac::buttonMargins() const -{ - static const int margins[3][4] = - { - { 4, 6, 7, 6 }, - { 4, 5, 6, 5 }, - { 0, 1, 1, 1 }, - }; - return margins[[button() controlSize]]; -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -void RenderThemeChromiumMac::setButtonSize(RenderStyle* style) const -{ - // If the width and height are both specified, then we have nothing to do. - if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) - return; - - // Use the font size to determine the intrinsic width of the control. - setSizeFromFont(style, buttonSizes()); -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -void RenderThemeChromiumMac::setButtonCellState(const RenderObject* o, const IntRect& r) -{ - NSButtonCell* button = this->button(); - - // Set the control size based off the rectangle we're painting into. - if (o->style()->appearance() == SquareButtonPart || - r.height() > buttonSizes()[NSRegularControlSize].height() * o->style()->effectiveZoom()) { - // Use the square button - if ([button bezelStyle] != NSShadowlessSquareBezelStyle) - [button setBezelStyle:NSShadowlessSquareBezelStyle]; - } else if ([button bezelStyle] != NSRoundedBezelStyle) - [button setBezelStyle:NSRoundedBezelStyle]; - - setControlSize(button, buttonSizes(), r.size(), o->style()->effectiveZoom()); - - NSWindow *window = [nil window]; - BOOL isDefaultButton = (isDefault(o) && [window isKeyWindow]); - [button setKeyEquivalent:(isDefaultButton ? @"\r" : @"")]; - - // Update the various states we respond to. - updateCheckedState(button, o); - updateEnabledState(button, o); - updatePressedState(button, o); - updateFocusedState(button, o); -} - -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -bool RenderThemeChromiumMac::paintButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) -{ - NSButtonCell* button = this->button(); - LocalCurrentGraphicsContext localContext(paintInfo.context); - - // Determine the width and height needed for the control and prepare the cell for painting. - setButtonCellState(o, r); - - paintInfo.context->save(); - - // We inflate the rect as needed to account for padding included in the cell to accommodate the button - // shadow. We don't consider this part of the bounds of the control in WebKit. - float zoomLevel = o->style()->effectiveZoom(); - IntSize size = buttonSizes()[[button controlSize]]; - size.setWidth(r.width()); - size.setHeight(size.height() * zoomLevel); - IntRect inflatedRect = r; - if ([button bezelStyle] == NSRoundedBezelStyle) { - // Center the button within the available space. - if (inflatedRect.height() > size.height()) { - inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2); - inflatedRect.setHeight(size.height()); - } - - // Now inflate it to account for the shadow. - inflatedRect = inflateRect(inflatedRect, size, buttonMargins(), zoomLevel); - - if (zoomLevel != 1.0f) { - inflatedRect.setWidth(inflatedRect.width() / zoomLevel); - inflatedRect.setHeight(inflatedRect.height() / zoomLevel); - paintInfo.context->translate(inflatedRect.x(), inflatedRect.y()); - paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); - paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y()); - } - } - - NSView *view = nil; - NSWindow *window = [view window]; - NSButtonCell *previousDefaultButtonCell = [window defaultButtonCell]; - - if (isDefault(o) && [window isKeyWindow]) { - [window setDefaultButtonCell:button]; - wkAdvanceDefaultButtonPulseAnimation(button); - } else if ([previousDefaultButtonCell isEqual:button]) - [window setDefaultButtonCell:nil]; - - [button drawWithFrame:NSRect(IntRectToNSRect(inflatedRect)) inView:view]; - [button setControlView:nil]; - - if (![previousDefaultButtonCell isEqual:button]) - [window setDefaultButtonCell:previousDefaultButtonCell]; - - paintInfo.context->restore(); - - return false; -} - bool RenderThemeChromiumMac::paintTextField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawBezeledTextFieldCell(IntRectToNSRect(r), isEnabled(o) && !isReadOnlyControl(o)); + wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o)); return false; } @@ -1051,21 +754,21 @@ void RenderThemeChromiumMac::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle { } -bool RenderThemeChromiumMac::paintCapsLockIndicator(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +bool RenderThemeChromiumMac::paintCapsLockIndicator(RenderObject*, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { if (paintInfo.context->paintingDisabled()) return true; LocalCurrentGraphicsContext localContext(paintInfo.context); wkDrawCapsLockIndicator(paintInfo.context->platformContext(), r); - + return false; } bool RenderThemeChromiumMac::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawBezeledTextArea(IntRectToNSRect(r), isEnabled(o) && !isReadOnlyControl(o)); + wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o)); return false; } @@ -1103,8 +806,6 @@ const int* RenderThemeChromiumMac::popupButtonPadding(NSControlSize size) const bool RenderThemeChromiumMac::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - LocalCurrentGraphicsContext localContext(paintInfo.context); - setPopupButtonCellState(o, r); NSPopUpButtonCell* popupButton = this->popupButton(); @@ -1120,7 +821,7 @@ bool RenderThemeChromiumMac::paintMenuList(RenderObject* o, const RenderObject:: inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins(), zoomLevel); paintInfo.context->save(); - + #ifndef BUILDING_ON_TIGER // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect paintInfo.context->clip(inflatedRect); @@ -1134,7 +835,7 @@ bool RenderThemeChromiumMac::paintMenuList(RenderObject* o, const RenderObject:: paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y()); } - [popupButton drawWithFrame:IntRectToNSRect(inflatedRect) inView:nil]; + [popupButton drawWithFrame:inflatedRect inView:FlippedView()]; [popupButton setControlView:nil]; paintInfo.context->restore(); @@ -1142,19 +843,19 @@ bool RenderThemeChromiumMac::paintMenuList(RenderObject* o, const RenderObject:: return false; } -static const float baseFontSize = 11.0f; -static const float baseArrowHeight = 4.0f; -static const float baseArrowWidth = 5.0f; -static const float baseSpaceBetweenArrows = 2.0f; -static const int arrowPaddingLeft = 6; -static const int arrowPaddingRight = 6; -static const int paddingBeforeSeparator = 4; -static const int baseBorderRadius = 5; -static const int styledPopupPaddingLeft = 8; -static const int styledPopupPaddingTop = 1; -static const int styledPopupPaddingBottom = 2; +const float baseFontSize = 11.0f; +const float baseArrowHeight = 4.0f; +const float baseArrowWidth = 5.0f; +const float baseSpaceBetweenArrows = 2.0f; +const int arrowPaddingLeft = 6; +const int arrowPaddingRight = 6; +const int paddingBeforeSeparator = 4; +const int baseBorderRadius = 5; +const int styledPopupPaddingLeft = 8; +const int styledPopupPaddingTop = 1; +const int styledPopupPaddingBottom = 2; -static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) +static void TopGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) { static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f }; static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f }; @@ -1164,7 +865,7 @@ static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* o outData[i] = (1.0f - a) * dark[i] + a * light[i]; } -static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) +static void BottomGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) { static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f }; static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f }; @@ -1174,7 +875,7 @@ static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat outData[i] = (1.0f - a) * dark[i] + a * light[i]; } -static void MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) +static void MainGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) { static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f }; static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; @@ -1184,7 +885,7 @@ static void MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData[i] = (1.0f - a) * dark[i] + a * light[i]; } -static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) +static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) { static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f }; static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f }; @@ -1196,11 +897,21 @@ static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* void RenderThemeChromiumMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { + if (r.isEmpty()) + return; + CGContextRef context = paintInfo.context->platformContext(); paintInfo.context->save(); - int radius = o->style()->borderTopLeftRadius().width(); + IntSize topLeftRadius; + IntSize topRightRadius; + IntSize bottomLeftRadius; + IntSize bottomRightRadius; + + o->style()->getBorderRadiiForRect(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + + int radius = topLeftRadius.width(); RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB()); @@ -1223,33 +934,27 @@ void RenderThemeChromiumMac::paintMenuListButtonGradients(RenderObject* o, const RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(), r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false)); paintInfo.context->save(); CGContextClipToRect(context, r); - paintInfo.context->addRoundedRectClip(r, - o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), - o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); + paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); CGContextDrawShading(context, mainShading.get()); paintInfo.context->restore(); paintInfo.context->save(); CGContextClipToRect(context, topGradient); - paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), - o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), - IntSize(), IntSize()); + paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), topLeftRadius, topRightRadius, IntSize(), IntSize()); CGContextDrawShading(context, topShading.get()); paintInfo.context->restore(); - paintInfo.context->save(); - CGContextClipToRect(context, bottomGradient); - paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), - IntSize(), IntSize(), - o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); - CGContextDrawShading(context, bottomShading.get()); - paintInfo.context->restore(); + if (!bottomGradient.isEmpty()) { + paintInfo.context->save(); + CGContextClipToRect(context, bottomGradient); + paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), IntSize(), IntSize(), bottomLeftRadius, bottomRightRadius); + CGContextDrawShading(context, bottomShading.get()); + paintInfo.context->restore(); + } paintInfo.context->save(); CGContextClipToRect(context, r); - paintInfo.context->addRoundedRectClip(r, - o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), - o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); + paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); CGContextDrawShading(context, leftShading.get()); CGContextDrawShading(context, rightShading.get()); paintInfo.context->restore(); @@ -1259,8 +964,6 @@ void RenderThemeChromiumMac::paintMenuListButtonGradients(RenderObject* o, const bool RenderThemeChromiumMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - paintInfo.context->save(); - IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(), r.y() + o->style()->borderTopWidth(), r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(), @@ -1278,8 +981,10 @@ bool RenderThemeChromiumMac::paintMenuListButton(RenderObject* o, const RenderOb if (bounds.width() < arrowWidth + arrowPaddingLeft * o->style()->effectiveZoom()) return false; - - paintInfo.context->setFillColor(o->style()->color()); + + paintInfo.context->save(); + + paintInfo.context->setFillColor(o->style()->color(), o->style()->colorSpace()); paintInfo.context->setStrokeStyle(NoStroke); FloatPoint arrow1[3]; @@ -1308,11 +1013,11 @@ bool RenderThemeChromiumMac::paintMenuListButton(RenderObject* o, const RenderOb // Draw the separator to the left of the arrows paintInfo.context->setStrokeThickness(1.0f); // Deliberately ignores zoom since it looks nicer if it stays thin. paintInfo.context->setStrokeStyle(SolidStroke); - paintInfo.context->setStrokeColor(leftSeparatorColor); + paintInfo.context->setStrokeColor(leftSeparatorColor, DeviceColorSpace); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), IntPoint(leftEdgeOfSeparator, bounds.bottom())); - paintInfo.context->setStrokeColor(rightSeparatorColor); + paintInfo.context->setStrokeColor(rightSeparatorColor, DeviceColorSpace); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom())); @@ -1332,7 +1037,7 @@ void RenderThemeChromiumMac::adjustMenuListStyle(CSSStyleSelector* selector, Ren style->resetBorder(); style->resetPadding(); - + // Height is locked to auto. style->setHeight(Length(Auto)); @@ -1357,7 +1062,7 @@ void RenderThemeChromiumMac::adjustMenuListStyle(CSSStyleSelector* selector, Ren int RenderThemeChromiumMac::popupInternalPaddingLeft(RenderStyle* style) const { if (style->appearance() == MenulistPart) - return popupButtonPadding(controlSizeForFont(style))[LeftPadding] * style->effectiveZoom(); + return popupButtonPadding(controlSizeForFont(style))[leftPadding] * style->effectiveZoom(); if (style->appearance() == MenulistButtonPart) return styledPopupPaddingLeft * style->effectiveZoom(); return 0; @@ -1366,7 +1071,7 @@ int RenderThemeChromiumMac::popupInternalPaddingLeft(RenderStyle* style) const int RenderThemeChromiumMac::popupInternalPaddingRight(RenderStyle* style) const { if (style->appearance() == MenulistPart) - return popupButtonPadding(controlSizeForFont(style))[RightPadding] * style->effectiveZoom(); + return popupButtonPadding(controlSizeForFont(style))[rightPadding] * style->effectiveZoom(); if (style->appearance() == MenulistButtonPart) { float fontScale = style->fontSize() / baseFontSize; float arrowWidth = baseArrowWidth * fontScale; @@ -1378,7 +1083,7 @@ int RenderThemeChromiumMac::popupInternalPaddingRight(RenderStyle* style) const int RenderThemeChromiumMac::popupInternalPaddingTop(RenderStyle* style) const { if (style->appearance() == MenulistPart) - return popupButtonPadding(controlSizeForFont(style))[TopPadding] * style->effectiveZoom(); + return popupButtonPadding(controlSizeForFont(style))[topPadding] * style->effectiveZoom(); if (style->appearance() == MenulistButtonPart) return styledPopupPaddingTop * style->effectiveZoom(); return 0; @@ -1387,13 +1092,13 @@ int RenderThemeChromiumMac::popupInternalPaddingTop(RenderStyle* style) const int RenderThemeChromiumMac::popupInternalPaddingBottom(RenderStyle* style) const { if (style->appearance() == MenulistPart) - return popupButtonPadding(controlSizeForFont(style))[BottomPadding] * style->effectiveZoom(); + return popupButtonPadding(controlSizeForFont(style))[bottomPadding] * style->effectiveZoom(); if (style->appearance() == MenulistButtonPart) return styledPopupPaddingBottom * style->effectiveZoom(); return 0; } -void RenderThemeChromiumMac::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +void RenderThemeChromiumMac::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { float fontScale = style->fontSize() / baseFontSize; @@ -1402,7 +1107,7 @@ void RenderThemeChromiumMac::adjustMenuListButtonStyle(CSSStyleSelector* selecto const int minHeight = 15; style->setMinHeight(Length(minHeight, Fixed)); - + style->setLineHeight(RenderStyle::initialLineHeight()); } @@ -1414,6 +1119,7 @@ void RenderThemeChromiumMac::setPopupButtonCellState(const RenderObject* o, cons setControlSize(popupButton, popupButtonSizes(), r.size(), o->style()->effectiveZoom()); // Update the various states we respond to. + updateActiveState(popupButton, o); updateCheckedState(popupButton, o); updateEnabledState(popupButton, o); updatePressedState(popupButton, o); @@ -1431,10 +1137,10 @@ int RenderThemeChromiumMac::minimumMenuListSize(RenderStyle* style) const return sizeForSystemFont(style, menuListSizes()).width(); } -static const int trackWidth = 5; -static const int trackRadius = 2; +const int trackWidth = 5; +const int trackRadius = 2; -void RenderThemeChromiumMac::adjustSliderTrackStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +void RenderThemeChromiumMac::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { style->setBoxShadow(0); } @@ -1474,16 +1180,16 @@ bool RenderThemeChromiumMac::paintSliderTrack(RenderObject* o, const RenderObjec radius, radius); CGContextDrawShading(context, mainShading.get()); paintInfo.context->restore(); - + return false; } -void RenderThemeChromiumMac::adjustSliderThumbStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +void RenderThemeChromiumMac::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { style->setBoxShadow(0); } -static const float verticalSliderHeightPadding = 0.1f; +const float verticalSliderHeightPadding = 0.1f; bool RenderThemeChromiumMac::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { @@ -1496,6 +1202,7 @@ bool RenderThemeChromiumMac::paintSliderThumb(RenderObject* o, const RenderObjec LocalCurrentGraphicsContext localContext(paintInfo.context); // Update the various states we respond to. + updateActiveState(sliderThumbCell, o->parent()); updateEnabledState(sliderThumbCell, o->parent()); updateFocusedState(sliderThumbCell, o->parent()); @@ -1527,7 +1234,7 @@ bool RenderThemeChromiumMac::paintSliderThumb(RenderObject* o, const RenderObjec paintInfo.context->save(); float zoomLevel = o->style()->effectiveZoom(); - + FloatRect unzoomedRect = bounds; if (zoomLevel != 1.0f) { unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); @@ -1537,7 +1244,7 @@ bool RenderThemeChromiumMac::paintSliderThumb(RenderObject* o, const RenderObjec paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); } - [sliderThumbCell drawWithFrame:FloatRectToNSRect(unzoomedRect) inView:nil]; + [sliderThumbCell drawWithFrame:unzoomedRect inView:FlippedView()]; [sliderThumbCell setControlView:nil]; paintInfo.context->restore(); @@ -1545,23 +1252,6 @@ bool RenderThemeChromiumMac::paintSliderThumb(RenderObject* o, const RenderObjec return false; } -const int sliderThumbWidth = 15; -const int sliderThumbHeight = 15; -const int mediaSliderThumbWidth = 13; -const int mediaSliderThumbHeight = 14; - -void RenderThemeChromiumMac::adjustSliderThumbSize(RenderObject* o) const -{ - float zoomLevel = o->style()->effectiveZoom(); - if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == SliderThumbVerticalPart) { - o->style()->setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed)); - o->style()->setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed)); - } else if (o->style()->appearance() == MediaSliderThumbPart) { - o->style()->setWidth(Length(mediaSliderThumbWidth, Fixed)); - o->style()->setHeight(Length(mediaSliderThumbHeight, Fixed)); - } -} - bool RenderThemeChromiumMac::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { NSSearchFieldCell* search = this->search(); @@ -1574,7 +1264,7 @@ bool RenderThemeChromiumMac::paintSearchField(RenderObject* o, const RenderObjec float zoomLevel = o->style()->effectiveZoom(); IntRect unzoomedRect = r; - + if (zoomLevel != 1.0f) { unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); @@ -1586,7 +1276,7 @@ bool RenderThemeChromiumMac::paintSearchField(RenderObject* o, const RenderObjec // Set the search button to nil before drawing. Then reset it so we can draw it later. [search setSearchButtonCell:nil]; - [search drawWithFrame:NSRect(IntRectToNSRect(unzoomedRect)) inView:nil]; + [search drawWithFrame:NSRect(unzoomedRect) inView:FlippedView()]; #ifdef BUILDING_ON_TIGER if ([search showsFirstResponder]) wkDrawTextFieldCellFocusRing(search, NSRect(unzoomedRect)); @@ -1600,13 +1290,14 @@ bool RenderThemeChromiumMac::paintSearchField(RenderObject* o, const RenderObjec return false; } -void RenderThemeChromiumMac::setSearchCellState(RenderObject* o, const IntRect& r) +void RenderThemeChromiumMac::setSearchCellState(RenderObject* o, const IntRect&) { NSSearchFieldCell* search = this->search(); [search setControlSize:controlSizeForFont(o->style())]; // Update the various states we respond to. + updateActiveState(search, o); updateEnabledState(search, o); updateFocusedState(search, o); } @@ -1622,12 +1313,12 @@ void RenderThemeChromiumMac::setSearchFieldSize(RenderStyle* style) const // If the width and height are both specified, then we have nothing to do. if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) return; - + // Use the font size to determine the intrinsic width of the control. setSizeFromFont(style, searchFieldSizes()); } -void RenderThemeChromiumMac::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +void RenderThemeChromiumMac::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element*) const { // Override border. style->resetBorder(); @@ -1639,19 +1330,19 @@ void RenderThemeChromiumMac::adjustSearchFieldStyle(CSSStyleSelector* selector, style->setBorderBottomWidth(borderWidth); style->setBorderBottomStyle(INSET); style->setBorderTopWidth(borderWidth); - style->setBorderTopStyle(INSET); - + style->setBorderTopStyle(INSET); + // Override height. style->setHeight(Length(Auto)); setSearchFieldSize(style); - + // Override padding size to match AppKit text positioning. const int padding = 1 * style->effectiveZoom(); style->setPaddingLeft(Length(padding, Fixed)); style->setPaddingRight(Length(padding, Fixed)); style->setPaddingTop(Length(padding, Fixed)); style->setPaddingBottom(Length(padding, Fixed)); - + NSControlSize controlSize = controlSizeForFont(style); setFontFromControlSize(selector, style, controlSize); @@ -1660,22 +1351,25 @@ void RenderThemeChromiumMac::adjustSearchFieldStyle(CSSStyleSelector* selector, bool RenderThemeChromiumMac::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - LocalCurrentGraphicsContext localContext(paintInfo.context); - Node* input = o->node()->shadowAncestorNode(); + if (!input->renderer()->isBox()) + return false; + setSearchCellState(input->renderer(), r); NSSearchFieldCell* search = this->search(); + updateActiveState([search cancelButtonCell], o); updatePressedState([search cancelButtonCell], o); paintInfo.context->save(); float zoomLevel = o->style()->effectiveZoom(); - NSRect bounds = [search cancelButtonRectForBounds:NSRect(IntRectToNSRect(input->renderer()->absoluteBoundingBoxRect()))]; - - IntRect unzoomedRect(NSRectToIntRect(bounds)); + FloatRect localBounds = [search cancelButtonRectForBounds:NSRect(input->renderBox()->borderBoxRect())]; + localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); + + FloatRect unzoomedRect(localBounds); if (zoomLevel != 1.0f) { unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); @@ -1684,7 +1378,7 @@ bool RenderThemeChromiumMac::paintSearchFieldCancelButton(RenderObject* o, const paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); } - [[search cancelButtonCell] drawWithFrame:IntRectToNSRect(unzoomedRect) inView:nil]; + [[search cancelButtonCell] drawWithFrame:unzoomedRect inView:FlippedView()]; [[search cancelButtonCell] setControlView:nil]; paintInfo.context->restore(); @@ -1697,7 +1391,7 @@ const IntSize* RenderThemeChromiumMac::cancelButtonSizes() const return sizes; } -void RenderThemeChromiumMac::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +void RenderThemeChromiumMac::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { IntSize size = sizeForSystemFont(style, cancelButtonSizes()); style->setWidth(Length(size.width(), Fixed)); @@ -1711,8 +1405,8 @@ const IntSize* RenderThemeChromiumMac::resultsButtonSizes() const return sizes; } -static const int emptyResultsOffset = 9; -void RenderThemeChromiumMac::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +const int emptyResultsOffset = 9; +void RenderThemeChromiumMac::adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { IntSize size = sizeForSystemFont(style, resultsButtonSizes()); style->setWidth(Length(size.width() - emptyResultsOffset, Fixed)); @@ -1720,12 +1414,12 @@ void RenderThemeChromiumMac::adjustSearchFieldDecorationStyle(CSSStyleSelector* style->setBoxShadow(0); } -bool RenderThemeChromiumMac::paintSearchFieldDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +bool RenderThemeChromiumMac::paintSearchFieldDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return false; } -void RenderThemeChromiumMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +void RenderThemeChromiumMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { IntSize size = sizeForSystemFont(style, resultsButtonSizes()); style->setWidth(Length(size.width(), Fixed)); @@ -1735,24 +1429,29 @@ void RenderThemeChromiumMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSel bool RenderThemeChromiumMac::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - LocalCurrentGraphicsContext localContext(paintInfo.context); - Node* input = o->node()->shadowAncestorNode(); + if (!input->renderer()->isBox()) + return false; + setSearchCellState(input->renderer(), r); NSSearchFieldCell* search = this->search(); + updateActiveState([search searchButtonCell], o); + if ([search searchMenuTemplate] != nil) [search setSearchMenuTemplate:nil]; - NSRect bounds = [search searchButtonRectForBounds:NSRect(IntRectToNSRect(input->renderer()->absoluteBoundingBoxRect()))]; - [[search searchButtonCell] drawWithFrame:bounds inView:nil]; + FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->borderBoxRect())]; + localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); + + [[search searchButtonCell] drawWithFrame:localBounds inView:FlippedView()]; [[search searchButtonCell] setControlView:nil]; return false; } -static const int resultsArrowWidth = 5; -void RenderThemeChromiumMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +const int resultsArrowWidth = 5; +void RenderThemeChromiumMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { IntSize size = sizeForSystemFont(style, resultsButtonSizes()); style->setWidth(Length(size.width() + resultsArrowWidth, Fixed)); @@ -1762,13 +1461,16 @@ void RenderThemeChromiumMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelecto bool RenderThemeChromiumMac::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - LocalCurrentGraphicsContext localContext(paintInfo.context); - Node* input = o->node()->shadowAncestorNode(); + if (!input->renderer()->isBox()) + return false; + setSearchCellState(input->renderer(), r); NSSearchFieldCell* search = this->search(); + updateActiveState([search searchButtonCell], o); + if (![search searchMenuTemplate]) [search setSearchMenuTemplate:searchMenuTemplate()]; @@ -1776,9 +1478,10 @@ bool RenderThemeChromiumMac::paintSearchFieldResultsButton(RenderObject* o, cons float zoomLevel = o->style()->effectiveZoom(); - NSRect bounds = [search searchButtonRectForBounds:NSRect(IntRectToNSRect(input->renderer()->absoluteBoundingBoxRect()))]; - - IntRect unzoomedRect(NSRectToIntRect(bounds)); + FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->borderBoxRect())]; + localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); + + IntRect unzoomedRect(localBounds); if (zoomLevel != 1.0f) { unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); @@ -1787,188 +1490,78 @@ bool RenderThemeChromiumMac::paintSearchFieldResultsButton(RenderObject* o, cons paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); } - [[search searchButtonCell] drawWithFrame:IntRectToNSRect(unzoomedRect) inView:nil]; + [[search searchButtonCell] drawWithFrame:unzoomedRect inView:FlippedView()]; [[search searchButtonCell] setControlView:nil]; - + paintInfo.context->restore(); return false; } -#if ENABLE(VIDEO) -// FIXME: This enum is lifted from RenderThemeMac.mm We need to decide which theme to use for the default controls, or decide to avoid wkDrawMediaUIPart and render our own. -typedef enum { - MediaControllerThemeClassic = 1, - MediaControllerThemeQT = 2 -} MediaControllerThemeStyle; - -enum WKMediaControllerThemeState { - MediaUIPartDisabledFlag = 1 << 0, - MediaUIPartPressedFlag = 1 << 1, - MediaUIPartDrawEndCapsFlag = 1 << 3, -}; -#endif +const int sliderThumbWidth = 15; +const int sliderThumbHeight = 15; -bool RenderThemeChromiumMac::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +void RenderThemeChromiumMac::adjustSliderThumbSize(RenderObject* o) const { -#if ENABLE(VIDEO) - Node* node = o->node(); - if (!node) - return false; + float zoomLevel = o->style()->effectiveZoom(); + if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == SliderThumbVerticalPart) { + o->style()->setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed)); + o->style()->setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed)); + } - LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawMediaUIPart(MediaFullscreenButton, MediaControllerThemeClassic, paintInfo.context->platformContext(), r, - node->active() ? MediaUIPartPressedFlag : 0); +#if ENABLE(VIDEO) + RenderMediaControlsChromium::adjustMediaSliderThumbSize(o); #endif - return false; } -bool RenderThemeChromiumMac::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) -{ #if ENABLE(VIDEO) - Node* node = o->node(); - Node* mediaNode = node ? node->shadowAncestorNode() : 0; - if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) - return false; - - HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode); - if (!mediaElement) - return false; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawMediaUIPart(mediaElement->muted() ? MediaUnMuteButton : MediaMuteButton, MediaControllerThemeClassic, paintInfo.context->platformContext(), r, - node->active() ? MediaUIPartPressedFlag : 0); -#endif - return false; +bool RenderThemeChromiumMac::shouldRenderMediaControlPart(ControlPart part, Element* e) +{ + return RenderMediaControlsChromium::shouldRenderMediaControlPart(part, e); } -bool RenderThemeChromiumMac::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +bool RenderThemeChromiumMac::paintMediaPlayButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { -#if ENABLE(VIDEO) - Node* node = o->node(); - Node* mediaNode = node ? node->shadowAncestorNode() : 0; - if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) - return false; - - HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode); - if (!mediaElement) - return false; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawMediaUIPart(mediaElement->canPlay() ? MediaPlayButton : MediaPauseButton, MediaControllerThemeClassic, paintInfo.context->platformContext(), r, - node->active() ? MediaUIPartPressedFlag : 0); -#endif - return false; + return RenderMediaControlsChromium::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect); } -bool RenderThemeChromiumMac::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +bool RenderThemeChromiumMac::paintMediaMuteButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { -#if ENABLE(VIDEO) - Node* node = o->node(); - if (!node) - return false; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawMediaUIPart(MediaSeekBackButton, MediaControllerThemeClassic, paintInfo.context->platformContext(), r, - node->active() ? MediaUIPartPressedFlag : 0); -#endif - return false; + return RenderMediaControlsChromium::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect); } -bool RenderThemeChromiumMac::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +bool RenderThemeChromiumMac::paintMediaSliderTrack(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { -#if ENABLE(VIDEO) - Node* node = o->node(); - if (!node) - return false; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawMediaUIPart(MediaSeekForwardButton, MediaControllerThemeClassic, paintInfo.context->platformContext(), r, - node->active() ? MediaUIPartPressedFlag : 0); -#endif - return false; + return RenderMediaControlsChromium::paintMediaControlsPart(MediaSlider, object, paintInfo, rect); } -bool RenderThemeChromiumMac::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +bool RenderThemeChromiumMac::paintMediaVolumeSliderTrack(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { -#if ENABLE(VIDEO) - Node* node = o->node(); - Node* mediaNode = node ? node->shadowAncestorNode() : 0; - if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) - return false; - - HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode); - if (!mediaElement) - return false; - - float timeLoaded = 0; - float currentTime = 0; - float duration = 0; - if (MediaPlayer* player = mediaElement->player()) { - duration = player->duration(); - timeLoaded = player->maxTimeBuffered(); - currentTime = player->currentTime(); - } - - bool shouldDrawEndCaps = !toRenderMedia(mediaElement->renderer())->shouldShowTimeDisplayControls(); - wkDrawMediaSliderTrack(MediaControllerThemeClassic, paintInfo.context->platformContext(), r, timeLoaded, currentTime, duration, shouldDrawEndCaps ? MediaUIPartDrawEndCapsFlag : 0); -#endif - return false; + return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect); } -bool RenderThemeChromiumMac::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +bool RenderThemeChromiumMac::paintMediaSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { -#if ENABLE(VIDEO) - Node* node = o->node(); - if (!node) - return false; - - LocalCurrentGraphicsContext localContext(paintInfo.context); - wkDrawMediaUIPart(MediaSliderThumb, MediaControllerThemeClassic, paintInfo.context->platformContext(), r, - node->active() ? MediaUIPartPressedFlag : 0); -#endif - return false; + return RenderMediaControlsChromium::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect); } -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -NSButtonCell* RenderThemeChromiumMac::checkbox() const +bool RenderThemeChromiumMac::paintMediaVolumeSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { - if (!m_checkbox) { - m_checkbox.adoptNS([[NSButtonCell alloc] init]); - [m_checkbox.get() setButtonType:NSSwitchButton]; - [m_checkbox.get() setTitle:nil]; - [m_checkbox.get() setAllowsMixedState:YES]; - [m_checkbox.get() setFocusRingType:NSFocusRingTypeExterior]; - } - - return m_checkbox.get(); + return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect); } -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -NSButtonCell* RenderThemeChromiumMac::radio() const +bool RenderThemeChromiumMac::paintMediaControlsBackground(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { - if (!m_radio) { - m_radio.adoptNS([[NSButtonCell alloc] init]); - [m_radio.get() setButtonType:NSRadioButton]; - [m_radio.get() setTitle:nil]; - [m_radio.get() setFocusRingType:NSFocusRingTypeExterior]; - } - - return m_radio.get(); + return RenderMediaControlsChromium::paintMediaControlsPart(MediaTimelineContainer, object, paintInfo, rect); } -// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731. -NSButtonCell* RenderThemeChromiumMac::button() const +String RenderThemeChromiumMac::extraMediaControlsStyleSheet() { - if (!m_button) { - m_button.adoptNS([[NSButtonCell alloc] init]); - [m_button.get() setTitle:nil]; - [m_button.get() setButtonType:NSMomentaryPushInButton]; - } - - return m_button.get(); + return String(mediaControlsChromiumUserAgentStyleSheet, sizeof(mediaControlsChromiumUserAgentStyleSheet)); } +#endif + NSPopUpButtonCell* RenderThemeChromiumMac::popupButton() const { if (!m_popupButton) { @@ -1976,7 +1569,7 @@ NSPopUpButtonCell* RenderThemeChromiumMac::popupButton() const [m_popupButton.get() setUsesItemFromMenu:NO]; [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior]; } - + return m_popupButton.get(); } @@ -2010,7 +1603,7 @@ NSSliderCell* RenderThemeChromiumMac::sliderThumbHorizontal() const [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize]; [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior]; } - + return m_sliderThumbHorizontal.get(); } @@ -2023,7 +1616,7 @@ NSSliderCell* RenderThemeChromiumMac::sliderThumbVertical() const [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize]; [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior]; } - + return m_sliderThumbVertical.get(); } diff --git a/WebCore/rendering/RenderThemeChromiumSkia.cpp b/WebCore/rendering/RenderThemeChromiumSkia.cpp index 79804ac..7d3bcec 100644 --- a/WebCore/rendering/RenderThemeChromiumSkia.cpp +++ b/WebCore/rendering/RenderThemeChromiumSkia.cpp @@ -33,8 +33,11 @@ #include "MediaControlElements.h" #include "PlatformContextSkia.h" #include "RenderBox.h" +#include "RenderMediaControlsChromium.h" #include "RenderObject.h" +#include "RenderSlider.h" #include "ScrollbarTheme.h" +#include "TimeRanges.h" #include "TransformationMatrix.h" #include "UserAgentStyleSheets.h" @@ -70,19 +73,29 @@ static void setSizeIfAuto(RenderStyle* style, const IntSize& size) style->setHeight(Length(size.height(), Fixed)); } -#if ENABLE(VIDEO) -// Attempt to retrieve a HTMLMediaElement from a Node. Returns NULL if one cannot be found. -static HTMLMediaElement* mediaElementParent(Node* node) +static void drawVertLine(SkCanvas* canvas, int x, int y1, int y2, const SkPaint& paint) { - if (!node) - return 0; - Node* mediaNode = node->shadowAncestorNode(); - if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag))) - return 0; + SkIRect skrect; + skrect.set(x, y1, x + 1, y2 + 1); + canvas->drawIRect(skrect, paint); +} - return static_cast<HTMLMediaElement*>(mediaNode); +static void drawHorizLine(SkCanvas* canvas, int x1, int x2, int y, const SkPaint& paint) +{ + SkIRect skrect; + skrect.set(x1, y, x2 + 1, y + 1); + canvas->drawIRect(skrect, paint); +} + +static void drawBox(SkCanvas* canvas, const IntRect& rect, const SkPaint& paint) +{ + const int right = rect.x() + rect.width() - 1; + const int bottom = rect.y() + rect.height() - 1; + drawHorizLine(canvas, rect.x(), right, rect.y(), paint); + drawVertLine(canvas, right, rect.y(), bottom, paint); + drawHorizLine(canvas, rect.x(), right, bottom, paint); + drawVertLine(canvas, rect.x(), rect.y(), bottom, paint); } -#endif // We aim to match IE here. // -IE uses a font based on the encoding as the default font for form controls. @@ -204,6 +217,22 @@ int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle* style) const return 0; } +// These are the default dimensions of radio buttons and checkboxes. +static const int widgetStandardWidth = 13; +static const int widgetStandardHeight = 13; + +// Return a rectangle that has the same center point as |original|, but with a +// size capped at |width| by |height|. +IntRect center(const IntRect& original, int width, int height) +{ + width = std::min(original.width(), width); + height = std::min(original.height(), height); + int x = original.x() + (original.width() - width) / 2; + int y = original.y() + (original.height() - height) / 2; + + return IntRect(x, y, width, height); +} + bool RenderThemeChromiumSkia::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) { static Image* const checkedImage = Image::loadPlatformResource("linuxCheckboxOn").releaseRef(); @@ -218,7 +247,7 @@ bool RenderThemeChromiumSkia::paintCheckbox(RenderObject* o, const RenderObject: else image = this->isChecked(o) ? disabledCheckedImage : disabledUncheckedImage; - i.context->drawImage(image, rect); + i.context->drawImage(image, o->style()->colorSpace(), center(rect, widgetStandardHeight, widgetStandardWidth)); return false; } @@ -233,7 +262,7 @@ void RenderThemeChromiumSkia::setCheckboxSize(RenderStyle* style) const // querying the theme gives you a larger size that accounts for the higher // DPI. Until our entire engine honors a DPI setting other than 96, we // can't rely on the theme's metrics. - const IntSize size(13, 13); + const IntSize size(widgetStandardHeight, widgetStandardWidth); setSizeIfAuto(style, size); } @@ -250,7 +279,7 @@ bool RenderThemeChromiumSkia::paintRadio(RenderObject* o, const RenderObject::Pa else image = this->isChecked(o) ? disabledCheckedImage : disabledUncheckedImage; - i.context->drawImage(image, rect); + i.context->drawImage(image, o->style()->colorSpace(), center(rect, widgetStandardHeight, widgetStandardWidth)); return false; } @@ -302,7 +331,7 @@ static void paintButtonLike(RenderTheme* theme, RenderObject* o, const RenderObj 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); + paint.setColor(SK_ColorBLACK); SkPoint p[2]; const int lightEnd = theme->isPressed(o) ? 1 : 0; const int darkEnd = !lightEnd; @@ -335,6 +364,15 @@ bool RenderThemeChromiumSkia::paintButton(RenderObject* o, const RenderObject::P return false; } +void RenderThemeChromiumSkia::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + if (style->appearance() == PushButtonPart) { + // Ignore line-height. + style->setLineHeight(RenderStyle::initialLineHeight()); + } +} + + bool RenderThemeChromiumSkia::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) { return true; @@ -345,6 +383,12 @@ bool RenderThemeChromiumSkia::paintTextArea(RenderObject* o, const RenderObject: return paintTextField(o, i, r); } +void RenderThemeChromiumSkia::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + // Ignore line-height. + style->setLineHeight(RenderStyle::initialLineHeight()); +} + bool RenderThemeChromiumSkia::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { return paintTextField(o, i, r); @@ -380,7 +424,7 @@ bool RenderThemeChromiumSkia::paintSearchFieldCancelButton(RenderObject* o, cons static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef(); static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef(); - i.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, bounds); + i.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, o->style()->colorSpace(), bounds); return false; } @@ -420,7 +464,7 @@ bool RenderThemeChromiumSkia::paintSearchFieldResultsDecoration(RenderObject* o, bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef(); - i.context->drawImage(magnifierImage, bounds); + i.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds); return false; } @@ -456,46 +500,14 @@ bool RenderThemeChromiumSkia::paintSearchFieldResultsButton(RenderObject* o, con bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef(); - i.context->drawImage(magnifierImage, bounds); + i.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds); return false; } -bool RenderThemeChromiumSkia::paintMediaButtonInternal(GraphicsContext* context, const IntRect& rect, Image* image) -{ - // Create a destination rectangle for the image that is centered in the drawing rectangle, rounded left, and down. - IntRect imageRect = image->rect(); - imageRect.setY(rect.y() + (rect.height() - image->height() + 1) / 2); - imageRect.setX(rect.x() + (rect.width() - image->width() + 1) / 2); - - context->drawImage(image, imageRect); - return true; -} - bool RenderThemeChromiumSkia::paintMediaControlsBackground(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { #if ENABLE(VIDEO) - HTMLMediaElement* mediaElement = mediaElementParent(object->node()); - if (!mediaElement) - return false; - - if (!rect.isEmpty()) - { - SkCanvas* canvas = paintInfo.context->platformContext()->canvas(); - SkPaint paint; - - // Draws the left border, it is always 1px wide. - paint.setColor(object->style()->borderLeftColor().rgb()); - canvas->drawLine(rect.x() + 1, rect.y(), - rect.x() + 1, rect.y() + rect.height(), - paint); - - // Draws the right border, it is always 1px wide. - paint.setColor(object->style()->borderRightColor().rgb()); - canvas->drawLine(rect.x() + rect.width() - 1, rect.y(), - rect.x() + rect.width() - 1, rect.y() + rect.height(), - paint); - } - return true; + return RenderMediaControlsChromium::paintMediaControlsPart(MediaTimelineContainer, object, paintInfo, rect); #else UNUSED_PARAM(object); UNUSED_PARAM(paintInfo); @@ -507,62 +519,7 @@ bool RenderThemeChromiumSkia::paintMediaControlsBackground(RenderObject* object, bool RenderThemeChromiumSkia::paintMediaSliderTrack(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { #if ENABLE(VIDEO) - HTMLMediaElement* mediaElement = mediaElementParent(object->node()); - if (!mediaElement) - return false; - - SkCanvas* canvas = paintInfo.context->platformContext()->canvas(); - SkRect backgroundRect; - backgroundRect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); - - SkPaint paint; - paint.setAntiAlias(true); - - // Draw the border of the time bar. The border only has one single color, - // width and radius. So use the property of the left border. - SkColor borderColor = object->style()->borderLeftColor().rgb(); - int borderWidth = object->style()->borderLeftWidth(); - IntSize borderRadius = object->style()->borderTopLeftRadius(); - paint.setStyle(SkPaint::kStroke_Style); - paint.setStrokeWidth(borderWidth); - paint.setColor(borderColor); - canvas->drawRoundRect(backgroundRect, borderRadius.width(), borderRadius.height(), paint); - - // Draw the background of the time bar. - SkColor backgroundColor = object->style()->backgroundColor().rgb(); - paint.setStyle(SkPaint::kFill_Style); - paint.setColor(backgroundColor); - canvas->drawRoundRect(backgroundRect, borderRadius.width(), borderRadius.height(), paint); - - if (backgroundRect.width() >= 3 && backgroundRect.height() >= 3) - { - // Draw the buffered ranges. - // FIXME: Draw multiple ranges if there are multiple buffered ranges. - SkRect bufferedRect; - bufferedRect.set(backgroundRect.fLeft + 2, backgroundRect.fTop + 2, - backgroundRect.fRight - 1, backgroundRect.fBottom - 1); - int width = static_cast<int>(bufferedRect.width() * mediaElement->percentLoaded()); - bufferedRect.fRight = bufferedRect.fLeft + width; - - SkPoint points[2] = { { 0, bufferedRect.fTop }, { 0, bufferedRect.fBottom } }; - SkColor startColor = object->style()->color().rgb(); - SkColor endColor = SkColorSetRGB(SkColorGetR(startColor) / 2, - SkColorGetG(startColor) / 2, - SkColorGetB(startColor) / 2); - SkColor colors[2] = { startColor, endColor }; - SkShader* gradient = SkGradientShader::CreateLinear(points, colors, 0, - sizeof(points) / sizeof(points[0]), - SkShader::kMirror_TileMode, 0); - - paint.reset(); - paint.setShader(gradient); - paint.setAntiAlias(true); - // Check for round rect with zero width or height, otherwise Skia will assert - if (bufferedRect.width() > 0 && bufferedRect.height() > 0) - canvas->drawRoundRect(bufferedRect, borderRadius.width(), borderRadius.height(), paint); - gradient->unref(); - } - return true; + return RenderMediaControlsChromium::paintMediaControlsPart(MediaSlider, object, paintInfo, rect); #else UNUSED_PARAM(object); UNUSED_PARAM(paintInfo); @@ -571,14 +528,22 @@ bool RenderThemeChromiumSkia::paintMediaSliderTrack(RenderObject* object, const #endif } -void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderObject* object) const { +bool RenderThemeChromiumSkia::paintMediaVolumeSliderTrack(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ #if ENABLE(VIDEO) - if (object->style()->appearance() == MediaSliderThumbPart) { - static Image* mediaSliderThumb = Image::loadPlatformResource("mediaSliderThumb").releaseRef(); + return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect); +#else + UNUSED_PARAM(object); + UNUSED_PARAM(paintInfo); + UNUSED_PARAM(rect); + return false; +#endif +} - object->style()->setWidth(Length(mediaSliderThumb->width(), Fixed)); - object->style()->setHeight(Length(mediaSliderThumb->height(), Fixed)); - } +void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderObject* object) const +{ +#if ENABLE(VIDEO) + RenderMediaControlsChromium::adjustMediaSliderThumbSize(object); #else UNUSED_PARAM(object); #endif @@ -587,12 +552,19 @@ void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderObject* object) const bool RenderThemeChromiumSkia::paintMediaSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { #if ENABLE(VIDEO) - if (!object->parent()->isSlider()) - return false; - - static Image* mediaSliderThumb = Image::loadPlatformResource("mediaSliderThumb").releaseRef(); + return RenderMediaControlsChromium::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect); +#else + UNUSED_PARAM(object); + UNUSED_PARAM(paintInfo); + UNUSED_PARAM(rect); + return false; +#endif +} - return paintMediaButtonInternal(paintInfo.context, rect, mediaSliderThumb); +bool RenderThemeChromiumSkia::paintMediaVolumeSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ +#if ENABLE(VIDEO) + return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect); #else UNUSED_PARAM(object); UNUSED_PARAM(paintInfo); @@ -604,14 +576,7 @@ bool RenderThemeChromiumSkia::paintMediaSliderThumb(RenderObject* object, const bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { #if ENABLE(VIDEO) - HTMLMediaElement* mediaElement = mediaElementParent(object->node()); - if (!mediaElement) - return false; - - static Image* mediaPlay = Image::loadPlatformResource("mediaPlay").releaseRef(); - static Image* mediaPause = Image::loadPlatformResource("mediaPause").releaseRef(); - - return paintMediaButtonInternal(paintInfo.context, rect, mediaElement->paused() ? mediaPlay : mediaPause); + return RenderMediaControlsChromium::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect); #else UNUSED_PARAM(object); UNUSED_PARAM(paintInfo); @@ -623,14 +588,7 @@ bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject* object, const R bool RenderThemeChromiumSkia::paintMediaMuteButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { #if ENABLE(VIDEO) - HTMLMediaElement* mediaElement = mediaElementParent(object->node()); - if (!mediaElement) - return false; - - static Image* soundFull = Image::loadPlatformResource("mediaSoundFull").releaseRef(); - static Image* soundNone = Image::loadPlatformResource("mediaSoundNone").releaseRef(); - - return paintMediaButtonInternal(paintInfo.context, rect, mediaElement->muted() ? soundNone: soundFull); + return RenderMediaControlsChromium::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect); #else UNUSED_PARAM(object); UNUSED_PARAM(paintInfo); @@ -654,12 +612,13 @@ bool RenderThemeChromiumSkia::paintMenuList(RenderObject* o, const RenderObject: paintButtonLike(this, o, i, rect); SkPaint paint; - paint.setARGB(0xff, 0, 0, 0); + paint.setColor(SK_ColorBLACK); paint.setAntiAlias(true); paint.setStyle(SkPaint::kFill_Style); + int arrowXPosition = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13; SkPath path; - path.moveTo(right - 13, middle - 3); + path.moveTo(arrowXPosition, middle - 3); path.rLineTo(6, 0); path.rLineTo(-3, 6); path.close(); @@ -679,6 +638,69 @@ bool RenderThemeChromiumSkia::paintMenuListButton(RenderObject* o, const RenderO return paintMenuList(o, i, rect); } +bool RenderThemeChromiumSkia::paintSliderTrack(RenderObject*, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + // Just paint a grey box for now (matches the color of a scrollbar background. + SkCanvas* const canvas = i.context->platformContext()->canvas(); + int verticalCenter = rect.y() + rect.height() / 2; + int top = std::max(rect.y(), verticalCenter - 2); + int bottom = std::min(rect.y() + rect.height(), verticalCenter + 2); + + SkPaint paint; + const SkColor grey = SkColorSetARGB(0xff, 0xe3, 0xdd, 0xd8); + paint.setColor(grey); + + SkRect skrect; + skrect.set(rect.x(), top, rect.x() + rect.width(), bottom); + canvas->drawRect(skrect, paint); + + return false; +} + +bool RenderThemeChromiumSkia::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) +{ + // Make a thumb similar to the scrollbar thumb. + const bool hovered = isHovered(o) || toRenderSlider(o->parent())->inDragMode(); + const int midx = rect.x() + rect.width() / 2; + const int midy = rect.y() + rect.height() / 2; + const bool vertical = (o->style()->appearance() == SliderThumbVerticalPart); + SkCanvas* const canvas = i.context->platformContext()->canvas(); + + const SkColor thumbLightGrey = SkColorSetARGB(0xff, 0xf4, 0xf2, 0xef); + const SkColor thumbDarkGrey = SkColorSetARGB(0xff, 0xea, 0xe5, 0xe0); + SkPaint paint; + paint.setColor(hovered ? SK_ColorWHITE : thumbLightGrey); + + SkIRect skrect; + if (vertical) + skrect.set(rect.x(), rect.y(), midx + 1, rect.bottom()); + else + skrect.set(rect.x(), rect.y(), rect.right(), midy + 1); + + canvas->drawIRect(skrect, paint); + + paint.setColor(hovered ? thumbLightGrey : thumbDarkGrey); + + if (vertical) + skrect.set(midx + 1, rect.y(), rect.right(), rect.bottom()); + else + skrect.set(rect.x(), midy + 1, rect.right(), rect.bottom()); + + canvas->drawIRect(skrect, paint); + + const SkColor borderDarkGrey = SkColorSetARGB(0xff, 0x9d, 0x96, 0x8e); + paint.setColor(borderDarkGrey); + drawBox(canvas, rect, paint); + + if (rect.height() > 10 && rect.width() > 10) { + drawHorizLine(canvas, midx - 2, midx + 2, midy, paint); + drawHorizLine(canvas, midx - 2, midx + 2, midy - 3, paint); + drawHorizLine(canvas, midx - 2, midx + 2, midy + 3, paint); + } + + return false; +} + int RenderThemeChromiumSkia::popupInternalPaddingLeft(RenderStyle* style) const { return menuListInternalPadding(style, LeftPadding); @@ -719,6 +741,13 @@ int RenderThemeChromiumSkia::buttonInternalPaddingBottom() const return 1; } +#if ENABLE(VIDEO) +bool RenderThemeChromiumSkia::shouldRenderMediaControlPart(ControlPart part, Element* e) +{ + return RenderMediaControlsChromium::shouldRenderMediaControlPart(part, e); +} +#endif + // static void RenderThemeChromiumSkia::setDefaultFontSize(int fontSize) { diff --git a/WebCore/rendering/RenderThemeChromiumSkia.h b/WebCore/rendering/RenderThemeChromiumSkia.h index 7544c22..18fa859 100644 --- a/WebCore/rendering/RenderThemeChromiumSkia.h +++ b/WebCore/rendering/RenderThemeChromiumSkia.h @@ -71,11 +71,13 @@ namespace WebCore { virtual void setRadioSize(RenderStyle*) const; virtual bool paintButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; virtual bool paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintTextArea(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; @@ -91,8 +93,10 @@ namespace WebCore { virtual bool paintMediaControlsBackground(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaVolumeSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual void adjustSliderThumbSize(RenderObject*) const; virtual bool paintMediaSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaVolumeSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaPlayButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaMuteButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); @@ -110,6 +114,9 @@ namespace WebCore { virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; virtual bool paintMenuListButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + // These methods define the padding for the MenuList's inner block. virtual int popupInternalPaddingLeft(RenderStyle*) const; virtual int popupInternalPaddingRight(RenderStyle*) const; @@ -121,6 +128,11 @@ namespace WebCore { virtual int buttonInternalPaddingTop() const; virtual int buttonInternalPaddingBottom() const; +#if ENABLE(VIDEO) + // Media controls + virtual bool shouldRenderMediaControlPart(ControlPart, Element*); +#endif + // Provide a way to pass the default font size from the Settings object // to the render theme. FIXME: http://b/1129186 A cleaner way would be // to remove the default font size from this object and have callers diff --git a/WebCore/rendering/RenderThemeChromiumWin.cpp b/WebCore/rendering/RenderThemeChromiumWin.cpp index 35d1580..db31825 100644 --- a/WebCore/rendering/RenderThemeChromiumWin.cpp +++ b/WebCore/rendering/RenderThemeChromiumWin.cpp @@ -86,7 +86,7 @@ private: return transformMode == KeepTransform ? NoLayer : OpaqueCompositeLayer; } - static TransformMode getTransformMode(const TransformationMatrix& matrix) + static TransformMode getTransformMode(const AffineTransform& matrix) { if (matrix.b() != 0 || matrix.c() != 0) // Skew. return Untransform; @@ -311,6 +311,52 @@ void RenderThemeChromiumWin::systemFont(int propId, FontDescription& fontDescrip fontDescription = *cachedDesc; } +// Map a CSSValue* system color to an index understood by GetSysColor(). +static int cssValueIdToSysColorIndex(int cssValueId) +{ + switch (cssValueId) { + case CSSValueActiveborder: return COLOR_ACTIVEBORDER; + case CSSValueActivecaption: return COLOR_ACTIVECAPTION; + case CSSValueAppworkspace: return COLOR_APPWORKSPACE; + case CSSValueBackground: return COLOR_BACKGROUND; + case CSSValueButtonface: return COLOR_BTNFACE; + case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT; + case CSSValueButtonshadow: return COLOR_BTNSHADOW; + case CSSValueButtontext: return COLOR_BTNTEXT; + case CSSValueCaptiontext: return COLOR_CAPTIONTEXT; + case CSSValueGraytext: return COLOR_GRAYTEXT; + case CSSValueHighlight: return COLOR_HIGHLIGHT; + case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT; + case CSSValueInactiveborder: return COLOR_INACTIVEBORDER; + case CSSValueInactivecaption: return COLOR_INACTIVECAPTION; + case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT; + case CSSValueInfobackground: return COLOR_INFOBK; + case CSSValueInfotext: return COLOR_INFOTEXT; + case CSSValueMenu: return COLOR_MENU; + case CSSValueMenutext: return COLOR_MENUTEXT; + case CSSValueScrollbar: return COLOR_SCROLLBAR; + case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW; + case CSSValueThreedface: return COLOR_3DFACE; + case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT; + case CSSValueThreedlightshadow: return COLOR_3DLIGHT; + case CSSValueThreedshadow: return COLOR_3DSHADOW; + case CSSValueWindow: return COLOR_WINDOW; + case CSSValueWindowframe: return COLOR_WINDOWFRAME; + case CSSValueWindowtext: return COLOR_WINDOWTEXT; + default: return -1; // Unsupported CSSValue + } +} + +Color RenderThemeChromiumWin::systemColor(int cssValueId) const +{ + int sysColorIndex = cssValueIdToSysColorIndex(cssValueId); + if (ChromiumBridge::layoutTestMode() || (sysColorIndex == -1)) + return RenderTheme::systemColor(cssValueId); + + COLORREF color = GetSysColor(sysColorIndex); + return Color(GetRValue(color), GetGValue(color), GetBValue(color)); +} + void RenderThemeChromiumWin::adjustSliderThumbSize(RenderObject* o) const { // These sizes match what WinXP draws for various menus. @@ -475,14 +521,36 @@ unsigned RenderThemeChromiumWin::determineSliderThumbState(RenderObject* o) unsigned RenderThemeChromiumWin::determineClassicState(RenderObject* o) { unsigned result = 0; - if (!isEnabled(o)) - result = DFCS_INACTIVE; - else if (isPressed(o)) // Active supersedes hover - result = DFCS_PUSHED; - else if (isHovered(o)) - result = DFCS_HOT; - if (isChecked(o)) - result |= DFCS_CHECKED; + + ControlPart part = o->style()->appearance(); + + // Sliders are always in the normal state. + if (part == SliderHorizontalPart || part == SliderVerticalPart) + return result; + + // So are readonly text fields. + if (isReadOnlyControl(o) && (part == TextFieldPart || part == TextAreaPart || part == SearchFieldPart)) + return result; + + if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) { + if (!isEnabled(o->parent())) + result = DFCS_INACTIVE; + else if (toRenderSlider(o->parent())->inDragMode()) // Active supersedes hover + result = DFCS_PUSHED; + else if (isHovered(o)) + result = DFCS_HOT; + } else { + if (!isEnabled(o)) + result = DFCS_INACTIVE; + else if (isPressed(o)) // Active supersedes hover + result = DFCS_PUSHED; + else if (supportsFocus(part) && isFocused(o)) // So does focused + result = 0; + else if (isHovered(o)) + result = DFCS_HOT; + if (isChecked(o)) + result |= DFCS_CHECKED; + } return result; } @@ -524,6 +592,7 @@ ThemeData RenderThemeChromiumWin::getThemeData(RenderObject* o) break; case ListboxPart: case MenulistPart: + case MenulistButtonPart: case SearchFieldPart: case TextFieldPart: case TextAreaPart: @@ -542,21 +611,11 @@ bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o, 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. - // FIXME: Get rid of this if-check once we can properly clip rounded - // borders: http://b/1112604 and http://b/1108635 - // FIXME: make sure we do the right thing if css background-clip is set. - if (o->style()->hasBorderRadius()) - return false; - - const ThemeData& themeData = getThemeData(o); - // Fallback to white if the specified color object is invalid. + // (Note ChromiumBridge::paintTextField duplicates this check). Color backgroundColor(Color::white); - if (o->style()->backgroundColor().isValid()) { + if (o->style()->backgroundColor().isValid()) backgroundColor = o->style()->backgroundColor(); - } // If we have background-image, don't fill the content area to expose the // parent's background. Also, we shouldn't fill the content area if the @@ -565,17 +624,32 @@ bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o, // Note that we should paint the content area white if we have neither the // background color nor background image explicitly specified to keep the // appearance of select element consistent with other browsers. - bool fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha() != 0; - - WebCore::ThemePainter painter(i.context, r); - ChromiumBridge::paintTextField(painter.context(), - themeData.m_part, - themeData.m_state, - themeData.m_classicState, - painter.drawRect(), - backgroundColor, - fillContentArea, - drawEdges); + bool fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha(); + + if (o->style()->hasBorderRadius()) { + // If the style has rounded borders, setup the context to clip the + // background (themed or filled) appropriately. + // FIXME: make sure we do the right thing if css background-clip is set. + i.context->save(); + IntSize topLeft, topRight, bottomLeft, bottomRight; + o->style()->getBorderRadiiForRect(r, topLeft, topRight, bottomLeft, bottomRight); + i.context->addRoundedRectClip(r, topLeft, topRight, bottomLeft, bottomRight); + } + { + const ThemeData& themeData = getThemeData(o); + WebCore::ThemePainter painter(i.context, r); + ChromiumBridge::paintTextField(painter.context(), + themeData.m_part, + themeData.m_state, + themeData.m_classicState, + painter.drawRect(), + backgroundColor, + fillContentArea, + drawEdges); + // End of block commits the painter before restoring context. + } + if (o->style()->hasBorderRadius()) + i.context->restore(); return false; } diff --git a/WebCore/rendering/RenderThemeChromiumWin.h b/WebCore/rendering/RenderThemeChromiumWin.h index 5e98c9b..3b86980 100644 --- a/WebCore/rendering/RenderThemeChromiumWin.h +++ b/WebCore/rendering/RenderThemeChromiumWin.h @@ -59,6 +59,7 @@ namespace WebCore { // System fonts. virtual void systemFont(int propId, FontDescription&) const; + virtual Color systemColor(int cssValueId) const;
virtual void adjustSliderThumbSize(RenderObject*) const; diff --git a/WebCore/rendering/RenderThemeMac.h b/WebCore/rendering/RenderThemeMac.h index 85f141f..48c6c42 100644 --- a/WebCore/rendering/RenderThemeMac.h +++ b/WebCore/rendering/RenderThemeMac.h @@ -126,12 +126,15 @@ protected: virtual bool paintMediaSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaRewindButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaReturnToRealtimeButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaToggleClosedCaptionsButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaControlsBackground(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaCurrentTime(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaTimeRemaining(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); // Media controls virtual String extraMediaControlsStyleSheet(); + + virtual bool shouldRenderMediaControlPart(ControlPart, Element*); #endif private: diff --git a/WebCore/rendering/RenderThemeMac.mm b/WebCore/rendering/RenderThemeMac.mm index 6315f9f..ddb538b 100644 --- a/WebCore/rendering/RenderThemeMac.mm +++ b/WebCore/rendering/RenderThemeMac.mm @@ -38,6 +38,7 @@ #import "RenderSlider.h" #import "RenderView.h" #import "SharedBuffer.h" +#import "TimeRanges.h" #import "WebCoreSystemInterface.h" #import "UserAgentStyleSheets.h" #import <Carbon/Carbon.h> @@ -472,6 +473,7 @@ void RenderThemeMac::adjustRepaintRect(const RenderObject* o, IntRect& r) case RadioPart: case PushButtonPart: case SquareButtonPart: + case ListButtonPart: case DefaultButtonPart: case ButtonPart: return RenderTheme::adjustRepaintRect(o, r); @@ -824,11 +826,21 @@ static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outD void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { + if (r.isEmpty()) + return; + CGContextRef context = paintInfo.context->platformContext(); paintInfo.context->save(); - int radius = o->style()->borderTopLeftRadius().width(); + IntSize topLeftRadius; + IntSize topRightRadius; + IntSize bottomLeftRadius; + IntSize bottomRightRadius; + + o->style()->getBorderRadiiForRect(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + + int radius = topLeftRadius.width(); RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB()); @@ -851,33 +863,27 @@ void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const RenderO RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(), r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false)); paintInfo.context->save(); CGContextClipToRect(context, r); - paintInfo.context->addRoundedRectClip(r, - o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), - o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); + paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); CGContextDrawShading(context, mainShading.get()); paintInfo.context->restore(); paintInfo.context->save(); CGContextClipToRect(context, topGradient); - paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), - o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), - IntSize(), IntSize()); + paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), topLeftRadius, topRightRadius, IntSize(), IntSize()); CGContextDrawShading(context, topShading.get()); paintInfo.context->restore(); - paintInfo.context->save(); - CGContextClipToRect(context, bottomGradient); - paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), - IntSize(), IntSize(), - o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); - CGContextDrawShading(context, bottomShading.get()); - paintInfo.context->restore(); + if (!bottomGradient.isEmpty()) { + paintInfo.context->save(); + CGContextClipToRect(context, bottomGradient); + paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), IntSize(), IntSize(), bottomLeftRadius, bottomRightRadius); + CGContextDrawShading(context, bottomShading.get()); + paintInfo.context->restore(); + } paintInfo.context->save(); CGContextClipToRect(context, r); - paintInfo.context->addRoundedRectClip(r, - o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), - o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); + paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); CGContextDrawShading(context, leftShading.get()); CGContextDrawShading(context, rightShading.get()); paintInfo.context->restore(); @@ -887,8 +893,6 @@ void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const RenderO bool RenderThemeMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - paintInfo.context->save(); - IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(), r.y() + o->style()->borderTopWidth(), r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(), @@ -907,7 +911,9 @@ bool RenderThemeMac::paintMenuListButton(RenderObject* o, const RenderObject::Pa if (bounds.width() < arrowWidth + arrowPaddingLeft * o->style()->effectiveZoom()) return false; - paintInfo.context->setFillColor(o->style()->color()); + paintInfo.context->save(); + + paintInfo.context->setFillColor(o->style()->color(), o->style()->colorSpace()); paintInfo.context->setStrokeStyle(NoStroke); FloatPoint arrow1[3]; @@ -936,11 +942,11 @@ bool RenderThemeMac::paintMenuListButton(RenderObject* o, const RenderObject::Pa // Draw the separator to the left of the arrows paintInfo.context->setStrokeThickness(1.0f); // Deliberately ignores zoom since it looks nicer if it stays thin. paintInfo.context->setStrokeStyle(SolidStroke); - paintInfo.context->setStrokeColor(leftSeparatorColor); + paintInfo.context->setStrokeColor(leftSeparatorColor, DeviceColorSpace); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), IntPoint(leftEdgeOfSeparator, bounds.bottom())); - paintInfo.context->setStrokeColor(rightSeparatorColor); + paintInfo.context->setStrokeColor(rightSeparatorColor, DeviceColorSpace); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom())); @@ -1416,13 +1422,11 @@ bool RenderThemeMac::paintSearchFieldResultsButton(RenderObject* o, const Render #if ENABLE(VIDEO) typedef enum { MediaControllerThemeClassic = 1, - MediaControllerThemeQT = 2 + MediaControllerThemeQuickTime = 2 } MediaControllerThemeStyle; static int mediaControllerTheme() { - static const long minimumQuickTimeVersion = 0x07630000; // 7.6.3 - static SInt32 quickTimeVersion = 0; static int controllerTheme = -1; if (controllerTheme != -1) @@ -1430,27 +1434,18 @@ static int mediaControllerTheme() controllerTheme = MediaControllerThemeClassic; - if (!quickTimeVersion) { - OSErr err; - err = Gestalt(gestaltQuickTime, &quickTimeVersion); - if (err != noErr) - return controllerTheme; - } - if (quickTimeVersion < minimumQuickTimeVersion) - return controllerTheme; - Boolean validKey; - Boolean useQTMediaUI = CFPreferencesGetAppBooleanValue(CFSTR("UseQuickTimeMediaUI"), CFSTR("com.apple.WebCore"), &validKey); + Boolean useQTMediaUIPref = CFPreferencesGetAppBooleanValue(CFSTR("UseQuickTimeMediaUI"), CFSTR("com.apple.WebCore"), &validKey); -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) - if (validKey && !useQTMediaUI) +#if !defined(BUILDING_ON_TIGER) + if (validKey && !useQTMediaUIPref) return controllerTheme; #else - if (!validKey || !useQTMediaUI) + if (!validKey || !useQTMediaUIPref) return controllerTheme; #endif - controllerTheme = MediaControllerThemeQT; + controllerTheme = MediaControllerThemeQuickTime; return controllerTheme; } #endif @@ -1473,10 +1468,10 @@ void RenderThemeMac::adjustSliderThumbSize(RenderObject* o) const int width = mediaSliderThumbWidth; int height = mediaSliderThumbHeight; - if (mediaControllerTheme() == MediaControllerThemeQT) { + if (mediaControllerTheme() == MediaControllerThemeQuickTime) { CGSize size; - wkMeasureMediaUIPart(MediaSliderThumb, MediaControllerThemeQT, NULL, &size); + wkMeasureMediaUIPart(MediaSliderThumb, MediaControllerThemeQuickTime, NULL, &size); width = size.width; height = size.height; } @@ -1500,7 +1495,9 @@ static unsigned getMediaUIPartStateFlags(Node* node) { unsigned flags = 0; - if (node->active()) + if (node->disabled()) + flags |= MediaUIPartDisabledFlag; + else if (node->active()) flags |= MediaUIPartPressedFlag; return flags; } @@ -1510,7 +1507,7 @@ static FloatRect getUnzoomedRectAndAdjustCurrentContext(RenderObject* o, const R { float zoomLevel = o->style()->effectiveZoom(); FloatRect unzoomedRect(originalRect); - if (zoomLevel != 1.0f && mediaControllerTheme() == MediaControllerThemeQT) { + if (zoomLevel != 1.0f && mediaControllerTheme() == MediaControllerThemeQuickTime) { unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); @@ -1539,7 +1536,7 @@ bool RenderThemeMac::paintMediaMuteButton(RenderObject* o, const RenderObject::P if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) return false; - if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(node)) { + if (MediaControlMuteButtonElement* btn = static_cast<MediaControlMuteButtonElement*>(node)) { LocalCurrentGraphicsContext localContext(paintInfo.context); wkDrawMediaUIPart(btn->displayType(), mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node)); @@ -1594,14 +1591,13 @@ bool RenderThemeMac::paintMediaSliderTrack(RenderObject* o, const RenderObject:: if (!mediaElement) return false; - float timeLoaded = 0; - float currentTime = 0; - float duration = 0; - if (MediaPlayer* player = mediaElement->player()) { - duration = player->duration(); - timeLoaded = player->maxTimeBuffered(); - currentTime = player->currentTime(); - } + RefPtr<TimeRanges> timeRanges = mediaElement->buffered(); + ExceptionCode ignoredException; + float timeLoaded = timeRanges->length() ? timeRanges->end(0, ignoredException) : 0; + float currentTime = mediaElement->currentTime(); + float duration = mediaElement->duration(); + if (isnan(duration)) + duration = 0; paintInfo.context->save(); FloatRect unzoomedRect = getUnzoomedRectAndAdjustCurrentContext(o, paintInfo, r); @@ -1645,7 +1641,22 @@ bool RenderThemeMac::paintMediaReturnToRealtimeButton(RenderObject* o, const Ren return false; } +bool RenderThemeMac::paintMediaToggleClosedCaptionsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + HTMLInputElement* node = static_cast<HTMLInputElement*>(o->node()); + if (!node) + return false; + + MediaControlToggleClosedCaptionsButtonElement* btn = static_cast<MediaControlToggleClosedCaptionsButtonElement*>(node); + if (!btn) + return false; + + LocalCurrentGraphicsContext localContext(paintInfo.context); + wkDrawMediaUIPart(btn->displayType(), mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node)); + return false; +} + bool RenderThemeMac::paintMediaControlsBackground(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { Node* node = o->node(); @@ -1685,12 +1696,25 @@ bool RenderThemeMac::paintMediaTimeRemaining(RenderObject* o, const RenderObject String RenderThemeMac::extraMediaControlsStyleSheet() { - if (mediaControllerTheme() == MediaControllerThemeQT) - return String(mediaControlsQTUserAgentStyleSheet, sizeof(mediaControlsQTUserAgentStyleSheet)); + if (mediaControllerTheme() == MediaControllerThemeQuickTime) + return String(mediaControlsQuickTimeUserAgentStyleSheet, sizeof(mediaControlsQuickTimeUserAgentStyleSheet)); else return String(); } -#endif + +bool RenderThemeMac::shouldRenderMediaControlPart(ControlPart part, Element* element) +{ + if (part == MediaToggleClosedCaptionsButtonPart) { + + // We rely on QTKit to render captions so don't enable the button unless it will be able to do so. + if (!element->hasTagName(videoTag)) + return false; + } + + return RenderTheme::shouldRenderMediaControlPart(part, element); +} + +#endif // ENABLE(VIDEO) NSPopUpButtonCell* RenderThemeMac::popupButton() const { diff --git a/WebCore/rendering/RenderThemeSafari.cpp b/WebCore/rendering/RenderThemeSafari.cpp index 23998d4..9e97079 100644 --- a/WebCore/rendering/RenderThemeSafari.cpp +++ b/WebCore/rendering/RenderThemeSafari.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. + * Copyright (C) 2007, 2008, 2009 Apple Inc. * Copyright (C) 2009 Kenneth Rohde Christiansen * * This library is free software; you can redistribute it and/or @@ -86,7 +86,7 @@ PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) return safariTheme; // keep the reference of one. } -#if !defined(NDEBUG) && defined(USE_DEBUG_SAFARI_THEME) +#ifdef DEBUG_ALL SOFT_LINK_DEBUG_LIBRARY(SafariTheme) #else SOFT_LINK_LIBRARY(SafariTheme) @@ -744,11 +744,21 @@ static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* void RenderThemeSafari::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { + if (r.isEmpty()) + return; + CGContextRef context = paintInfo.context->platformContext(); paintInfo.context->save(); - int radius = o->style()->borderTopLeftRadius().width(); + IntSize topLeftRadius; + IntSize topRightRadius; + IntSize bottomLeftRadius; + IntSize bottomRightRadius; + + o->style()->getBorderRadiiForRect(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + + int radius = topLeftRadius.width(); RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB()); @@ -771,33 +781,27 @@ void RenderThemeSafari::paintMenuListButtonGradients(RenderObject* o, const Rend RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(), r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false)); paintInfo.context->save(); CGContextClipToRect(context, r); - paintInfo.context->addRoundedRectClip(r, - o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), - o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); + paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); CGContextDrawShading(context, mainShading.get()); paintInfo.context->restore(); paintInfo.context->save(); CGContextClipToRect(context, topGradient); - paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), - o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), - IntSize(), IntSize()); + paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), topLeftRadius, topRightRadius, IntSize(), IntSize()); CGContextDrawShading(context, topShading.get()); paintInfo.context->restore(); - paintInfo.context->save(); - CGContextClipToRect(context, bottomGradient); - paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), - IntSize(), IntSize(), - o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); - CGContextDrawShading(context, bottomShading.get()); - paintInfo.context->restore(); + if (!bottomGradient.isEmpty()) { + paintInfo.context->save(); + CGContextClipToRect(context, bottomGradient); + paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), IntSize(), IntSize(), bottomLeftRadius, bottomRightRadius); + CGContextDrawShading(context, bottomShading.get()); + paintInfo.context->restore(); + } paintInfo.context->save(); CGContextClipToRect(context, r); - paintInfo.context->addRoundedRectClip(r, - o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), - o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); + paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); CGContextDrawShading(context, leftShading.get()); CGContextDrawShading(context, rightShading.get()); paintInfo.context->restore(); @@ -807,8 +811,6 @@ void RenderThemeSafari::paintMenuListButtonGradients(RenderObject* o, const Rend bool RenderThemeSafari::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - paintInfo.context->save(); - IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(), r.y() + o->style()->borderTopWidth(), r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(), @@ -826,8 +828,10 @@ bool RenderThemeSafari::paintMenuListButton(RenderObject* o, const RenderObject: if (bounds.width() < arrowWidth + arrowPaddingLeft) return false; - paintInfo.context->setFillColor(o->style()->color()); - paintInfo.context->setStrokeColor(NoStroke); + paintInfo.context->save(); + + paintInfo.context->setFillColor(o->style()->color(), DeviceColorSpace); + paintInfo.context->setStrokeColor(NoStroke, DeviceColorSpace); FloatPoint arrow[3]; arrow[0] = FloatPoint(leftEdge, centerY - arrowHeight / 2.0f); @@ -847,11 +851,11 @@ bool RenderThemeSafari::paintMenuListButton(RenderObject* o, const RenderObject: // Draw the separator to the left of the arrows paintInfo.context->setStrokeThickness(1.0f); paintInfo.context->setStrokeStyle(SolidStroke); - paintInfo.context->setStrokeColor(leftSeparatorColor); + paintInfo.context->setStrokeColor(leftSeparatorColor, DeviceColorSpace); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), IntPoint(leftEdgeOfSeparator, bounds.bottom())); - paintInfo.context->setStrokeColor(rightSeparatorColor); + paintInfo.context->setStrokeColor(rightSeparatorColor, DeviceColorSpace); paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom())); diff --git a/WebCore/rendering/RenderThemeWin.cpp b/WebCore/rendering/RenderThemeWin.cpp index 92bfd03..52afbd6 100644 --- a/WebCore/rendering/RenderThemeWin.cpp +++ b/WebCore/rendering/RenderThemeWin.cpp @@ -819,7 +819,7 @@ bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const RenderO static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef(); static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef(); - paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, bounds); + paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, o->style()->colorSpace(), bounds); return false; } @@ -868,7 +868,7 @@ bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const Re bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef(); - paintInfo.context->drawImage(magnifierImage, bounds); + paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds); return false; } @@ -904,7 +904,7 @@ bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const Render bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef(); - paintInfo.context->drawImage(magnifierImage, bounds); + paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds); return false; } @@ -955,6 +955,23 @@ Color RenderThemeWin::systemColor(int cssValueId) const } #if ENABLE(VIDEO) + +bool RenderThemeWin::shouldRenderMediaControlPart(ControlPart part, Element* element) +{ + if (part == MediaToggleClosedCaptionsButtonPart) { + // We rely on QuickTime to render captions so only enable the button for a video element. +#if SAFARI_THEME_VERSION >= 4 + if (!element->hasTagName(videoTag)) + return false; +#else + return false; +#endif + } + + return RenderTheme::shouldRenderMediaControlPart(part, element); +} + + bool RenderThemeWin::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { return RenderMediaControls::paintMediaControlsPart(MediaFullscreenButton, o, paintInfo, r); @@ -989,6 +1006,12 @@ bool RenderThemeWin::paintMediaSliderThumb(RenderObject* o, const RenderObject:: { return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r); } + +bool RenderThemeWin::paintMediaToggleClosedCaptionsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r); +} + #endif } diff --git a/WebCore/rendering/RenderThemeWin.h b/WebCore/rendering/RenderThemeWin.h index 99c2004..a9fa5e6 100644 --- a/WebCore/rendering/RenderThemeWin.h +++ b/WebCore/rendering/RenderThemeWin.h @@ -125,6 +125,7 @@ public: virtual bool supportsFocusRing(const RenderStyle*) const; #if ENABLE(VIDEO) + virtual bool shouldRenderMediaControlPart(ControlPart, Element*); virtual bool paintMediaFullscreenButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaPlayButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaMuteButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); @@ -132,6 +133,7 @@ public: virtual bool paintMediaSeekForwardButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual bool paintMediaSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaToggleClosedCaptionsButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); #endif private: diff --git a/WebCore/rendering/RenderThemeWince.cpp b/WebCore/rendering/RenderThemeWince.cpp index fb89678..c4aaaad 100644 --- a/WebCore/rendering/RenderThemeWince.cpp +++ b/WebCore/rendering/RenderThemeWince.cpp @@ -28,6 +28,7 @@ #include "CSSValueKeywords.h" #include "Document.h" #include "GraphicsContext.h" +#include "NotImplemented.h" #if ENABLE(VIDEO) #include "HTMLMediaElement.h" #endif @@ -377,12 +378,12 @@ bool RenderThemeWince::paintSearchFieldCancelButton(RenderObject* o, const Rende IntRect cancelBounds(IntPoint(x, y), cancelSize); paintInfo.context->save(); paintInfo.context->addRoundedRectClip(cancelBounds, cancelRadius, cancelRadius, cancelRadius, cancelRadius); - paintInfo.context->fillRect(cancelBounds, buttonColor); + paintInfo.context->fillRect(cancelBounds, buttonColor, DeviceColorSpace); // Draw the 'x' IntSize xSize(3, 3); IntRect xBounds(cancelBounds.location() + IntSize(3, 3), xSize); - paintInfo.context->setStrokeColor(Color::white); + paintInfo.context->setStrokeColor(Color::white, DeviceColorSpace); paintInfo.context->drawLine(xBounds.location(), xBounds.location() + xBounds.size()); paintInfo.context->drawLine(IntPoint(xBounds.right(), xBounds.y()), IntPoint(xBounds.x(), xBounds.bottom())); @@ -489,11 +490,11 @@ bool RenderThemeWince::paintSliderTrack(RenderObject* o, const RenderObject::Pai bool rc = RenderTheme::paintSliderTrack(o, i, r); IntPoint left = IntPoint(r.x() + 2, (r.y() + r.bottom()) / 2); i.context->save(); - i.context->setStrokeColor(Color::gray); - i.context->setFillColor(Color::gray); + i.context->setStrokeColor(Color::gray, DeviceColorSpace); + i.context->setFillColor(Color::gray, DeviceColorSpace); i.context->fillRect(r); #if ENABLE(VIDEO) - HTMLMediaElement *mediaElement = mediaElementParent(o->node()); + HTMLMediaElement* mediaElement = mediaElementParent(o->node()); if (mediaElement) { i.context->setStrokeColor(Color(0, 0xff, 0)); IntPoint right = IntPoint(left.x() + mediaElement->percentLoaded() * (r.right() - r.x() - 4), (r.y() + r.bottom()) / 2); @@ -501,7 +502,7 @@ bool RenderThemeWince::paintSliderTrack(RenderObject* o, const RenderObject::Pai left = right; } #endif - i.context->setStrokeColor(Color::black); + i.context->setStrokeColor(Color::black, DeviceColorSpace); i.context->drawLine(left, IntPoint(r.right() - 2, left.y())); i.context->restore(); return rc; @@ -511,10 +512,10 @@ bool RenderThemeWince::paintSliderThumb(RenderObject* o, const RenderObject::Pai { bool rc = RenderTheme::paintSliderThumb(o, i, r); i.context->save(); - i.context->setStrokeColor(Color::black); - i.context->setFillColor(Color::black); + i.context->setStrokeColor(Color::black, DeviceColorSpace); + i.context->setFillColor(Color::black, DeviceColorSpace); #if ENABLE(VIDEO) - HTMLMediaElement *mediaElement = mediaElementParent(o->node()); + HTMLMediaElement* mediaElement = mediaElementParent(o->node()); if (mediaElement) { float pt = (mediaElement->currentTime() - mediaElement->startTime()) / mediaElement->duration(); FloatRect intRect = r; @@ -574,7 +575,7 @@ bool RenderThemeWince::paintMediaFullscreenButton(RenderObject* o, const RenderO bool RenderThemeWince::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { bool rc = paintButton(o, paintInfo, r); - HTMLMediaElement *mediaElement = mediaElementParent(o->node()); + HTMLMediaElement* mediaElement = mediaElementParent(o->node()); bool muted = !mediaElement || mediaElement->muted(); FloatRect imRect = r; imRect.inflate(-2); @@ -604,7 +605,7 @@ bool RenderThemeWince::paintMediaPlayButton(RenderObject* o, const RenderObject: paintInfo.context->save(); paintInfo.context->setStrokeColor(Color::black); paintInfo.context->setFillColor(Color::black); - HTMLMediaElement *mediaElement = mediaElementParent(o->node()); + HTMLMediaElement* mediaElement = mediaElementParent(o->node()); bool paused = !mediaElement || mediaElement->paused(); if (paused) { float width = imRect.width(); diff --git a/WebCore/rendering/RenderTreeAsText.cpp b/WebCore/rendering/RenderTreeAsText.cpp index 5c6e738..ca4d9d1 100644 --- a/WebCore/rendering/RenderTreeAsText.cpp +++ b/WebCore/rendering/RenderTreeAsText.cpp @@ -28,6 +28,7 @@ #include "CSSMutableStyleDeclaration.h" #include "CharacterNames.h" +#include "CString.h" #include "Document.h" #include "Frame.h" #include "FrameView.h" @@ -35,13 +36,16 @@ #include "HTMLNames.h" #include "InlineTextBox.h" #include "RenderBR.h" +#include "RenderFileUploadControl.h" #include "RenderInline.h" #include "RenderListMarker.h" +#include "RenderPart.h" #include "RenderTableCell.h" #include "RenderView.h" #include "RenderWidget.h" #include "SelectionController.h" #include "TextStream.h" +#include <wtf/UnusedParam.h> #include <wtf/Vector.h> #if ENABLE(SVG) @@ -54,11 +58,19 @@ #include "SVGRenderTreeAsText.h" #endif +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerBacking.h" +#endif + +#if PLATFORM(QT) +#include <QWidget> +#endif + namespace WebCore { using namespace HTMLNames; -static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0); +static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal); #if !ENABLE(SVG) static TextStream &operator<<(TextStream& ts, const IntRect& r) @@ -216,6 +228,9 @@ static TextStream &operator<<(TextStream& ts, const RenderObject& o) ts << " " << r; if (!(o.isText() && !o.isBR())) { + if (o.isFileUploadControl()) { + ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue()); + } if (o.parent() && (o.parent()->style()->color() != o.style()->color())) ts << " [color=" << o.style()->color().name() << "]"; @@ -335,6 +350,24 @@ static TextStream &operator<<(TextStream& ts, const RenderObject& o) } } +#if PLATFORM(QT) + // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget + // is invisible the QWidget should be invisible too. + if (o.isRenderPart()) { + const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o)); + if (part->widget() && part->widget()->platformWidget()) { + QWidget* wid = part->widget()->platformWidget(); + + ts << " [QT: "; + ts << "geometry: {" << wid->geometry() << "} "; + ts << "isHidden: " << wid->isHidden() << " "; + ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " "; + ts << "isParentVisible: " << part->widget()->isParentVisible() << " "; + ts << "mask: {" << wid->mask().boundingRect() << "} ] "; + } + } +#endif + return ts; } @@ -416,9 +449,15 @@ void write(TextStream& ts, const RenderObject& o, int indent) } } +enum LayerPaintPhase { + LayerPaintPhaseAll = 0, + LayerPaintPhaseBackground = -1, + LayerPaintPhaseForeground = 1 +}; + static void write(TextStream& ts, RenderLayer& l, const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect, - int layerType = 0, int indent = 0) + LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal) { writeIndent(ts, indent); @@ -444,19 +483,28 @@ static void write(TextStream& ts, RenderLayer& l, ts << " scrollHeight " << l.scrollHeight(); } - if (layerType == -1) + if (paintPhase == LayerPaintPhaseBackground) ts << " layerType: background only"; - else if (layerType == 1) + else if (paintPhase == LayerPaintPhaseForeground) ts << " layerType: foreground only"; - + +#if USE(ACCELERATED_COMPOSITING) + if (behavior & RenderAsTextShowCompositedLayers) { + if (l.isComposited()) + ts << " (composited, bounds " << l.backing()->compositedBounds() << ")"; + } +#else + UNUSED_PARAM(behavior); +#endif + ts << "\n"; - if (layerType != -1) + if (paintPhase != LayerPaintPhaseBackground) write(ts, *l.renderer(), indent + 1); } static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l, - const IntRect& paintDirtyRect, int indent) + const IntRect& paintDirtyRect, int indent, RenderAsTextBehavior behavior) { // Calculate the clip rects we should use. IntRect layerBounds, damageRect, clipRectToApply, outlineRect; @@ -466,29 +514,46 @@ static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLaye l->updateZOrderLists(); l->updateNormalFlowList(); - bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect, rootLayer); + bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect, rootLayer); Vector<RenderLayer*>* negList = l->negZOrderList(); - if (shouldPaint && negList && negList->size() > 0) - write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, -1, indent); + bool paintsBackgroundSeparately = negList && negList->size() > 0; + if (shouldPaint && paintsBackgroundSeparately) + write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, LayerPaintPhaseBackground, indent, behavior); if (negList) { + int currIndent = indent; + if (behavior & RenderAsTextShowLayerNesting) { + writeIndent(ts, indent); + ts << " negative z-order list(" << negList->size() << ")\n"; + ++currIndent; + } for (unsigned i = 0; i != negList->size(); ++i) - writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent); + writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior); } if (shouldPaint) - write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, negList && negList->size() > 0, indent); - - Vector<RenderLayer*>* normalFlowList = l->normalFlowList(); - if (normalFlowList) { + write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior); + + if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) { + int currIndent = indent; + if (behavior & RenderAsTextShowLayerNesting) { + writeIndent(ts, indent); + ts << " normal flow list(" << normalFlowList->size() << ")\n"; + ++currIndent; + } for (unsigned i = 0; i != normalFlowList->size(); ++i) - writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, indent); + writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior); } - Vector<RenderLayer*>* posList = l->posZOrderList(); - if (posList) { + if (Vector<RenderLayer*>* posList = l->posZOrderList()) { + int currIndent = indent; + if (behavior & RenderAsTextShowLayerNesting) { + writeIndent(ts, indent); + ts << " positive z-order list(" << posList->size() << ")\n"; + ++currIndent; + } for (unsigned i = 0; i != posList->size(); ++i) - writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent); + writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior); } } @@ -534,8 +599,11 @@ static void writeSelection(TextStream& ts, const RenderObject* o) << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().node()) << "\n"; } -String externalRepresentation(RenderObject* o) +String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior) { + frame->document()->updateLayout(); + + RenderObject* o = frame->contentRenderer(); if (!o) return String(); @@ -543,14 +611,43 @@ String externalRepresentation(RenderObject* o) #if ENABLE(SVG) writeRenderResources(ts, o->document()); #endif - if (o->view()->frameView()) - o->view()->frameView()->layout(); if (o->hasLayer()) { RenderLayer* l = toRenderBox(o)->layer(); - writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height())); + writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), 0, behavior); writeSelection(ts, o); } return ts.release(); } +static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter) +{ + for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) { + if (child->isCounter()) { + if (!isFirstCounter) + stream << " "; + isFirstCounter = false; + String str(toRenderText(child)->text()); + stream << str; + } + } +} + +String counterValueForElement(Element* element) +{ + // Make sure the element is not freed during the layout. + RefPtr<Element> elementRef(element); + element->document()->updateLayout(); + TextStream stream; + bool isFirstCounter = true; + // The counter renderers should be children of anonymous children + // (i.e., :before or :after pseudo-elements). + if (RenderObject* renderer = element->renderer()) { + for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling()) { + if (child->isAnonymous()) + writeCounterValuesFromChildren(stream, child, isFirstCounter); + } + } + return stream.release(); +} + } // namespace WebCore diff --git a/WebCore/rendering/RenderTreeAsText.h b/WebCore/rendering/RenderTreeAsText.h index daca253..13525e7 100644 --- a/WebCore/rendering/RenderTreeAsText.h +++ b/WebCore/rendering/RenderTreeAsText.h @@ -28,15 +28,27 @@ namespace WebCore { - class RenderObject; - class String; - class TextStream; +class Element; +class Frame; +class RenderObject; +class String; +class TextStream; - String externalRepresentation(RenderObject*); - void write(TextStream&, const RenderObject&, int indent = 0); +enum RenderAsTextBehaviorFlags { + RenderAsTextBehaviorNormal = 0, + RenderAsTextShowAllLayers = 1 << 0, // Dump all layers, not just those that would paint. + RenderAsTextShowLayerNesting = 1 << 1, // Annotate the layer lists. + RenderAsTextShowCompositedLayers = 1 << 2 // Show which layers are composited. +}; +typedef unsigned RenderAsTextBehavior; - // Helper function shared with SVGRenderTreeAsText - String quoteAndEscapeNonPrintables(const String&); +String externalRepresentation(Frame*, RenderAsTextBehavior = RenderAsTextBehaviorNormal); +void write(TextStream&, const RenderObject&, int indent = 0); + +// Helper function shared with SVGRenderTreeAsText +String quoteAndEscapeNonPrintables(const String&); + +String counterValueForElement(Element*); } // namespace WebCore diff --git a/WebCore/rendering/RenderVideo.cpp b/WebCore/rendering/RenderVideo.cpp index 246d0c0..813f2ef 100644 --- a/WebCore/rendering/RenderVideo.cpp +++ b/WebCore/rendering/RenderVideo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,6 +34,7 @@ #include "HTMLNames.h" #include "HTMLVideoElement.h" #include "MediaPlayer.h" +#include "RenderView.h" #if USE(ACCELERATED_COMPOSITING) #include "RenderLayer.h" @@ -49,18 +50,25 @@ using namespace HTMLNames; static const int cDefaultWidth = 300; static const int cDefaultHeight = 150; -RenderVideo::RenderVideo(HTMLMediaElement* video) +RenderVideo::RenderVideo(HTMLVideoElement* video) : RenderMedia(video) { if (video->player()) setIntrinsicSize(video->player()->naturalSize()); else { - // Video in standalone media documents should not use the default 300x150 - // size since they also have audio thrown at them. By setting the intrinsic - // size to 300x1 the video will resize itself in these cases, and audio will - // have the correct height (it needs to be > 0 for controls to render properly). - if (video->ownerDocument() && video->ownerDocument()->isMediaDocument()) + // When the natural size of the video is unavailable, we use the provided + // width and height attributes of the video element as the intrinsic size until + // better values become available. If these attributes are not set, we fall back + // to a default video size (300x150). + if (video->hasAttribute(widthAttr) && video->hasAttribute(heightAttr)) + setIntrinsicSize(IntSize(video->width(), video->height())); + else if (video->ownerDocument() && video->ownerDocument()->isMediaDocument()) { + // Video in standalone media documents should not use the default 300x150 + // size since they also have audio thrown at them. By setting the intrinsic + // size to 300x1 the video will resize itself in these cases, and audio will + // have the correct height (it needs to be > 0 for controls to render properly). setIntrinsicSize(IntSize(cDefaultWidth, 1)); + } else setIntrinsicSize(IntSize(cDefaultWidth, cDefaultHeight)); } @@ -73,7 +81,15 @@ RenderVideo::~RenderVideo() p->setFrameView(0); } } - + +void RenderVideo::intrinsicSizeChanged() +{ + if (videoElement()->shouldDisplayPosterImage()) + RenderMedia::intrinsicSizeChanged(); + videoSizeChanged(); +} + + void RenderVideo::videoSizeChanged() { if (!player()) @@ -86,41 +102,72 @@ void RenderVideo::videoSizeChanged() } } -IntRect RenderVideo::videoBox() const +void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect) { + RenderMedia::imageChanged(newImage, rect); + + // Cache the image intrinsic size so we can continue to use it to draw the image correctly + // even after we know the video intrisic size but aren't able to draw video frames yet + // (we don't want to scale the poster to the video size). + if (videoElement()->shouldDisplayPosterImage()) + m_cachedImageSize = intrinsicSize(); +} + +IntRect RenderVideo::videoBox() const +{ + if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage()) + return IntRect(); + + IntSize elementSize; + if (videoElement()->shouldDisplayPosterImage()) + elementSize = m_cachedImageSize; + else + elementSize = intrinsicSize(); + IntRect contentRect = contentBoxRect(); - - if (intrinsicSize().isEmpty() || contentRect.isEmpty()) + if (elementSize.isEmpty() || contentRect.isEmpty()) return IntRect(); - IntRect resultRect = contentRect; - int ratio = contentRect.width() * intrinsicSize().height() - contentRect.height() * intrinsicSize().width(); + IntRect renderBox = contentRect; + int ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width(); if (ratio > 0) { - int newWidth = contentRect.height() * intrinsicSize().width() / intrinsicSize().height(); + int newWidth = renderBox.height() * elementSize.width() / elementSize.height(); // Just fill the whole area if the difference is one pixel or less (in both sides) - if (resultRect.width() - newWidth > 2) - resultRect.setWidth(newWidth); - resultRect.move((contentRect.width() - resultRect.width()) / 2, 0); + if (renderBox.width() - newWidth > 2) + renderBox.setWidth(newWidth); + renderBox.move((contentRect.width() - renderBox.width()) / 2, 0); } else if (ratio < 0) { - int newHeight = contentRect.width() * intrinsicSize().height() / intrinsicSize().width(); - if (resultRect.height() - newHeight > 2) - resultRect.setHeight(newHeight); - resultRect.move(0, (contentRect.height() - resultRect.height()) / 2); + int newHeight = renderBox.width() * elementSize.height() / elementSize.width(); + if (renderBox.height() - newHeight > 2) + renderBox.setHeight(newHeight); + renderBox.move(0, (contentRect.height() - renderBox.height()) / 2); } - return resultRect; + + return renderBox; } void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty) { MediaPlayer* mediaPlayer = player(); - if (!mediaPlayer) + bool displayingPoster = videoElement()->shouldDisplayPosterImage(); + + if (displayingPoster && document()->printing() && !view()->printImages()) return; - updatePlayer(); + + if (!displayingPoster) { + if (!mediaPlayer) + return; + updatePlayer(); + } + IntRect rect = videoBox(); if (rect.isEmpty()) return; rect.move(tx, ty); - mediaPlayer->paint(paintInfo.context, rect); + if (displayingPoster) + paintIntoRect(paintInfo.context, rect); + else + mediaPlayer->paint(paintInfo.context, rect); } void RenderVideo::layout() @@ -129,6 +176,12 @@ void RenderVideo::layout() updatePlayer(); } +HTMLVideoElement* RenderVideo::videoElement() const +{ + ASSERT(node()->hasTagName(videoTag)); + return static_cast<HTMLVideoElement*>(node()); +} + void RenderVideo::updateFromElement() { RenderMedia::updateFromElement(); @@ -140,7 +193,7 @@ void RenderVideo::updatePlayer() MediaPlayer* mediaPlayer = player(); if (!mediaPlayer) return; - if (!mediaElement()->inActiveDocument()) { + if (!videoElement()->inActiveDocument()) { mediaPlayer->setVisible(false); return; } @@ -155,40 +208,6 @@ void RenderVideo::updatePlayer() mediaPlayer->setVisible(true); } -bool RenderVideo::isWidthSpecified() const -{ - switch (style()->width().type()) { - case Fixed: - case Percent: - return true; - case Auto: - case Relative: // FIXME: Shouldn't this case return true? It doesn't for images. - case Static: - case Intrinsic: - case MinIntrinsic: - return false; - } - ASSERT(false); - return false; -} - -bool RenderVideo::isHeightSpecified() const -{ - switch (style()->height().type()) { - case Fixed: - case Percent: - return true; - case Auto: - case Relative: // FIXME: Shouldn't this case return true? It doesn't for images. - case Static: - case Intrinsic: - case MinIntrinsic: - return false; - } - ASSERT(false); - return false; -} - int RenderVideo::calcReplacedWidth(bool includeMaxWidth) const { int width; @@ -235,24 +254,9 @@ int RenderVideo::calcAspectRatioHeight() const return RenderBox::calcReplacedWidth() * intrinsicHeight / intrinsicWidth; } -void RenderVideo::calcPrefWidths() +int RenderVideo::minimumReplacedHeight() const { - ASSERT(prefWidthsDirty()); - - int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight(); - m_maxPrefWidth = calcReplacedWidth(false) + paddingAndBorders; - - if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) - m_maxPrefWidth = min(m_maxPrefWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0)); - - if (style()->width().isPercent() || style()->height().isPercent() || - style()->maxWidth().isPercent() || style()->maxHeight().isPercent() || - style()->minWidth().isPercent() || style()->minHeight().isPercent()) - m_minPrefWidth = 0; - else - m_minPrefWidth = m_maxPrefWidth; - - setPrefWidthsDirty(false); + return 0; } #if USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/rendering/RenderVideo.h b/WebCore/rendering/RenderVideo.h index 79e5b4e..3ca5328 100644 --- a/WebCore/rendering/RenderVideo.h +++ b/WebCore/rendering/RenderVideo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,13 +33,14 @@ namespace WebCore { class HTMLMediaElement; +class HTMLVideoElement; #if USE(ACCELERATED_COMPOSITING) class GraphicsLayer; #endif class RenderVideo : public RenderMedia { public: - RenderVideo(HTMLMediaElement*); + RenderVideo(HTMLVideoElement*); virtual ~RenderVideo(); void videoSizeChanged(); @@ -53,8 +54,10 @@ public: private: virtual void updateFromElement(); + inline HTMLVideoElement* videoElement() const; - virtual void intrinsicSizeChanged() { videoSizeChanged(); } + virtual void intrinsicSizeChanged(); + virtual void imageChanged(WrappedImagePtr, const IntRect*); virtual const char* renderName() const { return "RenderVideo"; } @@ -67,16 +70,14 @@ private: virtual int calcReplacedWidth(bool includeMaxWidth = true) const; virtual int calcReplacedHeight() const; - - virtual void calcPrefWidths(); + virtual int minimumReplacedHeight() const; int calcAspectRatioWidth() const; int calcAspectRatioHeight() const; - bool isWidthSpecified() const; - bool isHeightSpecified() const; - void updatePlayer(); + + IntSize m_cachedImageSize; }; inline RenderVideo* toRenderVideo(RenderObject* object) diff --git a/WebCore/rendering/RenderView.cpp b/WebCore/rendering/RenderView.cpp index 584e38a..094abfd 100644 --- a/WebCore/rendering/RenderView.cpp +++ b/WebCore/rendering/RenderView.cpp @@ -133,12 +133,10 @@ void RenderView::layout() if (needsLayout()) RenderBlock::layout(); - // Reset overflowWidth and overflowHeight, since they act as a lower bound for docWidth() and docHeight(). - setOverflowWidth(width()); - setOverflowHeight(height()); - - setOverflowWidth(docWidth()); - setOverflowHeight(docHeight()); + // Reset overflow and then replace it with docWidth and docHeight. + m_overflow.clear(); + addLayoutOverflow(IntRect(0, 0, docWidth(), docHeight())); + ASSERT(layoutDelta() == IntSize()); ASSERT(m_layoutStateDisableCount == 0); @@ -153,12 +151,30 @@ void RenderView::mapLocalToContainer(RenderBoxModelObject* repaintContainer, boo // then we should have found it by now. ASSERT_UNUSED(repaintContainer, !repaintContainer || repaintContainer == this); +#ifdef ANDROID_FIXED_ELEMENTS +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + const Settings * settings = document()->settings(); + if (settings && (settings->viewportWidth() == -1 || settings->viewportWidth() == 0) && + !settings->viewportUserScalable()) +#else + if (false) +#endif +#endif if (fixed && m_frameView) transformState.move(m_frameView->scrollOffset()); } void RenderView::mapAbsoluteToLocalPoint(bool fixed, bool /*useTransforms*/, TransformState& transformState) const { +#ifdef ANDROID_FIXED_ELEMENTS +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + const Settings * settings = document()->settings(); + if (settings && (settings->viewportWidth() == -1 || settings->viewportWidth() == 0) && + !settings->viewportUserScalable()) +#else + if (false) +#endif +#endif if (fixed && m_frameView) transformState.move(-m_frameView->scrollOffset()); } @@ -212,7 +228,7 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, int, int) if (baseColor.alpha() > 0) { paintInfo.context->save(); paintInfo.context->setCompositeOperation(CompositeCopy); - paintInfo.context->fillRect(paintInfo.rect, baseColor); + paintInfo.context->fillRect(paintInfo.rect, baseColor, style()->colorSpace()); paintInfo.context->restore(); } else paintInfo.context->clearRect(paintInfo.rect); @@ -282,6 +298,15 @@ void RenderView::computeRectForRepaint(RenderBoxModelObject* repaintContainer, I if (printing()) return; +#ifdef ANDROID_FIXED_ELEMENTS +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + const Settings * settings = document()->settings(); + if (settings && (settings->viewportWidth() == -1 || settings->viewportWidth() == 0) && + !settings->viewportUserScalable()) +#else + if (false) +#endif +#endif if (fixed && m_frameView) rect.move(m_frameView->scrollX(), m_frameView->scrollY()); @@ -340,7 +365,13 @@ IntRect RenderView::selectionBounds(bool clipToVisibleContent) const SelectionMap::iterator end = selectedObjects.end(); for (SelectionMap::iterator i = selectedObjects.begin(); i != end; ++i) { RenderSelectionInfo* info = i->second; - selRect.unite(info->rect()); + // RenderSelectionInfo::rect() is in the coordinates of the repaintContainer, so map to page coordinates. + IntRect currRect = info->rect(); + if (RenderBoxModelObject* repaintContainer = info->repaintContainer()) { + FloatQuad absQuad = repaintContainer->localToAbsoluteQuad(FloatRect(currRect)); + currRect = absQuad.enclosingBoundingBox(); + } + selRect.unite(currRect); delete info; } return selRect; @@ -439,7 +470,7 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e o = o->nextInPreOrder(); } - m_cachedSelectionBounds = IntRect(); + m_layer->clearBlockSelectionGapsBounds(); // Now that the selection state has been updated for the new objects, walk them again and // put them in the new objects list. @@ -452,9 +483,7 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e RenderBlockSelectionInfo* blockInfo = newSelectedBlocks.get(cb); if (blockInfo) break; - blockInfo = new RenderBlockSelectionInfo(cb); - newSelectedBlocks.set(cb, blockInfo); - m_cachedSelectionBounds.unite(blockInfo->rects()); + newSelectedBlocks.set(cb, new RenderBlockSelectionInfo(cb)); cb = cb->containingBlock(); } } @@ -472,6 +501,8 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e return; } + m_frameView->beginDeferredRepaints(); + // 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; @@ -523,11 +554,13 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e newInfo->repaint(); delete newInfo; } + + m_frameView->endDeferredRepaints(); } void RenderView::clearSelection() { - repaintViewRectangle(m_cachedSelectionBounds); + m_layer->repaintBlockSelectionGaps(); setSelection(0, -1, 0, -1, RepaintNewMinusOld); } @@ -658,6 +691,17 @@ void RenderView::pushLayoutState(RenderObject* root) m_layoutState = new (renderArena()) LayoutState(root); } +bool RenderView::shouldDisableLayoutStateForSubtree(RenderObject* renderer) const +{ + RenderObject* o = renderer; + while (o) { + if (o->hasColumns() || o->hasTransform() || o->hasReflection()) + return true; + o = o->container(); + } + return false; +} + void RenderView::updateHitTestResult(HitTestResult& result, const IntPoint& point) { if (result.innerNode()) diff --git a/WebCore/rendering/RenderView.h b/WebCore/rendering/RenderView.h index 089fe85..faa2465 100644 --- a/WebCore/rendering/RenderView.h +++ b/WebCore/rendering/RenderView.h @@ -1,6 +1,4 @@ /* - * This file is part of the HTML widget for KDE. - * * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * Copyright (C) 2006 Apple Computer, Inc. * @@ -77,9 +75,11 @@ public: bool printing() const; 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 setTruncatedAt(int y) { m_truncatedAt = y; m_bestTruncatedAt = m_truncatorWidth = 0; m_minimumColumnHeight = 0; m_forcedPageBreak = false; } void setBestTruncatedAt(int y, RenderBoxModelObject* forRenderer, bool forcedBreak = false); + void setMinimumColumnHeight(int height) { m_minimumColumnHeight = height; } int bestTruncatedAt() const { return m_bestTruncatedAt; } + int minimumColumnHeight() const { return m_minimumColumnHeight; } int truncatedAt() const { return m_truncatedAt; } @@ -105,6 +105,9 @@ public: void updateWidgetPositions(); void addWidget(RenderWidget*); void removeWidget(RenderWidget*); +#ifdef ANDROID_PLUGINS + const HashSet<RenderWidget*>& widgets() const { return m_widgets; } +#endif // layoutDelta is used transiently during layout to store how far an object has moved from its // last layout location, in order to repaint correctly. @@ -140,6 +143,8 @@ public: state->destroy(renderArena()); } + bool shouldDisableLayoutStateForSubtree(RenderObject*) const; + // Returns true if layoutState should be used for its cached offset and clip. bool layoutStateEnabled() const { return m_layoutStateDisableCount == 0 && m_layoutState; } LayoutState* layoutState() const { return m_layoutState; } @@ -169,7 +174,7 @@ protected: private: bool shouldRepaint(const IntRect& r) const; - + #ifdef FLATTEN_FRAMESET public: // used by layout function #endif @@ -196,10 +201,9 @@ protected: RenderWidgetSet m_widgets; private: - IntRect m_cachedSelectionBounds; - int m_bestTruncatedAt; int m_truncatorWidth; + int m_minimumColumnHeight; bool m_forcedPageBreak; LayoutState* m_layoutState; unsigned m_layoutStateDisableCount; diff --git a/WebCore/rendering/RenderWidget.cpp b/WebCore/rendering/RenderWidget.cpp index 36f4fed..f6f6da8 100644 --- a/WebCore/rendering/RenderWidget.cpp +++ b/WebCore/rendering/RenderWidget.cpp @@ -23,11 +23,12 @@ #include "config.h" #include "RenderWidget.h" -#include "AnimationController.h" #include "AXObjectCache.h" +#include "AnimationController.h" #include "GraphicsContext.h" #include "HitTestResult.h" #include "RenderView.h" +#include "RenderWidgetProtector.h" using namespace std; @@ -39,18 +40,65 @@ static HashMap<const Widget*, RenderWidget*>& widgetRendererMap() return *staticWidgetRendererMap; } +static size_t widgetHierarchyUpdateSuspendCount; + +typedef HashMap<RefPtr<Widget>, FrameView*> WidgetToParentMap; + +static WidgetToParentMap& widgetNewParentMap() +{ + DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ()); + return map; +} + +void RenderWidget::suspendWidgetHierarchyUpdates() +{ + widgetHierarchyUpdateSuspendCount++; +} + +void RenderWidget::resumeWidgetHierarchyUpdates() +{ + ASSERT(widgetHierarchyUpdateSuspendCount); + if (widgetHierarchyUpdateSuspendCount == 1) { + WidgetToParentMap map = widgetNewParentMap(); + widgetNewParentMap().clear(); + WidgetToParentMap::iterator end = map.end(); + for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) { + Widget* child = it->first.get(); + ScrollView* currentParent = child->parent(); + FrameView* newParent = it->second; + if (newParent != currentParent) { + if (currentParent) + currentParent->removeChild(child); + if (newParent) + newParent->addChild(child); + } + } + } + widgetHierarchyUpdateSuspendCount--; +} + +static void moveWidgetToParentSoon(Widget* child, FrameView* parent) +{ + if (!widgetHierarchyUpdateSuspendCount) { + if (parent) + parent->addChild(child); + else + child->removeFromParent(); + return; + } + widgetNewParentMap().set(child, parent); +} + RenderWidget::RenderWidget(Node* node) : RenderReplaced(node) , m_widget(0) , m_frameView(node->document()->view()) - , m_refCount(0) -{ - view()->addWidget(this); - // Reference counting is used to prevent the widget from being // destroyed while inside the Widget code, which might not be // able to handle that. - ref(); + , m_refCount(1) +{ + view()->addWidget(this); } void RenderWidget::destroy() @@ -60,6 +108,7 @@ void RenderWidget::destroy() // So the code below includes copied and pasted contents of // both RenderBox::destroy() and RenderObject::destroy(). // Fix originally made for <rdar://problem/4228818>. + animation()->cancelAnimations(this); if (RenderView* v = view()) @@ -71,12 +120,8 @@ void RenderWidget::destroy() } remove(); - if (m_widget) { - if (m_frameView) - m_frameView->removeChild(m_widget.get()); - widgetRendererMap().remove(m_widget.get()); - } - + setWidget(0); + // removes from override size map if (hasOverrideSize()) setOverrideSize(-1); @@ -103,40 +148,43 @@ RenderWidget::~RenderWidget() clearWidget(); } -void RenderWidget::setWidgetGeometry(const IntRect& frame) +bool RenderWidget::setWidgetGeometry(const IntRect& frame) { - if (node() && m_widget->frameRect() != frame) { - RenderArena* arena = ref(); - RefPtr<Node> protectedElement(node()); - m_widget->setFrameRect(frame); - deref(arena); - } + ASSERT(!widgetHierarchyUpdateSuspendCount); + if (!node() || m_widget->frameRect() == frame) + return false; + + RenderWidgetProtector protector(this); + RefPtr<Node> protectedNode(node()); + m_widget->setFrameRect(frame); + return true; } void RenderWidget::setWidget(PassRefPtr<Widget> widget) { - if (widget != m_widget) { - if (m_widget) { - m_widget->removeFromParent(); - widgetRendererMap().remove(m_widget.get()); - clearWidget(); - } - m_widget = widget; - if (m_widget) { - widgetRendererMap().add(m_widget.get(), this); - // if we've already received a layout, apply the calculated space to the - // widget immediately, but we have to have really been full constructed (with a non-null - // style pointer). - if (style()) { - if (!needsLayout()) - setWidgetGeometry(absoluteContentBox()); - if (style()->visibility() != VISIBLE) - m_widget->hide(); - else - m_widget->show(); - } - m_frameView->addChild(m_widget.get()); + if (widget == m_widget) + return; + + if (m_widget) { + moveWidgetToParentSoon(m_widget.get(), 0); + widgetRendererMap().remove(m_widget.get()); + clearWidget(); + } + m_widget = widget; + if (m_widget) { + widgetRendererMap().add(m_widget.get(), this); + // If we've already received a layout, apply the calculated space to the + // widget immediately, but we have to have really been fully constructed (with a non-null + // style pointer). + if (style()) { + if (!needsLayout()) + setWidgetGeometry(absoluteContentBox()); + if (style()->visibility() != VISIBLE) + m_widget->hide(); + else + m_widget->show(); } + moveWidgetToParentSoon(m_widget.get(), m_frameView); } } @@ -158,6 +206,12 @@ void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldSt } } +void RenderWidget::showSubstituteImage(PassRefPtr<Image> prpImage) +{ + m_substituteImage = prpImage; + repaint(); +} + void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) { if (!shouldPaint(paintInfo, tx, ty)) @@ -183,27 +237,43 @@ void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) #endif if (style()->hasBorderRadius()) { + IntRect borderRect = IntRect(tx, ty, width(), height()); + + if (borderRect.isEmpty()) + return; + // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. paintInfo.context->save(); IntSize topLeft, topRight, bottomLeft, bottomRight; - IntRect borderRect = IntRect(tx, ty, width(), height()); style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); } if (m_widget) { - // Move the widget if necessary. We normally move and resize widgets during layout, but sometimes - // widgets can move without layout occurring (most notably when you scroll a document that - // contains fixed positioned elements). - m_widget->move(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); - // Tell the widget to paint now. This is the only time the widget is allowed // to paint itself. That way it will composite properly with z-indexed layers. - m_widget->paint(paintInfo.context, paintInfo.rect); + if (m_substituteImage) + paintInfo.context->drawImage(m_substituteImage.get(), style()->colorSpace(), m_widget->frameRect()); + else { + IntPoint widgetLocation = m_widget->frameRect().location(); + IntPoint paintLocation(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); + IntRect paintRect = paintInfo.rect; + + IntSize paintOffset = paintLocation - widgetLocation; + // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, + // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. + if (!paintOffset.isZero()) { + paintInfo.context->translate(paintOffset); + paintRect.move(-paintOffset); + } + m_widget->paint(paintInfo.context, paintRect); - if (m_widget->isFrameView() && paintInfo.overlapTestRequests && !static_cast<FrameView*>(m_widget.get())->useSlowRepaints()) { + if (!paintOffset.isZero()) + paintInfo.context->translate(-paintOffset); + } + if (m_widget->isFrameView() && paintInfo.overlapTestRequests && !static_cast<FrameView*>(m_widget.get())->useSlowRepaintsIfNotOverlapped()) { ASSERT(!paintInfo.overlapTestRequests->contains(this)); paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); } @@ -215,7 +285,7 @@ void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) // Paint a partially transparent wash over selected widgets. if (isSelected() && !document()->printing()) { // FIXME: selectionRect() is in absolute, not painting coordinates. - paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor()); + paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace()); } } @@ -244,17 +314,8 @@ void RenderWidget::updateWidgetPosition() int w = width() - borderLeft() - borderRight() - paddingLeft() - paddingRight(); int h = height() - borderTop() - borderBottom() - paddingTop() - paddingBottom(); - IntRect newBounds(absPos.x(), absPos.y(), w, h); - IntRect oldBounds(m_widget->frameRect()); - bool boundsChanged = newBounds != oldBounds; - if (boundsChanged) { - RenderArena* arena = ref(); - node()->ref(); - m_widget->setFrameRect(newBounds); - node()->deref(); - deref(arena); - } - + bool boundsChanged = setWidgetGeometry(IntRect(absPos.x(), absPos.y(), w, h)); + #ifndef FLATTEN_IFRAME // 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 diff --git a/WebCore/rendering/RenderWidget.h b/WebCore/rendering/RenderWidget.h index bb68143..6cad04a 100644 --- a/WebCore/rendering/RenderWidget.h +++ b/WebCore/rendering/RenderWidget.h @@ -40,6 +40,11 @@ public: void updateWidgetPosition(); + void showSubstituteImage(PassRefPtr<Image>); + + static void suspendWidgetHierarchyUpdates(); + static void resumeWidgetHierarchyUpdates(); + protected: RenderWidget(Node*); @@ -59,12 +64,14 @@ private: virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); virtual void setOverlapTestResult(bool); - void setWidgetGeometry(const IntRect&); + bool setWidgetGeometry(const IntRect&); + friend class RenderWidgetProtector; RenderArena* ref() { ++m_refCount; return renderArena(); } void deref(RenderArena*); RefPtr<Widget> m_widget; + RefPtr<Image> m_substituteImage; FrameView* m_frameView; int m_refCount; }; diff --git a/WebCore/rendering/RenderWidgetProtector.h b/WebCore/rendering/RenderWidgetProtector.h new file mode 100644 index 0000000..788304c --- /dev/null +++ b/WebCore/rendering/RenderWidgetProtector.h @@ -0,0 +1,53 @@ +/* + * 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 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 RenderWidgetProtector_h +#define RenderWidgetProtector_h + +#include "RenderWidget.h" + +namespace WebCore { + +class RenderWidgetProtector : private Noncopyable { +public: + RenderWidgetProtector(RenderWidget* object) + : m_object(object) + , m_arena(object->ref()) + { + } + + ~RenderWidgetProtector() + { + m_object->deref(m_arena); + } + +private: + RenderWidget* m_object; + RenderArena* m_arena; +}; + +} + +#endif // RenderWidgetProtector_h diff --git a/WebCore/rendering/RootInlineBox.cpp b/WebCore/rendering/RootInlineBox.cpp index e8f04da..23316f7 100644 --- a/WebCore/rendering/RootInlineBox.cpp +++ b/WebCore/rendering/RootInlineBox.cpp @@ -21,6 +21,7 @@ #include "RootInlineBox.h" #include "BidiResolver.h" +#include "Chrome.h" #include "ChromeClient.h" #include "Document.h" #include "EllipsisBox.h" @@ -38,28 +39,8 @@ namespace WebCore { typedef WTF::HashMap<const RootInlineBox*, EllipsisBox*> EllipsisBoxMap; static EllipsisBoxMap* gEllipsisBoxMap = 0; -void* RootInlineBox::Overflow::operator new(size_t sz, RenderArena* renderArena) throw() -{ - return renderArena->allocate(sz); -} - -void RootInlineBox::Overflow::operator delete(void* ptr, size_t sz) -{ - // Stash size where destroy can find it. - *(size_t *)ptr = sz; -} - -void RootInlineBox::Overflow::destroy(RenderArena* renderArena) -{ - delete this; - // Recover the size left there for us by operator delete and free the memory. - renderArena->free(*(size_t *)this, this); -} - void RootInlineBox::destroy(RenderArena* arena) { - if (m_overflow) - m_overflow->destroy(arena); detachEllipsisBox(arena); InlineFlowBox::destroy(arena); } @@ -155,8 +136,8 @@ void RootInlineBox::addHighlightOverflow() // Highlight acts as a selection inflation. FloatRect rootRect(0, selectionTop(), width(), selectionHeight()); 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())); + setHorizontalOverflowPositions(leftLayoutOverflow(), rightLayoutOverflow(), min(leftVisualOverflow(), inflatedRect.x()), max(rightVisualOverflow(), inflatedRect.right())); + setVerticalOverflowPositions(topLayoutOverflow(), bottomLayoutOverflow(), min(topVisualOverflow(), inflatedRect.y()), max(bottomVisualOverflow(), inflatedRect.bottom()), height()); } void RootInlineBox::paintCustomHighlight(RenderObject::PaintInfo& paintInfo, int tx, int ty, const AtomicString& highlightType) @@ -205,12 +186,8 @@ bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re void RootInlineBox::adjustPosition(int dx, int dy) { InlineFlowBox::adjustPosition(dx, dy); - if (m_overflow) { - m_overflow->m_topOverflow += dy; - m_overflow->m_bottomOverflow += dy; - m_overflow->m_selectionTop += dy; - m_overflow->m_selectionBottom += dy; - } + m_lineTop += dy; + m_lineBottom += dy; m_blockHeight += dy; } @@ -225,6 +202,37 @@ void RootInlineBox::childRemoved(InlineBox* box) } } +int RootInlineBox::verticallyAlignBoxes(int heightOfBlock) +{ + int maxPositionTop = 0; + int maxPositionBottom = 0; + int maxAscent = 0; + int maxDescent = 0; + + // 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 = renderer(); + while (curr && !curr->node()) + curr = curr->container(); + bool strictMode = (curr && curr->document()->inStrictMode()); + + computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode); + + if (maxAscent + maxDescent < max(maxPositionTop, maxPositionBottom)) + adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); + + int maxHeight = maxAscent + maxDescent; + int lineTop = heightOfBlock; + int lineBottom = heightOfBlock; + placeBoxesVertically(heightOfBlock, maxHeight, maxAscent, strictMode, lineTop, lineBottom); + computeVerticalOverflow(lineTop, lineBottom, strictMode); + setLineTopBottomPositions(lineTop, lineBottom); + + heightOfBlock += maxHeight; + + return heightOfBlock; +} + GapRects RootInlineBox::fillLineSelectionGap(int selTop, int selHeight, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, const RenderObject::PaintInfo* paintInfo) { @@ -321,9 +329,9 @@ InlineBox* RootInlineBox::lastSelectedBox() return 0; } -int RootInlineBox::selectionTop() +int RootInlineBox::selectionTop() const { - int selectionTop = m_overflow ? m_overflow->m_selectionTop : m_y; + int selectionTop = m_lineTop; if (!prevRootBox()) return selectionTop; @@ -407,18 +415,6 @@ EllipsisBox* RootInlineBox::ellipsisBox() const return gEllipsisBoxMap->get(this); } -void RootInlineBox::setVerticalOverflowPositions(int top, int bottom) -{ - if (!m_overflow) { - const Font& font = renderer()->style(m_firstLine)->font(); - if (top == m_y && bottom == m_y + font.height()) - return; - m_overflow = new (renderer()->renderArena()) Overflow(this); - } - m_overflow->m_topOverflow = top; - m_overflow->m_bottomOverflow = bottom; -} - void RootInlineBox::removeLineBoxFromRenderObject() { block()->lineBoxes()->removeLineBox(this); diff --git a/WebCore/rendering/RootInlineBox.h b/WebCore/rendering/RootInlineBox.h index 171be9d..7fce1d3 100644 --- a/WebCore/rendering/RootInlineBox.h +++ b/WebCore/rendering/RootInlineBox.h @@ -1,6 +1,4 @@ /* - * This file is part of the line box implementation for KDE. - * * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -38,31 +36,33 @@ class RootInlineBox : public InlineFlowBox { public: RootInlineBox(RenderObject* obj) : InlineFlowBox(obj) - , m_overflow(0) , m_lineBreakObj(0) , m_lineBreakPos(0) + , m_lineTop(0) + , m_lineBottom(0) { } + virtual void destroy(RenderArena*); + virtual bool isRootInlineBox() const { return true; } - virtual void destroy(RenderArena*); void detachEllipsisBox(RenderArena*); - RootInlineBox* nextRootBox() { return static_cast<RootInlineBox*>(m_nextLine); } - RootInlineBox* prevRootBox() { return static_cast<RootInlineBox*>(m_prevLine); } + RootInlineBox* nextRootBox() const { return static_cast<RootInlineBox*>(m_nextLine); } + RootInlineBox* prevRootBox() const { return static_cast<RootInlineBox*>(m_prevLine); } virtual void adjustPosition(int dx, int dy); - 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; } + int lineTop() const { return m_lineTop; } + int lineBottom() const { return m_lineBottom; } - virtual void setVerticalOverflowPositions(int top, int bottom); - void setHorizontalOverflowPositions(int left, int right); + int selectionTop() const; + int selectionBottom() const { return lineBottom(); } + int selectionHeight() const { return max(0, selectionBottom() - selectionTop()); } - virtual void setVerticalSelectionPositions(int top, int bottom); + virtual int verticallyAlignBoxes(int heightOfBlock); + void setLineTopBottomPositions(int top, int bottom); virtual RenderLineBoxList* rendererLineBoxes() const; @@ -116,68 +116,36 @@ public: RenderBlock* block() const; - int selectionTop(); - 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); Vector<RenderBox*>& floats() { ASSERT(!isDirty()); - if (!m_overflow) - m_overflow = new (m_renderer->renderArena()) Overflow(this); - return m_overflow->floats; + if (!m_floats) + m_floats.set(new Vector<RenderBox*>()); + return *m_floats; } - Vector<RenderBox*>* floatsPtr() { ASSERT(!isDirty()); return m_overflow ? &m_overflow->floats : 0; } + Vector<RenderBox*>* floatsPtr() { ASSERT(!isDirty()); return m_floats.get(); } 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 - // below our line (e.g., a child whose font has a huge descent). - - // Allocated only when some of these fields have non-default values - struct Overflow { - Overflow(RootInlineBox* box) - : m_topOverflow(box->m_y) - , 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->height()) - { - } - - void destroy(RenderArena*); - void* operator new(size_t, RenderArena*) throw(); - void operator delete(void*, size_t); - - int m_topOverflow; - int m_bottomOverflow; - int m_leftOverflow; - int m_rightOverflow; - int m_selectionTop; - int m_selectionBottom; - // Floats hanging off the line are pushed into this vector during layout. It is only - // good for as long as the line has not been marked dirty. - Vector<RenderBox*> floats; - private: - void* operator new(size_t) throw(); - }; - - Overflow* m_overflow; - // Where this line ended. The exact object and the position within that object are stored so that // we can create an InlineIterator beginning just after the end of this line. RenderObject* m_lineBreakObj; unsigned m_lineBreakPos; RefPtr<BidiContext> m_lineBreakContext; + int m_lineTop; + int m_lineBottom; + + // Floats hanging off the line are pushed into this vector during layout. It is only + // good for as long as the line has not been marked dirty. + OwnPtr<Vector<RenderBox*> > m_floats; + // The height of the block at the end of this line. This is where the next line starts. int m_blockHeight; @@ -186,27 +154,10 @@ protected: WTF::Unicode::Direction m_lineBreakBidiStatusLast : 5; }; -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_renderer->renderArena()) Overflow(this); - } - m_overflow->m_leftOverflow = left; - m_overflow->m_rightOverflow = right; -} - -inline void RootInlineBox::setVerticalSelectionPositions(int top, int bottom) +inline void RootInlineBox::setLineTopBottomPositions(int top, int bottom) { - if (!m_overflow) { - const Font& font = m_renderer->style(m_firstLine)->font(); - if (top == m_y && bottom == m_y + font.height()) - return; - m_overflow = new (m_renderer->renderArena()) Overflow(this); - } - m_overflow->m_selectionTop = top; - m_overflow->m_selectionBottom = bottom; + m_lineTop = top; + m_lineBottom = bottom; } } // namespace WebCore diff --git a/WebCore/rendering/SVGCharacterLayoutInfo.cpp b/WebCore/rendering/SVGCharacterLayoutInfo.cpp index 900e0ba..7e85672 100644 --- a/WebCore/rendering/SVGCharacterLayoutInfo.cpp +++ b/WebCore/rendering/SVGCharacterLayoutInfo.cpp @@ -512,9 +512,9 @@ bool SVGChar::isHidden() const return pathData && pathData->hidden; } -TransformationMatrix SVGChar::characterTransform() const +AffineTransform SVGChar::characterTransform() const { - TransformationMatrix ctm; + AffineTransform ctm; // Rotate character around angle, and possibly scale. ctm.translate(x, y); diff --git a/WebCore/rendering/SVGCharacterLayoutInfo.h b/WebCore/rendering/SVGCharacterLayoutInfo.h index b5b4f3e..f80c79c 100644 --- a/WebCore/rendering/SVGCharacterLayoutInfo.h +++ b/WebCore/rendering/SVGCharacterLayoutInfo.h @@ -24,15 +24,15 @@ #define SVGCharacterLayoutInfo_h #if ENABLE(SVG) +#include "AffineTransform.h" +#include "SVGRenderStyle.h" +#include "SVGTextContentElement.h" + #include <wtf/Assertions.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> -#include <wtf/Vector.h> - -#include "TransformationMatrix.h" #include <wtf/RefCounted.h> -#include "SVGRenderStyle.h" -#include "SVGTextContentElement.h" +#include <wtf/Vector.h> namespace WebCore { @@ -234,7 +234,7 @@ struct SVGChar { // Helper methods bool isHidden() const; - TransformationMatrix characterTransform() const; + AffineTransform characterTransform() const; }; struct SVGInlineBoxCharacterRange { @@ -275,7 +275,7 @@ struct SVGTextChunk { // textLength & lengthAdjust support float textLength; ELengthAdjust lengthAdjust; - TransformationMatrix ctm; + AffineTransform ctm; // status flags bool isVerticalText : 1; @@ -291,15 +291,19 @@ struct SVGTextChunk { struct SVGTextChunkWalkerBase { virtual ~SVGTextChunkWalkerBase() { } - virtual void operator()(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + virtual void operator()(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) = 0; // Followings methods are only used for painting text chunks virtual void start(InlineBox*) = 0; virtual void end(InlineBox*) = 0; + virtual bool setupBackground(InlineBox*) = 0; virtual bool setupFill(InlineBox*) = 0; + virtual bool setupFillSelection(InlineBox*) = 0; virtual bool setupStroke(InlineBox*) = 0; + virtual bool setupStrokeSelection(InlineBox*) = 0; + virtual bool setupForeground(InlineBox*) = 0; }; template<typename CallbackClass> @@ -307,7 +311,7 @@ struct SVGTextChunkWalker : public SVGTextChunkWalkerBase { public: typedef void (CallbackClass::*SVGTextChunkWalkerCallback)(SVGInlineTextBox* textBox, int startOffset, - const TransformationMatrix& chunkCtm, + const AffineTransform& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end); @@ -315,27 +319,37 @@ public: typedef void (CallbackClass::*SVGTextChunkStartCallback)(InlineBox* box); typedef void (CallbackClass::*SVGTextChunkEndCallback)(InlineBox* box); + typedef bool (CallbackClass::*SVGTextChunkSetupBackgroundCallback)(InlineBox* box); typedef bool (CallbackClass::*SVGTextChunkSetupFillCallback)(InlineBox* box); typedef bool (CallbackClass::*SVGTextChunkSetupStrokeCallback)(InlineBox* box); + typedef bool (CallbackClass::*SVGTextChunkSetupForegroundCallback)(InlineBox* box); SVGTextChunkWalker(CallbackClass* object, SVGTextChunkWalkerCallback walker, SVGTextChunkStartCallback start = 0, SVGTextChunkEndCallback end = 0, + SVGTextChunkSetupBackgroundCallback background = 0, SVGTextChunkSetupFillCallback fill = 0, - SVGTextChunkSetupStrokeCallback stroke = 0) + SVGTextChunkSetupFillCallback fillSelection = 0, + SVGTextChunkSetupStrokeCallback stroke = 0, + SVGTextChunkSetupStrokeCallback strokeSelection = 0, + SVGTextChunkSetupForegroundCallback foreground = 0) : m_object(object) , m_walkerCallback(walker) , m_startCallback(start) , m_endCallback(end) + , m_setupBackgroundCallback(background) , m_setupFillCallback(fill) + , m_setupFillSelectionCallback(fillSelection) , m_setupStrokeCallback(stroke) + , m_setupStrokeSelectionCallback(strokeSelection) + , m_setupForegroundCallback(foreground) { ASSERT(object); ASSERT(walker); } - virtual void operator()(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + virtual void operator()(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { (*m_object.*m_walkerCallback)(textBox, startOffset, chunkCtm, start, end); @@ -358,6 +372,15 @@ public: ASSERT_NOT_REACHED(); } + virtual bool setupBackground(InlineBox* box) + { + if (m_setupBackgroundCallback) + return (*m_object.*m_setupBackgroundCallback)(box); + + ASSERT_NOT_REACHED(); + return false; + } + virtual bool setupFill(InlineBox* box) { if (m_setupFillCallback) @@ -367,6 +390,15 @@ public: return false; } + virtual bool setupFillSelection(InlineBox* box) + { + if (m_setupFillSelectionCallback) + return (*m_object.*m_setupFillSelectionCallback)(box); + + ASSERT_NOT_REACHED(); + return false; + } + virtual bool setupStroke(InlineBox* box) { if (m_setupStrokeCallback) @@ -376,13 +408,35 @@ public: return false; } + virtual bool setupStrokeSelection(InlineBox* box) + { + if (m_setupStrokeSelectionCallback) + return (*m_object.*m_setupStrokeSelectionCallback)(box); + + ASSERT_NOT_REACHED(); + return false; + } + + virtual bool setupForeground(InlineBox* box) + { + if (m_setupForegroundCallback) + return (*m_object.*m_setupForegroundCallback)(box); + + ASSERT_NOT_REACHED(); + return false; + } + private: CallbackClass* m_object; SVGTextChunkWalkerCallback m_walkerCallback; SVGTextChunkStartCallback m_startCallback; SVGTextChunkEndCallback m_endCallback; + SVGTextChunkSetupBackgroundCallback m_setupBackgroundCallback; SVGTextChunkSetupFillCallback m_setupFillCallback; + SVGTextChunkSetupFillCallback m_setupFillSelectionCallback; SVGTextChunkSetupStrokeCallback m_setupStrokeCallback; + SVGTextChunkSetupStrokeCallback m_setupStrokeSelectionCallback; + SVGTextChunkSetupForegroundCallback m_setupForegroundCallback; }; struct SVGTextChunkLayoutInfo { diff --git a/WebCore/rendering/SVGInlineTextBox.cpp b/WebCore/rendering/SVGInlineTextBox.cpp index d0fa9ae..65aa5a1 100644 --- a/WebCore/rendering/SVGInlineTextBox.cpp +++ b/WebCore/rendering/SVGInlineTextBox.cpp @@ -1,6 +1,4 @@ /** - * This file is part of the DOM implementation for KDE. - * * Copyright (C) 2007 Rob Buis <buis@kde.org> * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> * @@ -110,9 +108,7 @@ FloatRect SVGInlineTextBox::calculateGlyphBoundaries(RenderStyle* style, int off FloatRect glyphRect(x1, y1, x2 - x1, y2 - y1); // Take per-character transformations into account - TransformationMatrix ctm = svgChar.characterTransform(); - if (!ctm.isIdentity()) - glyphRect = ctm.mapRect(glyphRect); + glyphRect = svgChar.characterTransform().mapRect(glyphRect); return glyphRect; } @@ -128,7 +124,7 @@ struct SVGInlineTextBoxClosestCharacterToPositionWalker { { } - void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { RenderStyle* style = textBox->textRenderer()->style(); @@ -195,7 +191,7 @@ struct SVGInlineTextBoxSelectionRectWalker { { } - void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { RenderStyle* style = textBox->textRenderer()->style(); @@ -326,7 +322,33 @@ IntRect SVGInlineTextBox::selectionRect(int, int, int startPos, int endPos) return enclosingIntRect(walkerCallback.selectionRect()); } -void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int tx, int ty, const SVGChar& svgChar, const UChar* chars, int length, SVGPaintServer* activePaintServer) +bool SVGInlineTextBox::chunkSelectionStartEnd(const UChar* chunk, int chunkLength, int& selectionStart, int& selectionEnd) +{ + // NOTE: We ignore SVGInlineTextBox::m_start here because it is always 0. + // Curently SVG doesn't use HTML block-level layout, in which m_start would be set. + + int chunkStart = chunk - textRenderer()->characters(); + ASSERT(0 <= chunkStart); + + selectionStartEnd(selectionStart, selectionEnd); + if (selectionEnd <= chunkStart) + return false; + if (chunkStart + chunkLength <= selectionStart) + return false; + + // Map indices from view-global to chunk-local. + selectionStart -= chunkStart; + selectionEnd -= chunkStart; + // Then clamp with chunk range + if (selectionStart < 0) + selectionStart = 0; + if (chunkLength < selectionEnd) + selectionEnd = chunkLength; + + return selectionStart < selectionEnd; +} + +void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int tx, int ty, const SVGChar& svgChar, const UChar* chars, int length, SVGTextPaintInfo& textPaintInfo) { if (renderer()->style()->visibility() != VISIBLE || paintInfo.phase == PaintPhaseOutline) return; @@ -352,13 +374,13 @@ void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int t RenderStyle* styleToUse = text->style(isFirstLineStyle()); const Font& font = styleToUse->font(); - TransformationMatrix ctm = svgChar.characterTransform(); + AffineTransform ctm = svgChar.characterTransform(); if (!ctm.isIdentity()) paintInfo.context->concatCTM(ctm); // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection // and marked text. - if (paintInfo.phase != PaintPhaseSelection && !isPrinting) { + if (paintInfo.phase != PaintPhaseSelection && !isPrinting && textPaintInfo.subphase == SVGTextPaintSubphaseBackground) { #if PLATFORM(MAC) // Custom highlighters go behind everything else. if (styleToUse->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) @@ -378,28 +400,52 @@ void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int t } } - // Set a text shadow if we have one. - // FIXME: Support multiple shadow effects. Need more from the CG API before - // we can do this. - bool setShadow = false; - if (styleToUse->textShadow()) { - paintInfo.context->setShadow(IntSize(styleToUse->textShadow()->x, styleToUse->textShadow()->y), - styleToUse->textShadow()->blur, styleToUse->textShadow()->color); - setShadow = true; - } + bool isGlyphPhase = textPaintInfo.subphase == SVGTextPaintSubphaseGlyphFill || textPaintInfo.subphase == SVGTextPaintSubphaseGlyphStroke; + bool isSelectionGlyphPhase = textPaintInfo.subphase == SVGTextPaintSubphaseGlyphFillSelection || textPaintInfo.subphase == SVGTextPaintSubphaseGlyphStrokeSelection; + + if (isGlyphPhase || isSelectionGlyphPhase) { + // Set a text shadow if we have one. + // FIXME: Support multiple shadow effects. Need more from the CG API before + // we can do this. + bool setShadow = false; + if (styleToUse->textShadow()) { + paintInfo.context->setShadow(IntSize(styleToUse->textShadow()->x, styleToUse->textShadow()->y), + styleToUse->textShadow()->blur, styleToUse->textShadow()->color, + styleToUse->colorSpace()); + setShadow = true; + } - IntPoint origin((int) svgChar.x, (int) svgChar.y); - TextRun run = svgTextRunForInlineTextBox(chars, length, styleToUse, this, svgChar.x); + IntPoint origin((int) svgChar.x, (int) svgChar.y); + TextRun run = svgTextRunForInlineTextBox(chars, length, styleToUse, this, svgChar.x); #if ENABLE(SVG_FONTS) - // SVG Fonts need access to the paint server used to draw the current text chunk. - // They need to be able to call renderPath() on a SVGPaintServer object. - run.setActivePaintServer(activePaintServer); + // SVG Fonts need access to the paint server used to draw the current text chunk. + // They need to be able to call renderPath() on a SVGPaintServer object. + ASSERT(textPaintInfo.activePaintServer); + run.setActivePaintServer(textPaintInfo.activePaintServer); #endif - paintInfo.context->drawText(font, run, origin); + int selectionStart = 0; + int selectionEnd = 0; + bool haveSelectedRange = haveSelection && chunkSelectionStartEnd(chars, length, selectionStart, selectionEnd); + + if (isGlyphPhase) { + if (haveSelectedRange) { + paintInfo.context->drawText(font, run, origin, 0, selectionStart); + paintInfo.context->drawText(font, run, origin, selectionEnd, run.length()); + } else + paintInfo.context->drawText(font, run, origin); + } else { + ASSERT(isSelectionGlyphPhase); + if (haveSelectedRange) + paintInfo.context->drawText(font, run, origin, selectionStart, selectionEnd); + } + + if (setShadow) + paintInfo.context->clearShadow(); + } - if (paintInfo.phase != PaintPhaseSelection) { + if (paintInfo.phase != PaintPhaseSelection && textPaintInfo.subphase == SVGTextPaintSubphaseForeground) { paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, false); if (useCustomUnderlines) { @@ -429,9 +475,6 @@ void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int t } - if (setShadow) - paintInfo.context->clearShadow(); - if (!ctm.isIdentity()) paintInfo.context->concatCTM(ctm.inverse()); } @@ -477,7 +520,7 @@ void SVGInlineTextBox::paintSelection(int boxStartOffset, const SVGChar& svgChar int adjust = startPos >= boxStartOffset ? boxStartOffset : 0; 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); + font.ascent() + font.descent(), color, style->colorSpace(), startPos - adjust, endPos - adjust); p->restore(); } @@ -521,7 +564,7 @@ void SVGInlineTextBox::paintDecoration(ETextDecoration decoration, GraphicsConte context->save(); context->beginPath(); - TransformationMatrix ctm = svgChar.characterTransform(); + AffineTransform ctm = svgChar.characterTransform(); if (!ctm.isIdentity()) context->concatCTM(ctm); diff --git a/WebCore/rendering/SVGInlineTextBox.h b/WebCore/rendering/SVGInlineTextBox.h index 76837cc..596fdf3 100644 --- a/WebCore/rendering/SVGInlineTextBox.h +++ b/WebCore/rendering/SVGInlineTextBox.h @@ -1,6 +1,4 @@ /* - * This file is part of the DOM implementation for KDE. - * * Copyright (C) 2007 Rob Buis <buis@kde.org> * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> * @@ -34,6 +32,22 @@ namespace WebCore { struct SVGChar; struct SVGTextDecorationInfo; + enum SVGTextPaintSubphase { + SVGTextPaintSubphaseBackground, + SVGTextPaintSubphaseGlyphFill, + SVGTextPaintSubphaseGlyphFillSelection, + SVGTextPaintSubphaseGlyphStroke, + SVGTextPaintSubphaseGlyphStrokeSelection, + SVGTextPaintSubphaseForeground + }; + + struct SVGTextPaintInfo { + SVGTextPaintInfo() : activePaintServer(0), subphase(SVGTextPaintSubphaseBackground) {} + + SVGPaintServer* activePaintServer; + SVGTextPaintSubphase subphase; + }; + class SVGInlineTextBox : public InlineTextBox { public: SVGInlineTextBox(RenderObject* obj); @@ -51,7 +65,7 @@ namespace WebCore { virtual IntRect selectionRect(int absx, int absy, int startPos, int endPos); // SVGs custom paint text method - void paintCharacters(RenderObject::PaintInfo&, int tx, int ty, const SVGChar&, const UChar* chars, int length, SVGPaintServer*); + void paintCharacters(RenderObject::PaintInfo&, int tx, int ty, const SVGChar&, const UChar* chars, int length, SVGTextPaintInfo&); // SVGs custom paint selection method void paintSelection(int boxStartOffset, const SVGChar&, const UChar*, int length, GraphicsContext*, RenderStyle*, const Font&); @@ -71,6 +85,7 @@ namespace WebCore { private: friend class RenderSVGInlineText; bool svgCharacterHitsPosition(int x, int y, int& offset) const; + bool chunkSelectionStartEnd(const UChar* chunk, int chunkLength, int& selectionStart, int& selectionEnd); int m_height; }; diff --git a/WebCore/rendering/SVGMarkerData.h b/WebCore/rendering/SVGMarkerData.h new file mode 100644 index 0000000..5ff2993 --- /dev/null +++ b/WebCore/rendering/SVGMarkerData.h @@ -0,0 +1,134 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SVGMarkerData_h +#define SVGMarkerData_h + +#if ENABLE(SVG) +#include "FloatConversion.h" +#include "Path.h" +#include <wtf/MathExtras.h> + +namespace WebCore { + +class SVGResourceMarker; + +class SVGMarkerData { +public: + enum Type { + Unknown = 0, + Start, + Mid, + End + }; + + SVGMarkerData(const Type& type = Unknown, SVGResourceMarker* marker = 0) + : m_type(type) + , m_marker(marker) + { + } + + FloatPoint origin() const { return m_origin; } + SVGResourceMarker* marker() const { return m_marker; } + + float currentAngle() const + { + FloatSize inslopeChange = m_inslopePoints[1] - m_inslopePoints[0]; + FloatSize outslopeChange = m_outslopePoints[1] - m_outslopePoints[0]; + + double inslope = rad2deg(atan2(inslopeChange.height(), inslopeChange.width())); + double outslope = rad2deg(atan2(outslopeChange.height(), outslopeChange.width())); + + double angle = 0; + switch (m_type) { + case Start: + angle = outslope; + break; + case Mid: + angle = (inslope + outslope) / 2; + break; + case End: + angle = inslope; + break; + default: + ASSERT_NOT_REACHED(); + break; + } + + return narrowPrecisionToFloat(angle); + } + + void updateTypeAndMarker(const Type& type, SVGResourceMarker* marker) + { + m_type = type; + m_marker = marker; + } + + void updateOutslope(const FloatPoint& point) + { + m_outslopePoints[0] = m_origin; + m_outslopePoints[1] = point; + } + + void updateMarkerDataForPathElement(const PathElement* element) + { + FloatPoint* points = element->points; + + switch (element->type) { + case PathElementAddQuadCurveToPoint: + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=33115 (PathElementAddQuadCurveToPoint not handled for <marker>) + m_origin = points[1]; + break; + case PathElementAddCurveToPoint: + m_inslopePoints[0] = points[1]; + m_inslopePoints[1] = points[2]; + m_origin = points[2]; + break; + case PathElementMoveToPoint: + m_subpathStart = points[0]; + case PathElementAddLineToPoint: + updateInslope(points[0]); + m_origin = points[0]; + break; + case PathElementCloseSubpath: + updateInslope(points[0]); + m_origin = m_subpathStart; + m_subpathStart = FloatPoint(); + } + } + +private: + void updateInslope(const FloatPoint& point) + { + m_inslopePoints[0] = m_origin; + m_inslopePoints[1] = point; + } + + Type m_type; + SVGResourceMarker* m_marker; + FloatPoint m_origin; + FloatPoint m_subpathStart; + FloatPoint m_inslopePoints[2]; + FloatPoint m_outslopePoints[2]; +}; + +} + +#endif // ENABLE(SVG) +#endif // SVGMarkerData_h diff --git a/WebCore/rendering/SVGMarkerLayoutInfo.cpp b/WebCore/rendering/SVGMarkerLayoutInfo.cpp new file mode 100644 index 0000000..3fe513f --- /dev/null +++ b/WebCore/rendering/SVGMarkerLayoutInfo.cpp @@ -0,0 +1,124 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2008 Rob Buis <buis@kde.org> + 2005, 2007 Eric Seidel <eric@webkit.org> + 2009 Google, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGMarkerLayoutInfo.h" + +#include "RenderSVGViewportContainer.h" +#include "SVGResourceMarker.h" + +namespace WebCore { + +SVGMarkerLayoutInfo::SVGMarkerLayoutInfo() + : m_midMarker(0) + , m_elementIndex(0) + , m_strokeWidth(0) +{ +} + +SVGMarkerLayoutInfo::~SVGMarkerLayoutInfo() +{ +} + +static inline void processStartAndMidMarkers(void* infoPtr, const PathElement* element) +{ + SVGMarkerLayoutInfo& info = *reinterpret_cast<SVGMarkerLayoutInfo*>(infoPtr); + SVGMarkerData& markerData = info.markerData(); + int& elementIndex = info.elementIndex(); + + // First update the outslope for the previous element + markerData.updateOutslope(element->points[0]); + + // Draw the marker for the previous element + SVGResourceMarker* marker = markerData.marker(); + if (elementIndex > 0 && marker) + info.addLayoutedMarker(marker, markerData.origin(), markerData.currentAngle()); + + // Update our marker data for this element + markerData.updateMarkerDataForPathElement(element); + + // After drawing the start marker, switch to drawing mid markers + if (elementIndex == 1) + markerData.updateTypeAndMarker(SVGMarkerData::Mid, info.midMarker()); + + ++elementIndex; +} + +FloatRect SVGMarkerLayoutInfo::calculateBoundaries(SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, SVGResourceMarker* endMarker, float strokeWidth, const Path& path) +{ + m_layout.clear(); + m_midMarker = midMarker; + m_strokeWidth = strokeWidth; + m_elementIndex = 0; + m_markerData = SVGMarkerData(SVGMarkerData::Start, startMarker); + path.apply(this, processStartAndMidMarkers); + + if (endMarker) { + m_markerData.updateTypeAndMarker(SVGMarkerData::End, endMarker); + addLayoutedMarker(endMarker, m_markerData.origin(), m_markerData.currentAngle()); + } + + if (m_layout.isEmpty()) + return FloatRect(); + + Vector<MarkerLayout>::iterator it = m_layout.begin(); + Vector<MarkerLayout>::iterator end = m_layout.end(); + + FloatRect bounds; + for (; it != end; ++it) { + MarkerLayout& layout = *it; + + RenderSVGViewportContainer* markerContent = layout.marker->renderer(); + ASSERT(markerContent); + + bounds.unite(markerContent->markerBoundaries(layout.matrix)); + } + + return bounds; +} + +void SVGMarkerLayoutInfo::drawMarkers(RenderObject::PaintInfo& paintInfo) +{ + if (m_layout.isEmpty()) + return; + + Vector<MarkerLayout>::iterator it = m_layout.begin(); + Vector<MarkerLayout>::iterator end = m_layout.end(); + + for (; it != end; ++it) { + MarkerLayout& layout = *it; + layout.marker->draw(paintInfo, layout.matrix); + } +} + +void SVGMarkerLayoutInfo::addLayoutedMarker(SVGResourceMarker* marker, const FloatPoint& origin, float angle) +{ + ASSERT(marker); + m_layout.append(MarkerLayout(marker, marker->markerTransformation(origin, angle, m_strokeWidth))); +} + +} + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGMarkerLayoutInfo.h b/WebCore/rendering/SVGMarkerLayoutInfo.h new file mode 100644 index 0000000..517c993 --- /dev/null +++ b/WebCore/rendering/SVGMarkerLayoutInfo.h @@ -0,0 +1,74 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SVGMarkerLayoutInfo_h +#define SVGMarkerLayoutInfo_h + +#if ENABLE(SVG) +#include "RenderObject.h" +#include "SVGMarkerData.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class Path; +class SVGResourceMarker; + +struct MarkerLayout { + MarkerLayout(SVGResourceMarker* markerObj = 0, AffineTransform matrixObj = AffineTransform()) + : marker(markerObj) + , matrix(matrixObj) + { + ASSERT(marker); + } + + SVGResourceMarker* marker; + AffineTransform matrix; +}; + +class SVGMarkerLayoutInfo : public Noncopyable { +public: + SVGMarkerLayoutInfo(); + ~SVGMarkerLayoutInfo(); + + FloatRect calculateBoundaries(SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, SVGResourceMarker* endMarker, float strokeWidth, const Path&); + void drawMarkers(RenderObject::PaintInfo&); + + // Used by static inline helper functions in SVGMarkerLayoutInfo.cpp + SVGMarkerData& markerData() { return m_markerData; } + SVGResourceMarker* midMarker() const { return m_midMarker; } + int& elementIndex() { return m_elementIndex; } + void addLayoutedMarker(SVGResourceMarker*, const FloatPoint& origin, float angle); + +private: + SVGResourceMarker* m_midMarker; + + // Used while layouting markers + int m_elementIndex; + SVGMarkerData m_markerData; + float m_strokeWidth; + + // Holds the final computed result + Vector<MarkerLayout> m_layout; +}; + +} + +#endif // ENABLE(SVG) +#endif // SVGMarkerLayoutInfo_h diff --git a/WebCore/rendering/SVGRenderSupport.cpp b/WebCore/rendering/SVGRenderSupport.cpp index fb6866f..079a36e 100644 --- a/WebCore/rendering/SVGRenderSupport.cpp +++ b/WebCore/rendering/SVGRenderSupport.cpp @@ -2,7 +2,8 @@ * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org> * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> * (C) 2007 Eric Seidel <eric@webkit.org> - * Copyright (C) 2009 Google, Inc. All rights reserved. + * (C) 2009 Google, Inc. All rights reserved. + * (C) 2009 Dirk Schulze <krit@webkit.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,6 +27,7 @@ #if ENABLE(SVG) #include "SVGRenderSupport.h" +#include "AffineTransform.h" #include "ImageBuffer.h" #include "RenderObject.h" #include "RenderSVGContainer.h" @@ -36,11 +38,14 @@ #include "SVGStyledElement.h" #include "SVGURIReference.h" #include "TransformState.h" -#include "TransformationMatrix.h" #include <wtf/UnusedParam.h> namespace WebCore { +SVGRenderBase::~SVGRenderBase() +{ +} + IntRect SVGRenderBase::clippedOverflowRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer) { // Return early for any cases where we don't actually paint @@ -56,6 +61,8 @@ IntRect SVGRenderBase::clippedOverflowRectForRepaint(RenderObject* object, Rende void SVGRenderBase::computeRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) { + object->style()->svgStyle()->inflateForShadow(repaintRect); + // Translate to coords in our parent renderer, and then call computeRectForRepaint on our parent repaintRect = object->localToParentTransform().mapRect(repaintRect); object->parent()->computeRectForRepaint(repaintContainer, repaintRect, fixed); @@ -64,12 +71,12 @@ void SVGRenderBase::computeRectForRepaint(RenderObject* object, RenderBoxModelOb void SVGRenderBase::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) { ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree. - ASSERT(useTransforms); // mapping a point through SVG w/o respecting trasnforms is useless. + ASSERT(useTransforms); // Mapping a point through SVG w/o respecting transforms is useless. transformState.applyTransform(object->localToParentTransform()); object->parent()->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); } -void SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, const FloatRect& boundingBox, SVGResourceFilter*& filter, SVGResourceFilter* rootFilter) +bool SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, const FloatRect& repaintRect, SVGResourceFilter*& filter, SVGResourceFilter* rootFilter) { #if !ENABLE(FILTERS) UNUSED_PARAM(filter); @@ -90,10 +97,16 @@ void SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject // Setup transparency layers before setting up filters! float opacity = style->opacity(); if (opacity < 1.0f) { - paintInfo.context->clip(enclosingIntRect(boundingBox)); + paintInfo.context->clip(repaintRect); paintInfo.context->beginTransparencyLayer(opacity); } + if (ShadowData* shadow = svgStyle->shadow()) { + paintInfo.context->clip(repaintRect); + paintInfo.context->setShadow(IntSize(shadow->x, shadow->y), shadow->blur, shadow->color, style->colorSpace()); + paintInfo.context->beginTransparencyLayer(1.0f); + } + #if ENABLE(FILTERS) AtomicString filterId(svgStyle->filter()); #endif @@ -104,7 +117,7 @@ void SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject Document* document = object->document(); #if ENABLE(FILTERS) - SVGResourceFilter* newFilter = getFilterById(document, filterId); + SVGResourceFilter* newFilter = getFilterById(document, filterId, object); if (newFilter == rootFilter) { // Catch <text filter="url(#foo)">Test<tspan filter="url(#foo)">123</tspan></text>. // The filter is NOT meant to be applied twice in that case! @@ -114,28 +127,32 @@ void SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject filter = newFilter; #endif - SVGResourceClipper* clipper = getClipperById(document, clipperId); - SVGResourceMasker* masker = getMaskerById(document, maskerId); + SVGResourceClipper* clipper = getClipperById(document, clipperId, object); + SVGResourceMasker* masker = getMaskerById(document, maskerId, object); + + if (masker) { + masker->addClient(styledElement); + if (!masker->applyMask(paintInfo.context, object)) + return false; + } else if (!maskerId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement); + + if (clipper) { + clipper->addClient(styledElement); + clipper->applyClip(paintInfo.context, object->objectBoundingBox()); + } else if (!clipperId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement); #if ENABLE(FILTERS) if (filter) { filter->addClient(styledElement); - filter->prepareFilter(paintInfo.context, object); + if (!filter->prepareFilter(paintInfo.context, object)) + return false; } else if (!filterId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(filterId, styledElement); #endif - if (clipper) { - clipper->addClient(styledElement); - clipper->applyClip(paintInfo.context, boundingBox); - } else if (!clipperId.isEmpty()) - svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement); - - if (masker) { - masker->addClient(styledElement); - masker->applyMask(paintInfo.context, boundingBox); - } else if (!maskerId.isEmpty()) - svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement); + return true; } void SVGRenderBase::finishRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, SVGResourceFilter*& filter, GraphicsContext* savedContext) @@ -160,6 +177,11 @@ void SVGRenderBase::finishRenderSVGContent(RenderObject* object, RenderObject::P float opacity = style->opacity(); if (opacity < 1.0f) paintInfo.context->endTransparencyLayer(); + + // This needs to be done separately from opacity, because if both properties are set, + // then the transparency layers are nested. + if (style->svgStyle()->shadow()) + paintInfo.context->endTransparencyLayer(); } void renderSubtreeToImage(ImageBuffer* image, RenderObject* item) @@ -215,19 +237,74 @@ FloatRect SVGRenderBase::computeContainerBoundingBox(const RenderObject* contain return boundingBox; } -FloatRect SVGRenderBase::filterBoundingBoxForRenderer(const RenderObject* object) +void SVGRenderBase::layoutChildren(RenderObject* start, bool selfNeedsLayout) +{ + for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { + // Only force our kids to layout if we're being asked to relayout as a result of a parent changing + // FIXME: We should be able to skip relayout of non-relative kids when only bounds size has changed + // that's a possible future optimization using LayoutState + // http://bugs.webkit.org/show_bug.cgi?id=15391 + bool needsLayout = selfNeedsLayout; + if (!needsLayout) { + if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) { + if (element->isStyled()) + needsLayout = static_cast<SVGStyledElement*>(element)->hasRelativeValues(); + } + } + + if (needsLayout) + child->setNeedsLayout(true, false); + + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + } +} + +bool SVGRenderBase::isOverflowHidden(const RenderObject* object) +{ + // SVG doesn't support independent x/y overflow + ASSERT(object->style()->overflowX() == object->style()->overflowY()); + + // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle + ASSERT(object->style()->overflowX() != OSCROLL); + + // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size. + ASSERT(!object->isRoot()); + + return object->style()->overflowX() == OHIDDEN; +} + +FloatRect SVGRenderBase::filterBoundingBoxForRenderer(const RenderObject* object) const { #if ENABLE(FILTERS) - SVGResourceFilter* filter = getFilterById(object->document(), object->style()->svgStyle()->filter()); + SVGResourceFilter* filter = getFilterById(object->document(), object->style()->svgStyle()->filter(), object); if (filter) - return filter->filterBBoxForItemBBox(object->objectBoundingBox()); + return filter->filterBoundingBox(object->objectBoundingBox()); #else UNUSED_PARAM(object); #endif return FloatRect(); } -void applyTransformToPaintInfo(RenderObject::PaintInfo& paintInfo, const TransformationMatrix& localToAncestorTransform) +FloatRect SVGRenderBase::clipperBoundingBoxForRenderer(const RenderObject* object) const +{ + SVGResourceClipper* clipper = getClipperById(object->document(), object->style()->svgStyle()->clipPath(), object); + if (clipper) + return clipper->clipperBoundingBox(object->objectBoundingBox()); + + return FloatRect(); +} + +FloatRect SVGRenderBase::maskerBoundingBoxForRenderer(const RenderObject* object) const +{ + SVGResourceMasker* masker = getMaskerById(object->document(), object->style()->svgStyle()->maskElement(), object); + if (masker) + return masker->maskerBoundingBox(object->objectBoundingBox()); + + return FloatRect(); +} + +void applyTransformToPaintInfo(RenderObject::PaintInfo& paintInfo, const AffineTransform& localToAncestorTransform) { if (localToAncestorTransform.isIdentity()) return; diff --git a/WebCore/rendering/SVGRenderSupport.h b/WebCore/rendering/SVGRenderSupport.h index da2bf59..cf75365 100644 --- a/WebCore/rendering/SVGRenderSupport.h +++ b/WebCore/rendering/SVGRenderSupport.h @@ -38,12 +38,30 @@ namespace WebCore { // all SVG renderers inherit from RenderSVGModelObject. class SVGRenderBase { public: + virtual ~SVGRenderBase(); + + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } + // FIXME: These are only public for SVGRootInlineBox. // It's unclear if these should be exposed or not. SVGRootInlineBox may // pass the wrong RenderObject* and boundingBox to these functions. - static void prepareToRenderSVGContent(RenderObject*, RenderObject::PaintInfo&, const FloatRect& boundingBox, SVGResourceFilter*&, SVGResourceFilter* rootFilter = 0); + static bool prepareToRenderSVGContent(RenderObject*, RenderObject::PaintInfo&, const FloatRect& boundingBox, SVGResourceFilter*&, SVGResourceFilter* rootFilter = 0); static void finishRenderSVGContent(RenderObject*, RenderObject::PaintInfo&, SVGResourceFilter*&, GraphicsContext* savedContext); + // Layout all children of the passed render object + static void layoutChildren(RenderObject*, bool selfNeedsLayout); + + // Helper function determining wheter overflow is hidden + static bool isOverflowHidden(const RenderObject*); + + virtual FloatRect strokeBoundingBox() const { return FloatRect(); } + virtual FloatRect markerBoundingBox() const { return FloatRect(); } + + // returns the bounding box of filter, clipper, marker and masker (or the empty rect if no filter) in local coordinates + FloatRect filterBoundingBoxForRenderer(const RenderObject*) const; + FloatRect clipperBoundingBoxForRenderer(const RenderObject*) const; + FloatRect maskerBoundingBoxForRenderer(const RenderObject*) const; + protected: static IntRect clippedOverflowRectForRepaint(RenderObject*, RenderBoxModelObject* repaintContainer); static void computeRectForRepaint(RenderObject*, RenderBoxModelObject* repaintContainer, IntRect&, bool fixed); @@ -53,14 +71,11 @@ namespace WebCore { // Used to share the "walk all the children" logic between objectBoundingBox // and repaintRectInLocalCoordinates in RenderSVGRoot and RenderSVGContainer static FloatRect computeContainerBoundingBox(const RenderObject* container, bool includeAllPaintedContent); - - // returns the filter bounding box (or the empty rect if no filter) in local coordinates - static FloatRect filterBoundingBoxForRenderer(const RenderObject*); }; // FIXME: This should move to RenderObject or PaintInfo // Used for transforming the GraphicsContext and damage rect before passing PaintInfo to child renderers. - void applyTransformToPaintInfo(RenderObject::PaintInfo&, const TransformationMatrix& localToChildTransform); + void applyTransformToPaintInfo(RenderObject::PaintInfo&, const AffineTransform& localToChildTransform); // This offers a way to render parts of a WebKit rendering tree into a ImageBuffer. void renderSubtreeToImage(ImageBuffer*, RenderObject*); diff --git a/WebCore/rendering/SVGRenderTreeAsText.cpp b/WebCore/rendering/SVGRenderTreeAsText.cpp index 28e506a..aff718f 100644 --- a/WebCore/rendering/SVGRenderTreeAsText.cpp +++ b/WebCore/rendering/SVGRenderTreeAsText.cpp @@ -179,7 +179,7 @@ TextStream& operator<<(TextStream& ts, const FloatSize& s) return ts; } -TextStream& operator<<(TextStream& ts, const TransformationMatrix& transform) +TextStream& operator<<(TextStream& ts, const AffineTransform& transform) { if (transform.isIdentity()) ts << "identity"; @@ -310,7 +310,7 @@ static void writeStyle(TextStream& ts, const RenderObject& object) static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& object) { - ts << " " << object.absoluteTransform().mapRect(object.repaintRectInLocalCoordinates()); + ts << " " << const_cast<RenderObject&>(object).absoluteClippedOverflowRect(); writeStyle(ts, object); return ts; } @@ -521,11 +521,11 @@ void writeRenderResources(TextStream& ts, Node* parent) continue; SVGStyledElement* styled = static_cast<SVGStyledElement*>(svgElement); - RefPtr<SVGResource> resource(styled->canvasResource()); + RefPtr<SVGResource> resource(styled->canvasResource(node->renderer())); if (!resource) continue; - String elementId = svgElement->getAttribute(HTMLNames::idAttr); + String elementId = svgElement->getAttribute(svgElement->idAttributeName()); // FIXME: These names are lies! if (resource->isPaintServer()) { RefPtr<SVGPaintServer> paintServer = WTF::static_pointer_cast<SVGPaintServer>(resource); diff --git a/WebCore/rendering/SVGRenderTreeAsText.h b/WebCore/rendering/SVGRenderTreeAsText.h index bee4f36..13fc475 100644 --- a/WebCore/rendering/SVGRenderTreeAsText.h +++ b/WebCore/rendering/SVGRenderTreeAsText.h @@ -45,7 +45,7 @@ namespace WebCore { class RenderPath; class RenderSVGRoot; class RenderText; - class TransformationMatrix; + class AffineTransform; // functions used by the main RenderTreeAsText code void write(TextStream&, const RenderPath&, int indent); @@ -58,7 +58,7 @@ void writeSVGText(TextStream&, const RenderBlock&, int indent); void writeRenderResources(TextStream&, Node* parent); // helper operators defined used in various classes to dump the render tree. -TextStream& operator<<(TextStream&, const TransformationMatrix&); +TextStream& operator<<(TextStream&, const AffineTransform&); TextStream& operator<<(TextStream&, const IntRect&); TextStream& operator<<(TextStream&, const Color&); TextStream& operator<<(TextStream&, const IntPoint&); diff --git a/WebCore/rendering/SVGRootInlineBox.cpp b/WebCore/rendering/SVGRootInlineBox.cpp index 8319e7c..d0dd4a8 100644 --- a/WebCore/rendering/SVGRootInlineBox.cpp +++ b/WebCore/rendering/SVGRootInlineBox.cpp @@ -363,6 +363,13 @@ struct SVGRootInlineBoxPaintWalker { ASSERT(!m_chunkStarted); } + bool mayHaveSelection(InlineBox* box) const + { + int selectionStart = 0, selectionEnd = 0; + box->renderer()->selectionStartEnd(selectionStart, selectionEnd); + return selectionStart < selectionEnd; + } + void teardownFillPaintServer() { if (!m_fillPaintServer) @@ -429,6 +436,12 @@ struct SVGRootInlineBoxPaintWalker { m_paintInfo.rect = m_savedInfo.rect; } + bool chunkSetupBackgroundCallback(InlineBox* /*box*/) + { + m_textPaintInfo.subphase = SVGTextPaintSubphaseBackground; + return true; + } + bool chunkSetupFillCallback(InlineBox* box) { InlineFlowBox* flowBox = box->parent(); @@ -440,6 +453,7 @@ struct SVGRootInlineBoxPaintWalker { ASSERT(!m_strokePaintServer); teardownFillPaintServer(); + m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphFill; m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object); if (m_fillPaintServer) { m_fillPaintServer->setup(m_paintInfo.context, object, ApplyToFillTargetType, true); @@ -450,6 +464,34 @@ struct SVGRootInlineBoxPaintWalker { return false; } + bool chunkSetupFillSelectionCallback(InlineBox* box) + { + InlineFlowBox* flowBox = box->parent(); + + // Setup fill paint server + RenderObject* object = flowBox->renderer(); + ASSERT(object); + RenderStyle* style = object->getCachedPseudoStyle(SELECTION); + if (!style) + style = object->style(); + + ASSERT(!m_strokePaintServer); + teardownFillPaintServer(); + + if (!mayHaveSelection(box)) + return false; + + m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphFillSelection; + m_fillPaintServer = SVGPaintServer::fillPaintServer(style, object); + if (m_fillPaintServer) { + m_fillPaintServer->setup(m_paintInfo.context, object, style, ApplyToFillTargetType, true); + m_fillPaintServerObject = object; + return true; + } + + return false; + } + bool chunkSetupStrokeCallback(InlineBox* box) { InlineFlowBox* flowBox = box->parent(); @@ -462,6 +504,7 @@ struct SVGRootInlineBoxPaintWalker { teardownFillPaintServer(); teardownStrokePaintServer(); + m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphStroke; m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object); if (m_strokePaintServer) { @@ -473,7 +516,64 @@ struct SVGRootInlineBoxPaintWalker { return false; } - void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + bool chunkSetupStrokeSelectionCallback(InlineBox* box) + { + InlineFlowBox* flowBox = box->parent(); + + // Setup stroke paint server + RenderObject* object = flowBox->renderer(); + ASSERT(object); + RenderStyle* style = object->getCachedPseudoStyle(SELECTION); + if (!style) + style = object->style(); + + // If we're both stroked & filled, teardown fill paint server before stroking. + teardownFillPaintServer(); + teardownStrokePaintServer(); + + if (!mayHaveSelection(box)) + return false; + + m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphStrokeSelection; + m_strokePaintServer = SVGPaintServer::strokePaintServer(style, object); + if (m_strokePaintServer) { + m_strokePaintServer->setup(m_paintInfo.context, object, style, ApplyToStrokeTargetType, true); + m_strokePaintServerObject = object; + return true; + } + + return false; + } + + bool chunkSetupForegroundCallback(InlineBox* /*box*/) + { + teardownFillPaintServer(); + teardownStrokePaintServer(); + + m_textPaintInfo.subphase = SVGTextPaintSubphaseForeground; + + return true; + } + + SVGPaintServer* activePaintServer() const + { + switch (m_textPaintInfo.subphase) { + case SVGTextPaintSubphaseGlyphFill: + case SVGTextPaintSubphaseGlyphFillSelection: + ASSERT(m_fillPaintServer); + return m_fillPaintServer; + case SVGTextPaintSubphaseGlyphStroke: + case SVGTextPaintSubphaseGlyphStrokeSelection: + ASSERT(m_strokePaintServer); + return m_strokePaintServer; + case SVGTextPaintSubphaseBackground: + case SVGTextPaintSubphaseForeground: + default: + return 0; + } + } + + void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { RenderText* text = textBox->textRenderer(); @@ -523,12 +623,8 @@ struct SVGRootInlineBoxPaintWalker { textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); // Paint text - SVGPaintServer* activePaintServer = m_fillPaintServer; - if (!activePaintServer) - activePaintServer = m_strokePaintServer; - - ASSERT(activePaintServer); - textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer); + m_textPaintInfo.activePaintServer = activePaintServer(); + textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, m_textPaintInfo); // Paint decorations, that have to be drawn afterwards if (textDecorations & LINE_THROUGH && textWidth != 0.0f) @@ -561,6 +657,8 @@ private: int m_tx; int m_ty; + + SVGTextPaintInfo m_textPaintInfo; }; void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) @@ -575,18 +673,22 @@ void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) FloatRect boundingBox(tx + x(), ty + y(), width(), height()); // Initialize text rendering - SVGRenderBase::prepareToRenderSVGContent(renderer(), paintInfo, boundingBox, filter); - - // Render text, chunk-by-chunk - SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty); - SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback, - &SVGRootInlineBoxPaintWalker::chunkPortionCallback, - &SVGRootInlineBoxPaintWalker::chunkStartCallback, - &SVGRootInlineBoxPaintWalker::chunkEndCallback, - &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback, - &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback); - - walkTextChunks(&walker); + if (SVGRenderBase::prepareToRenderSVGContent(renderer(), paintInfo, boundingBox, filter)) { + // Render text, chunk-by-chunk + SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty); + SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback, + &SVGRootInlineBoxPaintWalker::chunkPortionCallback, + &SVGRootInlineBoxPaintWalker::chunkStartCallback, + &SVGRootInlineBoxPaintWalker::chunkEndCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupBackgroundCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupFillSelectionCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupStrokeSelectionCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupForegroundCallback); + + walkTextChunks(&walker); + } // Finalize text rendering SVGRenderBase::finishRenderSVGContent(renderer(), paintInfo, filter, savedInfo.context); @@ -831,9 +933,8 @@ static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk) SVGChar& firstChar = *(chunk.start); // Assure we apply the chunk scaling in the right origin - TransformationMatrix newChunkCtm; - newChunkCtm.translate(firstChar.x, firstChar.y); - newChunkCtm = chunk.ctm * newChunkCtm; + AffineTransform newChunkCtm(chunk.ctm); + newChunkCtm.translateRight(firstChar.x, firstChar.y); newChunkCtm.translate(-firstChar.x, -firstChar.y); chunk.ctm = newChunkCtm; @@ -944,7 +1045,6 @@ void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacter Vector<SVGTextChunk>::iterator it = tempChunks.begin(); Vector<SVGTextChunk>::iterator end = tempChunks.end(); - TransformationMatrix ctm; float computedLength = 0.0f; for (; it != end; ++it) { @@ -1087,8 +1187,8 @@ void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>:: start->setWidth(highX - lowX); static_cast<SVGRootInlineBox*>(start)->setHeight(highY - lowY); - start->setVerticalOverflowPositions(top, bottom); - start->setVerticalSelectionPositions(top, bottom); + start->computeVerticalOverflow(top, bottom, true); + static_cast<SVGRootInlineBox*>(start)->setLineTopBottomPositions(top, bottom); } } @@ -1388,7 +1488,7 @@ void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* } else ASSERT(!info.chunk.boxes.isEmpty()); - // Walk string to find out new chunk positions, if existant + // Walk string to find out new chunk positions, if existent for (unsigned i = 0; i < length; ++i) { ASSERT(info.it != svgChars.end()); @@ -1680,11 +1780,24 @@ void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGI if (textBox) (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); else { + if (walker->setupBackground(range.box)) + (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + if (walker->setupFill(range.box)) (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + if (walker->setupFillSelection(range.box)) + (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + if (walker->setupStroke(range.box)) (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + + if (walker->setupStrokeSelection(range.box)) + (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + + if (walker->setupForeground(range.box)) + (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + } chunkOffset += length; diff --git a/WebCore/rendering/SVGRootInlineBox.h b/WebCore/rendering/SVGRootInlineBox.h index 65bade0..7b1dcc4 100644 --- a/WebCore/rendering/SVGRootInlineBox.h +++ b/WebCore/rendering/SVGRootInlineBox.h @@ -28,6 +28,7 @@ #if ENABLE(SVG) #include "RootInlineBox.h" #include "SVGCharacterLayoutInfo.h" +#include "SVGRenderSupport.h" namespace WebCore { @@ -43,13 +44,14 @@ struct LastGlyphInfo { bool isValid; }; -class SVGRootInlineBox : public RootInlineBox { +class SVGRootInlineBox : public RootInlineBox, protected SVGRenderBase { public: SVGRootInlineBox(RenderObject* obj) : RootInlineBox(obj) , m_height(0) { } + virtual const SVGRenderBase* toSVGRenderBase() const { return this; } virtual bool isSVGRootInlineBox() { return true; } @@ -63,6 +65,9 @@ public: virtual void computePerCharacterLayoutInformation(); + virtual FloatRect objectBoundingBox() const { return FloatRect(); } + virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); } + // Used by SVGInlineTextBox const Vector<SVGTextChunk>& svgTextChunks() const; diff --git a/WebCore/rendering/SVGShadowTreeElements.cpp b/WebCore/rendering/SVGShadowTreeElements.cpp new file mode 100644 index 0000000..d9ce640 --- /dev/null +++ b/WebCore/rendering/SVGShadowTreeElements.cpp @@ -0,0 +1,80 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGShadowTreeElements.h" + +#include "Document.h" +#include "FloatSize.h" +#include "RenderObject.h" +#include "SVGNames.h" + +namespace WebCore { + +// SVGShadowTreeContainerElement +SVGShadowTreeContainerElement::SVGShadowTreeContainerElement(Document* document) + : SVGGElement(SVGNames::gTag, document) +{ +} + +SVGShadowTreeContainerElement::~SVGShadowTreeContainerElement() +{ +} + +FloatSize SVGShadowTreeContainerElement::containerTranslation() const +{ + return FloatSize(m_xOffset.value(this), m_yOffset.value(this)); +} + +// SVGShadowTreeRootElement +SVGShadowTreeRootElement::SVGShadowTreeRootElement(Document* document, Node* shadowParent) + : SVGShadowTreeContainerElement(document) + , m_shadowParent(shadowParent) +{ + setInDocument(true); +} + +SVGShadowTreeRootElement::~SVGShadowTreeRootElement() +{ +} + +void SVGShadowTreeRootElement::attachElement(PassRefPtr<RenderStyle> style, RenderArena* arena) +{ + ASSERT(m_shadowParent); + + // Create the renderer with the specified style + RenderObject* renderer = createRenderer(arena, style.get()); + if (renderer) { + setRenderer(renderer); + renderer->setStyle(style); + } + + // Set these explicitly since this normally happens during an attach() + setAttached(); + + // Add the renderer to the render tree + if (renderer) + m_shadowParent->renderer()->addChild(renderer); +} + +} + +#endif diff --git a/WebCore/rendering/SVGShadowTreeElements.h b/WebCore/rendering/SVGShadowTreeElements.h new file mode 100644 index 0000000..ed42e89 --- /dev/null +++ b/WebCore/rendering/SVGShadowTreeElements.h @@ -0,0 +1,67 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SVGShadowTreeElements_h +#define SVGShadowTreeElements_h + +#if ENABLE(SVG) +#include "SVGGElement.h" +#include "SVGLength.h" + +namespace WebCore { + +class FloatSize; + +class SVGShadowTreeContainerElement : public SVGGElement { +public: + SVGShadowTreeContainerElement(Document*); + virtual ~SVGShadowTreeContainerElement(); + + virtual bool isShadowTreeContainerElement() const { return true; } + + FloatSize containerTranslation() const; + void setContainerOffset(const SVGLength& x, const SVGLength& y) + { + m_xOffset = x; + m_yOffset = y; + } + +private: + SVGLength m_xOffset; + SVGLength m_yOffset; +}; + +class SVGShadowTreeRootElement : public SVGShadowTreeContainerElement { +public: + SVGShadowTreeRootElement(Document*, Node* shadowParent); + virtual ~SVGShadowTreeRootElement(); + + virtual bool isShadowNode() const { return m_shadowParent; } + virtual Node* shadowParentNode() { return m_shadowParent; } + + void attachElement(PassRefPtr<RenderStyle>, RenderArena*); + +private: + Node* m_shadowParent; +}; + +} + +#endif +#endif diff --git a/WebCore/rendering/TableLayout.h b/WebCore/rendering/TableLayout.h index 8ae0ce7..10d6e26 100644 --- a/WebCore/rendering/TableLayout.h +++ b/WebCore/rendering/TableLayout.h @@ -1,6 +1,4 @@ /* - * This file is part of the HTML rendering engine for KDE. - * * Copyright (C) 2002 Lars Knoll (knoll@kde.org) * (C) 2002 Dirk Mueller (mueller@kde.org) * @@ -23,11 +21,13 @@ #ifndef TableLayout_h #define TableLayout_h +#include <wtf/Noncopyable.h> + namespace WebCore { class RenderTable; -class TableLayout { +class TableLayout : public Noncopyable { public: TableLayout(RenderTable* table) : m_table(table) diff --git a/WebCore/rendering/TrailingFloatsRootInlineBox.h b/WebCore/rendering/TrailingFloatsRootInlineBox.h new file mode 100644 index 0000000..68bf637 --- /dev/null +++ b/WebCore/rendering/TrailingFloatsRootInlineBox.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TrailingFloatsRootInlineBox_h +#define TrailingFloatsRootInlineBox_h + +#include "RootInlineBox.h" + +namespace WebCore { + +class TrailingFloatsRootInlineBox : public RootInlineBox { +public: + TrailingFloatsRootInlineBox(RenderObject* object) : RootInlineBox(object) + { +#if ENABLE(SVG) + setHasVirtualHeight(); +#endif + } + +private: + virtual int virtualHeight() const { return 0; } +}; + +} // namespace WebCore + +#endif // TrailingFloatsRootInlineBox_h diff --git a/WebCore/rendering/TransformState.cpp b/WebCore/rendering/TransformState.cpp index a9e68f4..ecc614e 100644 --- a/WebCore/rendering/TransformState.cpp +++ b/WebCore/rendering/TransformState.cpp @@ -49,6 +49,12 @@ void TransformState::move(int x, int y, TransformAccumulation accumulate) m_accumulatingTransform = accumulate == AccumulateTransform; } +// FIXME: We transform AffineTransform to TransformationMatrix. This is rather inefficient. +void TransformState::applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation accumulate) +{ + applyTransform(transformFromContainer.toTransformationMatrix(), accumulate); +} + void TransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate) { // If we have an accumulated transform from last time, multiply in this transform @@ -115,7 +121,7 @@ void TransformState::flattenWithTransform(const TransformationMatrix& t) } // We could throw away m_accumulatedTransform if we wanted to here, but that - // would cause thrash when traversing hierarachies with alternating + // would cause thrash when traversing hierarchies with alternating // preserve-3d and flat elements. if (m_accumulatedTransform) m_accumulatedTransform->makeIdentity(); diff --git a/WebCore/rendering/TransformState.h b/WebCore/rendering/TransformState.h index d2c962a..0b4ca46 100644 --- a/WebCore/rendering/TransformState.h +++ b/WebCore/rendering/TransformState.h @@ -26,6 +26,7 @@ #ifndef TransformState_h #define TransformState_h +#include "AffineTransform.h" #include "FloatPoint.h" #include "FloatQuad.h" #include "IntSize.h" @@ -59,6 +60,7 @@ public: } void move(int x, int y, TransformAccumulation = FlattenTransform); + void applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation = FlattenTransform); void applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation = FlattenTransform); void flatten(); diff --git a/WebCore/rendering/break_lines.cpp b/WebCore/rendering/break_lines.cpp index 0e81caa..4b6c0aa 100644 --- a/WebCore/rendering/break_lines.cpp +++ b/WebCore/rendering/break_lines.cpp @@ -1,21 +1,26 @@ /* - * Copyright (C) 2005, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2007, 2010 Apple Inc. All rights reserved. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -44,23 +49,40 @@ static inline bool isBreakableSpace(UChar ch, bool treatNoBreakSpaceAsBreak) } } -static inline bool shouldBreakAfter(UChar ch) +// This differs from the Unicode algorithm only in that Unicode does not break +// between a question mark and a vertical line (U+007C). +static const unsigned char internetExplorerLineBreaksAfterQuestionMarkTable[0x80] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, // \t + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, // ! " ' ) , . / + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, // : ; ? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, // ] + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1 // } +}; + +static const size_t internetExplorerLineBreaksAfterQuestionMarkTableSize = sizeof(internetExplorerLineBreaksAfterQuestionMarkTable) / sizeof(*internetExplorerLineBreaksAfterQuestionMarkTable); + +static inline bool shouldBreakAfter(UChar ch, UChar nextCh) { - // Match WinIE's breaking strategy, which is to always allow breaks after hyphens and question marks. - // FIXME: it appears that IE behavior is more complex, see <http://bugs.webkit.org/show_bug.cgi?id=17475>. switch (ch) { - case '-': + // For a question mark preceding a non-ASCII characters, defer to the Unicode algorithm by returning false. + // For ASCII characters, use a lookup table for enhanced speed and for compatibility with Internet Explorer. case '?': -#ifdef ANDROID_LAYOUT - // as '/' is used in uri which is always long, we would like to break it - case '/': -#endif + return nextCh < internetExplorerLineBreaksAfterQuestionMarkTableSize && internetExplorerLineBreaksAfterQuestionMarkTable[nextCh]; + // Internet Explorer always allows breaking after a hyphen. + case '-': case softHyphen: // FIXME: cases for ideographicComma and ideographicFullStop are a workaround for an issue in Unicode 5.0 // which is likely to be resolved in Unicode 5.1 <http://bugs.webkit.org/show_bug.cgi?id=17411>. // We may want to remove or conditionalize this workaround at some point. case ideographicComma: case ideographicFullStop: +#ifdef ANDROID_LAYOUT + // as '/' is used in uri which is always long, we would like to break it + case '/': +#endif return true; default: return false; @@ -92,7 +114,7 @@ int nextBreakablePosition(const UChar* str, int pos, int len, bool treatNoBreakS for (int i = pos; i < len; i++) { UChar ch = str[i]; - if (isBreakableSpace(ch, treatNoBreakSpaceAsBreak) || shouldBreakAfter(lastCh)) + if (isBreakableSpace(ch, treatNoBreakSpaceAsBreak) || shouldBreakAfter(lastCh, ch)) return i; if (needsLineBreakIterator(ch) || needsLineBreakIterator(lastCh)) { diff --git a/WebCore/rendering/break_lines.h b/WebCore/rendering/break_lines.h index 14f740f..4d6b8dc 100644 --- a/WebCore/rendering/break_lines.h +++ b/WebCore/rendering/break_lines.h @@ -1,6 +1,4 @@ /* - * This file is part of the DOM implementation for KDE. - * * Copyright (C) 2005 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or diff --git a/WebCore/rendering/style/ContentData.h b/WebCore/rendering/style/ContentData.h index 24d5f86..2c261f8 100644 --- a/WebCore/rendering/style/ContentData.h +++ b/WebCore/rendering/style/ContentData.h @@ -25,15 +25,15 @@ #ifndef ContentData_h #define ContentData_h -#include "PlatformString.h" #include "RenderStyleConstants.h" -#include "StringImpl.h" -#include "StyleImage.h" #include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> namespace WebCore { class CounterContent; +class StringImpl; +class StyleImage; struct ContentData : Noncopyable { public: diff --git a/WebCore/rendering/style/CounterContent.h b/WebCore/rendering/style/CounterContent.h index cf11813..702d9c2 100644 --- a/WebCore/rendering/style/CounterContent.h +++ b/WebCore/rendering/style/CounterContent.h @@ -30,7 +30,7 @@ namespace WebCore { -class CounterContent { +class CounterContent : public FastAllocBase { public: CounterContent(const AtomicString& identifier, EListStyleType style, const AtomicString& separator) : m_identifier(identifier) diff --git a/WebCore/rendering/style/FillLayer.cpp b/WebCore/rendering/style/FillLayer.cpp index 9c491aa..597e919 100644 --- a/WebCore/rendering/style/FillLayer.cpp +++ b/WebCore/rendering/style/FillLayer.cpp @@ -31,18 +31,20 @@ FillLayer::FillLayer(EFillLayerType type) , m_attachment(FillLayer::initialFillAttachment(type)) , m_clip(FillLayer::initialFillClip(type)) , m_origin(FillLayer::initialFillOrigin(type)) - , m_repeat(FillLayer::initialFillRepeat(type)) + , m_repeatX(FillLayer::initialFillRepeatX(type)) + , m_repeatY(FillLayer::initialFillRepeatY(type)) , m_composite(FillLayer::initialFillComposite(type)) - , m_size(FillLayer::initialFillSize(type)) + , m_sizeType(SizeNone) + , m_sizeLength(FillLayer::initialFillSizeLength(type)) , m_imageSet(false) , m_attachmentSet(false) , m_clipSet(false) , m_originSet(false) - , m_repeatSet(false) + , m_repeatXSet(false) + , m_repeatYSet(false) , m_xPosSet(false) , m_yPosSet(false) , m_compositeSet(type == MaskFillLayer) - , m_sizeSet(false) , m_type(type) , m_next(0) { @@ -55,18 +57,20 @@ FillLayer::FillLayer(const FillLayer& o) , m_attachment(o.m_attachment) , m_clip(o.m_clip) , m_origin(o.m_origin) - , m_repeat(o.m_repeat) + , m_repeatX(o.m_repeatX) + , m_repeatY(o.m_repeatY) , m_composite(o.m_composite) - , m_size(o.m_size) + , m_sizeType(o.m_sizeType) + , m_sizeLength(o.m_sizeLength) , m_imageSet(o.m_imageSet) , m_attachmentSet(o.m_attachmentSet) , m_clipSet(o.m_clipSet) , m_originSet(o.m_originSet) - , m_repeatSet(o.m_repeatSet) + , m_repeatXSet(o.m_repeatXSet) + , m_repeatYSet(o.m_repeatYSet) , m_xPosSet(o.m_xPosSet) , m_yPosSet(o.m_yPosSet) , m_compositeSet(o.m_compositeSet) - , m_sizeSet(o.m_sizeSet) , m_type(o.m_type) , m_next(o.m_next ? new FillLayer(*o.m_next) : 0) { @@ -91,18 +95,20 @@ FillLayer& FillLayer::operator=(const FillLayer& o) m_clip = o.m_clip; m_composite = o.m_composite; m_origin = o.m_origin; - m_repeat = o.m_repeat; - m_size = o.m_size; + m_repeatX = o.m_repeatX; + m_repeatY = o.m_repeatY; + m_sizeType = o.m_sizeType; + m_sizeLength = o.m_sizeLength; m_imageSet = o.m_imageSet; m_attachmentSet = o.m_attachmentSet; m_clipSet = o.m_clipSet; m_compositeSet = o.m_compositeSet; m_originSet = o.m_originSet; - m_repeatSet = o.m_repeatSet; + m_repeatXSet = o.m_repeatXSet; + m_repeatYSet = o.m_repeatYSet; m_xPosSet = o.m_xPosSet; m_yPosSet = o.m_yPosSet; - m_sizeSet = o.m_sizeSet; m_type = o.m_type; @@ -115,9 +121,9 @@ bool FillLayer::operator==(const FillLayer& o) const // to propagate patterns into layers. All layer comparisons happen after values have all been filled in anyway. return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition && m_attachment == o.m_attachment && m_clip == o.m_clip && - m_composite == o.m_composite && m_origin == o.m_origin && m_repeat == o.m_repeat && - m_size == o.m_size && m_type == o.m_type && - ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next); + m_composite == o.m_composite && m_origin == o.m_origin && m_repeatX == o.m_repeatX && + m_repeatY == o.m_repeatY && m_sizeType == o.m_sizeType && m_sizeLength == o.m_sizeLength && + m_type == o.m_type && ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next); } void FillLayer::fillUnsetProperties() @@ -200,11 +206,22 @@ void FillLayer::fillUnsetProperties() } } - for (curr = this; curr && curr->isRepeatSet(); curr = curr->next()) { } + for (curr = this; curr && curr->isRepeatXSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_repeat = pattern->m_repeat; + curr->m_repeatX = pattern->m_repeatX; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + + for (curr = this; curr && curr->isRepeatYSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_repeatY = pattern->m_repeatY; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; @@ -215,7 +232,8 @@ void FillLayer::fillUnsetProperties() if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_size = pattern->m_size; + curr->m_sizeType = pattern->m_sizeType; + curr->m_sizeLength = pattern->m_sizeLength; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; @@ -232,7 +250,8 @@ void FillLayer::cullEmptyLayers() !next->isXPositionSet() && !next->isYPositionSet() && !next->isAttachmentSet() && !next->isClipSet() && !next->isCompositeSet() && !next->isOriginSet() && - !next->isRepeatSet() && !next->isSizeSet()) { + !next->isRepeatXSet() && !next->isRepeatYSet() + && !next->isSizeSet()) { delete next; p->m_next = 0; break; @@ -251,4 +270,15 @@ bool FillLayer::containsImage(StyleImage* s) const return false; } +bool FillLayer::imagesAreLoaded() const +{ + const FillLayer* curr; + for (curr = this; curr; curr = curr->next()) { + if (curr->m_image && !curr->m_image->isLoaded()) + return false; + } + + return true; +} + } // namespace WebCore diff --git a/WebCore/rendering/style/FillLayer.h b/WebCore/rendering/style/FillLayer.h index c3944ad..cef6b19 100644 --- a/WebCore/rendering/style/FillLayer.h +++ b/WebCore/rendering/style/FillLayer.h @@ -34,7 +34,32 @@ namespace WebCore { -struct FillLayer { +struct FillSize { + FillSize() + : type(SizeLength) + { + } + + FillSize(EFillSizeType t, LengthSize l) + : type(t) + , size(l) + { + } + + bool operator==(const FillSize& o) const + { + return type == o.type && size == o.size; + } + bool operator!=(const FillSize& o) const + { + return !(*this == o); + } + + EFillSizeType type; + LengthSize size; +}; + +struct FillLayer : FastAllocBase { public: FillLayer(EFillLayerType); ~FillLayer(); @@ -45,9 +70,11 @@ public: EFillAttachment attachment() const { return static_cast<EFillAttachment>(m_attachment); } EFillBox clip() const { return static_cast<EFillBox>(m_clip); } EFillBox origin() const { return static_cast<EFillBox>(m_origin); } - EFillRepeat repeat() const { return static_cast<EFillRepeat>(m_repeat); } + EFillRepeat repeatX() const { return static_cast<EFillRepeat>(m_repeatX); } + EFillRepeat repeatY() const { return static_cast<EFillRepeat>(m_repeatY); } CompositeOperator composite() const { return static_cast<CompositeOperator>(m_composite); } - LengthSize size() const { return m_size; } + LengthSize sizeLength() const { return m_sizeLength; } + FillSize size() const { return FillSize(static_cast<EFillSizeType>(m_sizeType), m_sizeLength); } const FillLayer* next() const { return m_next; } FillLayer* next() { return m_next; } @@ -58,19 +85,23 @@ public: bool isAttachmentSet() const { return m_attachmentSet; } bool isClipSet() const { return m_clipSet; } bool isOriginSet() const { return m_originSet; } - bool isRepeatSet() const { return m_repeatSet; } + bool isRepeatXSet() const { return m_repeatXSet; } + bool isRepeatYSet() const { return m_repeatYSet; } bool isCompositeSet() const { return m_compositeSet; } - bool isSizeSet() const { return m_sizeSet; } + bool isSizeSet() const { return m_sizeType != SizeNone; } void setImage(StyleImage* i) { m_image = i; m_imageSet = true; } - void setXPosition(const Length& l) { m_xPosition = l; m_xPosSet = true; } - void setYPosition(const Length& l) { m_yPosition = l; m_yPosSet = true; } + void setXPosition(Length l) { m_xPosition = l; m_xPosSet = true; } + void setYPosition(Length l) { m_yPosition = l; m_yPosSet = true; } void setAttachment(EFillAttachment attachment) { m_attachment = attachment; m_attachmentSet = true; } void setClip(EFillBox b) { m_clip = b; m_clipSet = true; } void setOrigin(EFillBox b) { m_origin = b; m_originSet = true; } - void setRepeat(EFillRepeat r) { m_repeat = r; m_repeatSet = true; } + void setRepeatX(EFillRepeat r) { m_repeatX = r; m_repeatXSet = true; } + void setRepeatY(EFillRepeat r) { m_repeatY = r; m_repeatYSet = true; } void setComposite(CompositeOperator c) { m_composite = c; m_compositeSet = true; } - void setSize(const LengthSize& b) { m_size = b; m_sizeSet = true; } + void setSizeType(EFillSizeType b) { m_sizeType = b; } + void setSizeLength(LengthSize l) { m_sizeLength = l; } + void setSize(FillSize f) { m_sizeType = f.type; m_sizeLength = f.size; } void clearImage() { m_imageSet = false; } void clearXPosition() { m_xPosSet = false; } @@ -78,9 +109,10 @@ public: void clearAttachment() { m_attachmentSet = false; } void clearClip() { m_clipSet = false; } void clearOrigin() { m_originSet = false; } - void clearRepeat() { m_repeatSet = false; } + void clearRepeatX() { m_repeatXSet = false; } + void clearRepeatY() { m_repeatYSet = false; } void clearComposite() { m_compositeSet = false; } - void clearSize() { m_sizeSet = false; } + void clearSize() { m_sizeType = SizeNone; } void setNext(FillLayer* n) { if (m_next != n) { delete m_next; m_next = n; } } @@ -94,6 +126,7 @@ public: } bool containsImage(StyleImage*) const; + bool imagesAreLoaded() const; bool hasImage() const { @@ -117,9 +150,12 @@ public: static EFillAttachment initialFillAttachment(EFillLayerType) { return ScrollBackgroundAttachment; } static EFillBox initialFillClip(EFillLayerType) { return BorderFillBox; } static EFillBox initialFillOrigin(EFillLayerType type) { return type == BackgroundFillLayer ? PaddingFillBox : BorderFillBox; } - static EFillRepeat initialFillRepeat(EFillLayerType) { return RepeatFill; } + static EFillRepeat initialFillRepeatX(EFillLayerType) { return RepeatFill; } + static EFillRepeat initialFillRepeatY(EFillLayerType) { return RepeatFill; } static CompositeOperator initialFillComposite(EFillLayerType) { return CompositeSourceOver; } - static LengthSize initialFillSize(EFillLayerType) { return LengthSize(); } + static EFillSizeType initialFillSizeType(EFillLayerType) { return SizeLength; } + static LengthSize initialFillSizeLength(EFillLayerType) { return LengthSize(); } + static FillSize initialFillSize(EFillLayerType) { return FillSize(); } static Length initialFillXPosition(EFillLayerType) { return Length(0.0, Percent); } static Length initialFillYPosition(EFillLayerType) { return Length(0.0, Percent); } static StyleImage* initialFillImage(EFillLayerType) { return 0; } @@ -136,20 +172,22 @@ public: unsigned m_attachment : 2; // EFillAttachment unsigned m_clip : 2; // EFillBox unsigned m_origin : 2; // EFillBox - unsigned m_repeat : 2; // EFillRepeat + unsigned m_repeatX : 3; // EFillRepeat + unsigned m_repeatY : 3; // EFillRepeat unsigned m_composite : 4; // CompositeOperator - - LengthSize m_size; + unsigned m_sizeType : 2; // EFillSizeType + + LengthSize m_sizeLength; bool m_imageSet : 1; bool m_attachmentSet : 1; bool m_clipSet : 1; bool m_originSet : 1; - bool m_repeatSet : 1; + bool m_repeatXSet : 1; + bool m_repeatYSet : 1; bool m_xPosSet : 1; bool m_yPosSet : 1; bool m_compositeSet : 1; - bool m_sizeSet : 1; unsigned m_type : 1; // EFillLayerType diff --git a/WebCore/rendering/style/LineClampValue.h b/WebCore/rendering/style/LineClampValue.h new file mode 100644 index 0000000..2119ca2 --- /dev/null +++ b/WebCore/rendering/style/LineClampValue.h @@ -0,0 +1,69 @@ +/* + * 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LineClampValue_h +#define LineClampValue_h + +#include "RenderStyleConstants.h" + +namespace WebCore { + +class LineClampValue { +public: + LineClampValue() + : m_type(LineClampLineCount) + , m_value(-1) + { + } + + LineClampValue(int value, ELineClampType type) + : m_type(type) + , m_value(value) + { + } + + int value() const { return m_value; } + + bool isPercentage() const { return m_type == LineClampPercentage; } + + bool isNone() const { return m_value == -1; } + + bool operator==(const LineClampValue& o) const + { + return value() == o.value() && isPercentage() == o.isPercentage(); + } + + bool operator!=(const LineClampValue& o) const + { + return !(*this == o); + } + +private: + ELineClampType m_type; + int m_value; +}; + +} // namespace WebCore + +#endif // LineClampValue_h diff --git a/WebCore/rendering/style/RenderStyle.cpp b/WebCore/rendering/style/RenderStyle.cpp index 0e258c8..0952557 100644 --- a/WebCore/rendering/style/RenderStyle.cpp +++ b/WebCore/rendering/style/RenderStyle.cpp @@ -324,6 +324,7 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon *rareNonInheritedData->flexibleBox.get() != *other->rareNonInheritedData->flexibleBox.get()) return StyleDifferenceLayout; + // FIXME: We should add an optimized form of layout that just recomputes visual overflow. if (!rareNonInheritedData->shadowDataEquivalent(*other->rareNonInheritedData.get())) return StyleDifferenceLayout; @@ -453,6 +454,12 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon if (inherited->m_effectiveZoom != other->inherited->m_effectiveZoom) return StyleDifferenceLayout; + if ((rareNonInheritedData->opacity == 1 && other->rareNonInheritedData->opacity < 1) || + (rareNonInheritedData->opacity < 1 && other->rareNonInheritedData->opacity == 1)) { + // FIXME: We should add an optimized form of layout that just recomputes visual overflow. + return StyleDifferenceLayout; + } + // Make sure these left/top/right/bottom checks stay below all layout checks and above // all visible checks. if (position() != StaticPosition) { @@ -695,7 +702,7 @@ void RenderStyle::addBindingURI(StringImpl* uri) void RenderStyle::setTextShadow(ShadowData* val, bool add) { - ASSERT(!val || !val->spread && val->style == Normal); + ASSERT(!val || (!val->spread && val->style == Normal)); StyleRareInheritedData* rareData = rareInheritedData.access(); if (!add) { diff --git a/WebCore/rendering/style/RenderStyle.h b/WebCore/rendering/style/RenderStyle.h index 720bc8d..696a2b7 100644 --- a/WebCore/rendering/style/RenderStyle.h +++ b/WebCore/rendering/style/RenderStyle.h @@ -37,6 +37,7 @@ #include "CachedImage.h" #include "CollapsedBorderValue.h" #include "Color.h" +#include "ColorSpace.h" #include "ContentData.h" #include "CounterDirectives.h" #include "CursorList.h" @@ -49,6 +50,7 @@ #include "Length.h" #include "LengthBox.h" #include "LengthSize.h" +#include "LineClampValue.h" #include "NinePieceImage.h" #include "OutlineValue.h" #include "Pair.h" @@ -87,7 +89,11 @@ #include "BindingURI.h" #endif +#if COMPILER(WINSCW) +#define compareEqual(t, u) ((t) == (u)) +#else template<typename T, typename U> inline bool compareEqual(const T& t, const U& u) { return t == static_cast<T>(u); } +#endif #define SET_VAR(group, variable, value) \ if (!compareEqual(group->variable, value)) \ @@ -177,7 +183,7 @@ protected: unsigned _empty_cells : 1; // EEmptyCell unsigned _caption_side : 2; // ECaptionSide - unsigned _list_style_type : 5 ; // EListStyleType + unsigned _list_style_type : 7; // EListStyleType unsigned _list_style_position : 1; // EListStylePosition unsigned _visibility : 2; // EVisibility unsigned _text_align : 3; // ETextAlign @@ -188,37 +194,38 @@ protected: bool _border_collapse : 1 ; unsigned _white_space : 3; // EWhiteSpace unsigned _box_direction : 1; // EBoxDirection (CSS3 box_direction property, flexible box layout module) - // 32 bits + // 34 bits // non CSS2 inherited bool _visuallyOrdered : 1; bool _htmlHacks : 1; bool _force_backgrounds_to_white : 1; unsigned _pointerEvents : 4; // EPointerEvents - // 39 bits + // 41 bits } inherited_flags; // don't inherit struct NonInheritedFlags { bool operator==(const NonInheritedFlags& other) const { - return (_effectiveDisplay == other._effectiveDisplay) && - (_originalDisplay == other._originalDisplay) && - (_overflowX == other._overflowX) && - (_overflowY == other._overflowY) && - (_vertical_align == other._vertical_align) && - (_clear == other._clear) && - (_position == other._position) && - (_floating == other._floating) && - (_table_layout == other._table_layout) && - (_page_break_before == other._page_break_before) && - (_page_break_after == other._page_break_after) && - (_styleType == other._styleType) && - (_affectedByHover == other._affectedByHover) && - (_affectedByActive == other._affectedByActive) && - (_affectedByDrag == other._affectedByDrag) && - (_pseudoBits == other._pseudoBits) && - (_unicodeBidi == other._unicodeBidi); + return _effectiveDisplay == other._effectiveDisplay + && _originalDisplay == other._originalDisplay + && _overflowX == other._overflowX + && _overflowY == other._overflowY + && _vertical_align == other._vertical_align + && _clear == other._clear + && _position == other._position + && _floating == other._floating + && _table_layout == other._table_layout + && _page_break_before == other._page_break_before + && _page_break_after == other._page_break_after + && _page_break_inside == other._page_break_inside + && _styleType == other._styleType + && _affectedByHover == other._affectedByHover + && _affectedByActive == other._affectedByActive + && _affectedByDrag == other._affectedByDrag + && _pseudoBits == other._pseudoBits + && _unicodeBidi == other._unicodeBidi; } bool operator!=(const NonInheritedFlags& other) const { return !(*this == other); } @@ -235,6 +242,7 @@ protected: unsigned _page_break_before : 2; // EPageBreak unsigned _page_break_after : 2; // EPageBreak + unsigned _page_break_inside : 2; // EPageBreak unsigned _styleType : 5; // PseudoId bool _affectedByHover : 1; @@ -242,7 +250,7 @@ protected: bool _affectedByDrag : 1; unsigned _pseudoBits : 7; unsigned _unicodeBidi : 2; // EUnicodeBidi - // 48 bits + // 50 bits } noninherited_flags; // !END SYNC! @@ -278,6 +286,7 @@ protected: noninherited_flags._table_layout = initialTableLayout(); noninherited_flags._page_break_before = initialPageBreak(); noninherited_flags._page_break_after = initialPageBreak(); + noninherited_flags._page_break_inside = initialPageBreak(); noninherited_flags._styleType = NOPSEUDO; noninherited_flags._affectedByHover = false; noninherited_flags._affectedByActive = false; @@ -521,26 +530,29 @@ public: const Color& backgroundColor() const { return background->m_color; } StyleImage* backgroundImage() const { return background->m_background.m_image.get(); } - EFillRepeat backgroundRepeat() const { return static_cast<EFillRepeat>(background->m_background.m_repeat); } + EFillRepeat backgroundRepeatX() const { return static_cast<EFillRepeat>(background->m_background.m_repeatX); } + EFillRepeat backgroundRepeatY() const { return static_cast<EFillRepeat>(background->m_background.m_repeatY); } CompositeOperator backgroundComposite() const { return static_cast<CompositeOperator>(background->m_background.m_composite); } EFillAttachment backgroundAttachment() const { return static_cast<EFillAttachment>(background->m_background.m_attachment); } EFillBox backgroundClip() const { return static_cast<EFillBox>(background->m_background.m_clip); } EFillBox backgroundOrigin() const { return static_cast<EFillBox>(background->m_background.m_origin); } Length backgroundXPosition() const { return background->m_background.m_xPosition; } Length backgroundYPosition() const { return background->m_background.m_yPosition; } - LengthSize backgroundSize() const { return background->m_background.m_size; } + EFillSizeType backgroundSizeType() const { return static_cast<EFillSizeType>(background->m_background.m_sizeType); } + LengthSize backgroundSizeLength() const { return background->m_background.m_sizeLength; } FillLayer* accessBackgroundLayers() { return &(background.access()->m_background); } const FillLayer* backgroundLayers() const { return &(background->m_background); } StyleImage* maskImage() const { return rareNonInheritedData->m_mask.m_image.get(); } - EFillRepeat maskRepeat() const { return static_cast<EFillRepeat>(rareNonInheritedData->m_mask.m_repeat); } + EFillRepeat maskRepeatX() const { return static_cast<EFillRepeat>(rareNonInheritedData->m_mask.m_repeatX); } + EFillRepeat maskRepeatY() const { return static_cast<EFillRepeat>(rareNonInheritedData->m_mask.m_repeatY); } CompositeOperator maskComposite() const { return static_cast<CompositeOperator>(rareNonInheritedData->m_mask.m_composite); } EFillAttachment maskAttachment() const { return static_cast<EFillAttachment>(rareNonInheritedData->m_mask.m_attachment); } EFillBox maskClip() const { return static_cast<EFillBox>(rareNonInheritedData->m_mask.m_clip); } EFillBox maskOrigin() const { return static_cast<EFillBox>(rareNonInheritedData->m_mask.m_origin); } Length maskXPosition() const { return rareNonInheritedData->m_mask.m_xPosition; } Length maskYPosition() const { return rareNonInheritedData->m_mask.m_yPosition; } - LengthSize maskSize() const { return rareNonInheritedData->m_mask.m_size; } + LengthSize maskSize() const { return rareNonInheritedData->m_mask.m_sizeLength; } FillLayer* accessMaskLayers() { return &(rareNonInheritedData.access()->m_mask); } const FillLayer* maskLayers() const { return &(rareNonInheritedData->m_mask); } const NinePieceImage& maskBoxImage() const { return rareNonInheritedData->m_maskBoxImage; } @@ -576,7 +588,7 @@ public: short widows() const { return inherited->widows; } short orphans() const { return inherited->orphans; } - EPageBreak pageBreakInside() const { return static_cast<EPageBreak>(inherited->page_break_inside); } + EPageBreak pageBreakInside() const { return static_cast<EPageBreak>(noninherited_flags._page_break_inside); } EPageBreak pageBreakBefore() const { return static_cast<EPageBreak>(noninherited_flags._page_break_before); } EPageBreak pageBreakAfter() const { return static_cast<EPageBreak>(noninherited_flags._page_break_after); } @@ -596,6 +608,7 @@ public: const Color& textStrokeColor() const { return rareInheritedData->textStrokeColor; } float textStrokeWidth() const { return rareInheritedData->textStrokeWidth; } const Color& textFillColor() const { return rareInheritedData->textFillColor; } + ColorSpace colorSpace() const { return static_cast<ColorSpace>(rareInheritedData->colorSpace); } float opacity() const { return rareNonInheritedData->opacity; } ControlPart appearance() const { return static_cast<ControlPart>(rareNonInheritedData->m_appearance); } EBoxAlignment boxAlign() const { return static_cast<EBoxAlignment>(rareNonInheritedData->flexibleBox->align); } @@ -690,7 +703,7 @@ public: bool isRunningAcceleratedAnimation() const { return rareNonInheritedData->m_runningAcceleratedAnimation; } #endif - int lineClamp() const { return rareNonInheritedData->lineClamp; } + const LineClampValue& lineClamp() const { return rareNonInheritedData->lineClamp; } bool textSizeAdjust() const { return rareInheritedData->textSizeAdjust; } ETextSecurity textSecurity() const { return static_cast<ETextSecurity>(rareInheritedData->textSecurity); } @@ -755,7 +768,8 @@ public: void setBackgroundXPosition(Length l) { SET_VAR(background, m_background.m_xPosition, l) } void setBackgroundYPosition(Length l) { SET_VAR(background, m_background.m_yPosition, l) } - void setBackgroundSize(LengthSize l) { SET_VAR(background, m_background.m_size, l) } + void setBackgroundSize(EFillSizeType b) { SET_VAR(background, m_background.m_sizeType, b) } + void setBackgroundSizeLength(LengthSize l) { SET_VAR(background, m_background.m_sizeLength, l) } void setBorderImage(const NinePieceImage& b) { SET_VAR(surround, border.image, b) } @@ -868,7 +882,7 @@ public: void setMaskBoxImage(const NinePieceImage& b) { SET_VAR(rareNonInheritedData, m_maskBoxImage, b) } void setMaskXPosition(Length l) { SET_VAR(rareNonInheritedData, m_mask.m_xPosition, l) } void setMaskYPosition(Length l) { SET_VAR(rareNonInheritedData, m_mask.m_yPosition, l) } - void setMaskSize(LengthSize l) { SET_VAR(rareNonInheritedData, m_mask.m_size, l) } + void setMaskSize(LengthSize l) { SET_VAR(rareNonInheritedData, m_mask.m_sizeLength, l) } void setBorderCollapse(bool collapse) { inherited_flags._border_collapse = collapse; } void setHorizontalBorderSpacing(short v) { SET_VAR(inherited, horizontal_border_spacing, v) } @@ -914,7 +928,7 @@ public: void setWidows(short w) { SET_VAR(inherited, widows, w); } void setOrphans(short o) { SET_VAR(inherited, orphans, o); } - void setPageBreakInside(EPageBreak b) { SET_VAR(inherited, page_break_inside, b); } + void setPageBreakInside(EPageBreak b) { noninherited_flags._page_break_inside = b; } void setPageBreakBefore(EPageBreak b) { noninherited_flags._page_break_before = b; } void setPageBreakAfter(EPageBreak b) { noninherited_flags._page_break_after = b; } @@ -930,6 +944,7 @@ public: void setTextStrokeColor(const Color& c) { SET_VAR(rareInheritedData, textStrokeColor, c) } void setTextStrokeWidth(float w) { SET_VAR(rareInheritedData, textStrokeWidth, w) } void setTextFillColor(const Color& c) { SET_VAR(rareInheritedData, textFillColor, c) } + void setColorSpace(ColorSpace space) { SET_VAR(rareInheritedData, colorSpace, space) } void setOpacity(float f) { SET_VAR(rareNonInheritedData, opacity, f); } void setAppearance(ControlPart a) { SET_VAR(rareNonInheritedData, m_appearance, a); } void setBoxAlign(EBoxAlignment a) { SET_VAR(rareNonInheritedData.access()->flexibleBox, align, a); } @@ -1009,7 +1024,7 @@ public: void setIsRunningAcceleratedAnimation(bool b = true) { SET_VAR(rareNonInheritedData, m_runningAcceleratedAnimation, b); } #endif - void setLineClamp(int c) { SET_VAR(rareNonInheritedData, lineClamp, c); } + void setLineClamp(LineClampValue c) { SET_VAR(rareNonInheritedData, lineClamp, c); } void setTextSizeAdjust(bool b) { SET_VAR(rareInheritedData, textSizeAdjust, b); } void setTextSecurity(ETextSecurity aTextSecurity) { SET_VAR(rareInheritedData, textSecurity, aTextSecurity); } @@ -1106,7 +1121,7 @@ public: static EEmptyCell initialEmptyCells() { return SHOW; } static EFloat initialFloating() { return FNONE; } static EListStylePosition initialListStylePosition() { return OUTSIDE; } - static EListStyleType initialListStyleType() { return DISC; } + static EListStyleType initialListStyleType() { return Disc; } static EOverflow initialOverflowX() { return OVISIBLE; } static EOverflow initialOverflowY() { return OVISIBLE; } static EPageBreak initialPageBreak() { return PBAUTO; } @@ -1182,9 +1197,10 @@ public: static float initialPerspective() { return 0; } static Length initialPerspectiveOriginX() { return Length(50.0, Percent); } static Length initialPerspectiveOriginY() { return Length(50.0, Percent); } + static Color initialBackgroundColor() { return Color::transparent; } // Keep these at the end. - static int initialLineClamp() { return -1; } + static LineClampValue initialLineClamp() { return LineClampValue(); } static bool initialTextSizeAdjust() { return true; } static ETextSecurity initialTextSecurity() { return TSNONE; } #if ENABLE(DASHBOARD_SUPPORT) diff --git a/WebCore/rendering/style/RenderStyleConstants.h b/WebCore/rendering/style/RenderStyleConstants.h index c491816..01862f6 100644 --- a/WebCore/rendering/style/RenderStyleConstants.h +++ b/WebCore/rendering/style/RenderStyleConstants.h @@ -2,8 +2,9 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (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 @@ -68,10 +69,11 @@ 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, MEDIA_CONTROLS_REWIND_BUTTON, - MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, MEDIA_CONTROLS_STATUS_DISPLAY, - SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER, RESIZER, + MEDIA_CONTROLS_VOLUME_SLIDER, MEDIA_CONTROLS_VOLUME_SLIDER_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, MEDIA_CONTROLS_REWIND_BUTTON, + MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON, + MEDIA_CONTROLS_STATUS_DISPLAY, SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER, RESIZER, + INPUT_LIST_BUTTON, INNER_SPIN_BUTTON, OUTER_SPIN_BUTTON, FIRST_INTERNAL_PSEUDOID = FILE_UPLOAD_BUTTON }; @@ -131,13 +133,16 @@ enum EFillBox { }; enum EFillRepeat { - RepeatFill, RepeatXFill, RepeatYFill, NoRepeatFill + RepeatFill, NoRepeatFill, RoundFill, SpaceFill }; enum EFillLayerType { BackgroundFillLayer, MaskFillLayer }; +// CSS3 Background Values +enum EFillSizeType { Contain, Cover, SizeLength, SizeNone }; + // CSS3 Marquee Properties enum EMarqueeBehavior { MNONE, MSCROLL, MSLIDE, MALTERNATE }; @@ -198,12 +203,85 @@ enum EResize { RESIZE_NONE, RESIZE_BOTH, RESIZE_HORIZONTAL, RESIZE_VERTICAL }; +// The order of this enum must match the order of the list style types in CSSValueKeywords.in. enum EListStyleType { - DISC, CIRCLE, SQUARE, LDECIMAL, DECIMAL_LEADING_ZERO, - LOWER_ROMAN, UPPER_ROMAN, LOWER_GREEK, - LOWER_ALPHA, LOWER_LATIN, UPPER_ALPHA, UPPER_LATIN, - HEBREW, ARMENIAN, GEORGIAN, CJK_IDEOGRAPHIC, - HIRAGANA, KATAKANA, HIRAGANA_IROHA, KATAKANA_IROHA, LNONE + Disc, + Circle, + Square, + DecimalListStyle, + DecimalLeadingZero, + ArabicIndic, + BinaryListStyle, + Bengali, + Cambodian, + Khmer, + Devanagari, + Gujarati, + Gurmukhi, + Kannada, + LowerHexadecimal, + Lao, + Malayalam, + Mongolian, + Myanmar, + Octal, + Oriya, + Persian, + Urdu, + Telugu, + Tibetan, + Thai, + UpperHexadecimal, + LowerRoman, + UpperRoman, + LowerGreek, + LowerAlpha, + LowerLatin, + UpperAlpha, + UpperLatin, + Afar, + EthiopicHalehameAaEt, + EthiopicHalehameAaEr, + Amharic, + EthiopicHalehameAmEt, + AmharicAbegede, + EthiopicAbegedeAmEt, + CjkEarthlyBranch, + CjkHeavenlyStem, + Ethiopic, + EthiopicHalehameGez, + EthiopicAbegede, + EthiopicAbegedeGez, + HangulConsonant, + Hangul, + LowerNorwegian, + Oromo, + EthiopicHalehameOmEt, + Sidama, + EthiopicHalehameSidEt, + Somali, + EthiopicHalehameSoEt, + Tigre, + EthiopicHalehameTig, + TigrinyaEr, + EthiopicHalehameTiEr, + TigrinyaErAbegede, + EthiopicAbegedeTiEr, + TigrinyaEt, + EthiopicHalehameTiEt, + TigrinyaEtAbegede, + EthiopicAbegedeTiEt, + UpperGreek, + UpperNorwegian, + Hebrew, + Armenian, + Georgian, + CJKIdeographic, + Hiragana, + Katakana, + HiraganaIroha, + KatakanaIroha, + NoneListStyle }; enum StyleContentType { @@ -298,7 +376,11 @@ enum EDisplay { TABLE, INLINE_TABLE, TABLE_ROW_GROUP, TABLE_HEADER_GROUP, TABLE_FOOTER_GROUP, TABLE_ROW, TABLE_COLUMN_GROUP, TABLE_COLUMN, TABLE_CELL, - TABLE_CAPTION, BOX, INLINE_BOX, NONE + TABLE_CAPTION, BOX, INLINE_BOX, +#if ENABLE(WCSS) + WAP_MARQUEE, +#endif + NONE }; enum EPointerEvents { @@ -313,6 +395,8 @@ enum ETransformStyle3D { enum EBackfaceVisibility { BackfaceVisibilityVisible, BackfaceVisibilityHidden }; + +enum ELineClampType { LineClampLineCount, LineClampPercentage }; } // namespace WebCore diff --git a/WebCore/rendering/style/SVGRenderStyle.cpp b/WebCore/rendering/style/SVGRenderStyle.cpp index 1289b06..7958088 100644 --- a/WebCore/rendering/style/SVGRenderStyle.cpp +++ b/WebCore/rendering/style/SVGRenderStyle.cpp @@ -8,8 +8,6 @@ Copyright (C) 2002-2003 Dirk Mueller (mueller@kde.org) Copyright (C) 2002 Apple Computer, Inc. - This file is part of the KDE project - 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 @@ -32,11 +30,14 @@ #include "CSSPrimitiveValue.h" #include "CSSValueList.h" +#include "IntRect.h" #include "NodeRenderStyle.h" #include "RenderObject.h" #include "RenderStyle.h" #include "SVGStyledElement.h" +using namespace std; + namespace WebCore { SVGRenderStyle::SVGRenderStyle() @@ -51,6 +52,7 @@ SVGRenderStyle::SVGRenderStyle() mask = defaultStyle->mask; misc = defaultStyle->misc; markers = defaultStyle->markers; + shadowSVG = defaultStyle->shadowSVG; setBitDefaults(); } @@ -67,6 +69,7 @@ SVGRenderStyle::SVGRenderStyle(CreateDefaultType) mask.init(); misc.init(); markers.init(); + shadowSVG.init(); } SVGRenderStyle::SVGRenderStyle(const SVGRenderStyle& other) @@ -80,6 +83,7 @@ SVGRenderStyle::SVGRenderStyle(const SVGRenderStyle& other) mask = other.mask; misc = other.misc; markers = other.markers; + shadowSVG = other.shadowSVG; svg_inherited_flags = other.svg_inherited_flags; svg_noninherited_flags = other.svg_noninherited_flags; @@ -93,7 +97,7 @@ bool SVGRenderStyle::operator==(const SVGRenderStyle& o) const { return (fill == o.fill && stroke == o.stroke && text == o.text && stops == o.stops && clip == o.clip && mask == o.mask && - misc == o.misc && markers == o.markers && + misc == o.misc && markers == o.markers && shadowSVG == o.shadowSVG && svg_inherited_flags == o.svg_inherited_flags && svg_noninherited_flags == o.svg_noninherited_flags); } @@ -140,6 +144,56 @@ float SVGRenderStyle::cssPrimitiveToLength(const RenderObject* item, CSSValue* v return primitive->computeLengthFloat(const_cast<RenderStyle*>(item->style()), item->document()->documentElement()->renderStyle()); } + +static void getSVGShadowExtent(ShadowData* shadow, int& top, int& right, int& bottom, int& left) +{ + top = 0; + right = 0; + bottom = 0; + left = 0; + + int blurAndSpread = shadow->blur + shadow->spread; + + top = min(top, shadow->y - blurAndSpread); + right = max(right, shadow->x + blurAndSpread); + bottom = max(bottom, shadow->y + blurAndSpread); + left = min(left, shadow->x - blurAndSpread); +} + +void SVGRenderStyle::inflateForShadow(IntRect& repaintRect) const +{ + ShadowData* svgShadow = shadow(); + if (!svgShadow) + return; + + FloatRect repaintFloatRect = FloatRect(repaintRect); + inflateForShadow(repaintFloatRect); + repaintRect = enclosingIntRect(repaintFloatRect); +} + +void SVGRenderStyle::inflateForShadow(FloatRect& repaintRect) const +{ + ShadowData* svgShadow = shadow(); + if (!svgShadow) + return; + + int shadowTop; + int shadowRight; + int shadowBottom; + int shadowLeft; + getSVGShadowExtent(svgShadow, shadowTop, shadowRight, shadowBottom, shadowLeft); + + int overflowLeft = repaintRect.x() + shadowLeft; + int overflowRight = repaintRect.right() + shadowRight; + int overflowTop = repaintRect.y() + shadowTop; + int overflowBottom = repaintRect.bottom() + shadowBottom; + + repaintRect.setX(overflowLeft); + repaintRect.setY(overflowTop); + repaintRect.setWidth(overflowRight - overflowLeft); + repaintRect.setHeight(overflowBottom - overflowTop); +} + } #endif // ENABLE(SVG) diff --git a/WebCore/rendering/style/SVGRenderStyle.h b/WebCore/rendering/style/SVGRenderStyle.h index 0e9dae4..c7f85db 100644 --- a/WebCore/rendering/style/SVGRenderStyle.h +++ b/WebCore/rendering/style/SVGRenderStyle.h @@ -3,8 +3,6 @@ 2004, 2005 Rob Buis <buis@kde.org> Copyright (C) 2005, 2006 Apple Computer, Inc. - This file is part of the KDE project - 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 @@ -30,182 +28,188 @@ #include "GraphicsTypes.h" #include "SVGPaint.h" #include "SVGRenderStyleDefs.h" +#include "ShadowData.h" #include <wtf/Platform.h> namespace WebCore { - class RenderObject; - class RenderStyle; - - class SVGRenderStyle : public RefCounted<SVGRenderStyle> { - public: - static PassRefPtr<SVGRenderStyle> create() { return adoptRef(new SVGRenderStyle); } - PassRefPtr<SVGRenderStyle> copy() const { return adoptRef(new SVGRenderStyle(*this));} - ~SVGRenderStyle(); +class FloatRect; +class IntRect; +class RenderObject; +class RenderStyle; + +class SVGRenderStyle : public RefCounted<SVGRenderStyle> { +public: + static PassRefPtr<SVGRenderStyle> create() { return adoptRef(new SVGRenderStyle); } + PassRefPtr<SVGRenderStyle> copy() const { return adoptRef(new SVGRenderStyle(*this));} + ~SVGRenderStyle(); + + bool inheritedNotEqual(const SVGRenderStyle*) const; + void inheritFrom(const SVGRenderStyle*); + + // FIXME: These functions should move to ShadowData. + void inflateForShadow(IntRect&) const; + void inflateForShadow(FloatRect&) const; + + bool operator==(const SVGRenderStyle&) const; + bool operator!=(const SVGRenderStyle& o) const { return !(*this == o); } + + // SVG CSS Properties + SVG_RS_DEFINE_ATTRIBUTE(EAlignmentBaseline, AlignmentBaseline, alignmentBaseline, AB_AUTO) + SVG_RS_DEFINE_ATTRIBUTE(EDominantBaseline, DominantBaseline, dominantBaseline, DB_AUTO) + SVG_RS_DEFINE_ATTRIBUTE(EBaselineShift, BaselineShift, baselineShift, BS_BASELINE) + + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineCap, CapStyle, capStyle, ButtCap) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, ClipRule, clipRule, RULE_NONZERO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolation, colorInterpolation, CI_SRGB) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolationFilters, colorInterpolationFilters, CI_LINEARRGB) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorRendering, ColorRendering, colorRendering, CR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, FillRule, fillRule, RULE_NONZERO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EImageRendering, ImageRendering, imageRendering, IR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineJoin, JoinStyle, joinStyle, MiterJoin) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EShapeRendering, ShapeRendering, shapeRendering, SR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(ETextAnchor, TextAnchor, textAnchor, TA_START) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EWritingMode, WritingMode, writingMode, WM_LRTB) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationHorizontal, glyphOrientationHorizontal, GO_0DEG) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationVertical, glyphOrientationVertical, GO_AUTO) + + // SVG CSS Properties (using DataRef's) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, fill, opacity, FillOpacity, fillOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, fill, paint, FillPaint, fillPaint, SVGPaint::defaultFill()) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, opacity, StrokeOpacity, strokeOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, stroke, paint, StrokePaint, strokePaint, SVGPaint::defaultStroke()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValueList, stroke, dashArray, StrokeDashArray, strokeDashArray, 0) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, miterLimit, StrokeMiterLimit, strokeMiterLimit, 4.0f) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, width, StrokeWidth, strokeWidth, 0) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, dashOffset, StrokeDashOffset, strokeDashOffset, 0); + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, text, kerning, Kerning, kerning, 0) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stops, opacity, StopOpacity, stopOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, stops, color, StopColor, stopColor, Color(0, 0, 0)) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, clip, clipPath, ClipPath, clipPath, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, mask, maskElement, MaskElement, maskElement, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, startMarker, StartMarker, startMarker, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, midMarker, MidMarker, midMarker, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, endMarker, EndMarker, endMarker, String()) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, misc, filter, Filter, filter, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, misc, floodOpacity, FloodOpacity, floodOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, floodColor, FloodColor, floodColor, Color(0, 0, 0)) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, lightingColor, LightingColor, lightingColor, Color(255, 255, 255)) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, misc, baselineShiftValue, BaselineShiftValue, baselineShiftValue, 0) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_OWNPTR(ShadowData, shadowSVG, shadow, Shadow, shadow, 0) + + // convenience + bool hasStroke() const { return (strokePaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } + bool hasFill() const { return (fillPaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } + + static float cssPrimitiveToLength(const RenderObject*, CSSValue*, float defaultValue = 0.0f); + +protected: + // inherit + struct InheritedFlags { + bool operator==(const InheritedFlags& other) const + { + return (_colorRendering == other._colorRendering) + && (_imageRendering == other._imageRendering) + && (_shapeRendering == other._shapeRendering) + && (_clipRule == other._clipRule) + && (_fillRule == other._fillRule) + && (_capStyle == other._capStyle) + && (_joinStyle == other._joinStyle) + && (_textAnchor == other._textAnchor) + && (_colorInterpolation == other._colorInterpolation) + && (_colorInterpolationFilters == other._colorInterpolationFilters) + && (_writingMode == other._writingMode) + && (_glyphOrientationHorizontal == other._glyphOrientationHorizontal) + && (_glyphOrientationVertical == other._glyphOrientationVertical); + } - bool inheritedNotEqual(const SVGRenderStyle*) const; - void inheritFrom(const SVGRenderStyle*); - - bool operator==(const SVGRenderStyle&) const; - bool operator!=(const SVGRenderStyle& o) const { return !(*this == o); } - - // SVG CSS Properties - SVG_RS_DEFINE_ATTRIBUTE(EAlignmentBaseline, AlignmentBaseline, alignmentBaseline, AB_AUTO) - SVG_RS_DEFINE_ATTRIBUTE(EDominantBaseline, DominantBaseline, dominantBaseline, DB_AUTO) - SVG_RS_DEFINE_ATTRIBUTE(EBaselineShift, BaselineShift, baselineShift, BS_BASELINE) - - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineCap, CapStyle, capStyle, ButtCap) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, ClipRule, clipRule, RULE_NONZERO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolation, colorInterpolation, CI_SRGB) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolationFilters, colorInterpolationFilters, CI_LINEARRGB) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorRendering, ColorRendering, colorRendering, CR_AUTO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, FillRule, fillRule, RULE_NONZERO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EImageRendering, ImageRendering, imageRendering, IR_AUTO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineJoin, JoinStyle, joinStyle, MiterJoin) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EShapeRendering, ShapeRendering, shapeRendering, SR_AUTO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(ETextAnchor, TextAnchor, textAnchor, TA_START) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(ETextRendering, TextRendering, textRendering, TR_AUTO) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EWritingMode, WritingMode, writingMode, WM_LRTB) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationHorizontal, glyphOrientationHorizontal, GO_0DEG) - SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationVertical, glyphOrientationVertical, GO_AUTO) - - // SVG CSS Properties (using DataRef's) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, fill, opacity, FillOpacity, fillOpacity, 1.0f) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, fill, paint, FillPaint, fillPaint, SVGPaint::defaultFill()) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, opacity, StrokeOpacity, strokeOpacity, 1.0f) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, stroke, paint, StrokePaint, strokePaint, SVGPaint::defaultStroke()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValueList, stroke, dashArray, StrokeDashArray, strokeDashArray, 0) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, miterLimit, StrokeMiterLimit, strokeMiterLimit, 4.0f) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, width, StrokeWidth, strokeWidth, 0) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, dashOffset, StrokeDashOffset, strokeDashOffset, 0); - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, text, kerning, Kerning, kerning, 0) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stops, opacity, StopOpacity, stopOpacity, 1.0f) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, stops, color, StopColor, stopColor, Color(0, 0, 0)) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, clip, clipPath, ClipPath, clipPath, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, mask, maskElement, MaskElement, maskElement, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, startMarker, StartMarker, startMarker, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, midMarker, MidMarker, midMarker, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, endMarker, EndMarker, endMarker, String()) - - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, misc, filter, Filter, filter, String()) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, misc, floodOpacity, FloodOpacity, floodOpacity, 1.0f) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, floodColor, FloodColor, floodColor, Color(0, 0, 0)) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, lightingColor, LightingColor, lightingColor, Color(255, 255, 255)) - SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, misc, baselineShiftValue, BaselineShiftValue, baselineShiftValue, 0) - - // convenience - bool hasStroke() const { return (strokePaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } - bool hasFill() const { return (fillPaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } - - static float cssPrimitiveToLength(const RenderObject*, CSSValue*, float defaultValue = 0.0f); - - protected: - // inherit - struct InheritedFlags { - bool operator==(const InheritedFlags& other) const - { - return (_colorRendering == other._colorRendering) && - (_imageRendering == other._imageRendering) && - (_shapeRendering == other._shapeRendering) && - (_textRendering == other._textRendering) && - (_clipRule == other._clipRule) && - (_fillRule == other._fillRule) && - (_capStyle == other._capStyle) && - (_joinStyle == other._joinStyle) && - (_textAnchor == other._textAnchor) && - (_colorInterpolation == other._colorInterpolation) && - (_colorInterpolationFilters == other._colorInterpolationFilters) && - (_writingMode == other._writingMode) && - (_glyphOrientationHorizontal == other._glyphOrientationHorizontal) && - (_glyphOrientationVertical == other._glyphOrientationVertical); - } - - bool operator!=(const InheritedFlags& other) const - { - return !(*this == other); - } - - unsigned _colorRendering : 2; // EColorRendering - unsigned _imageRendering : 2; // EImageRendering - unsigned _shapeRendering : 2; // EShapeRendering - unsigned _textRendering : 2; // ETextRendering - unsigned _clipRule : 1; // WindRule - unsigned _fillRule : 1; // WindRule - unsigned _capStyle : 2; // LineCap - unsigned _joinStyle : 2; // LineJoin - unsigned _textAnchor : 2; // ETextAnchor - unsigned _colorInterpolation : 2; // EColorInterpolation - unsigned _colorInterpolationFilters : 2; // EColorInterpolation - unsigned _writingMode : 3; // EWritingMode - unsigned _glyphOrientationHorizontal : 3; // EGlyphOrientation - unsigned _glyphOrientationVertical : 3; // EGlyphOrientation - } svg_inherited_flags; - - // don't inherit - struct NonInheritedFlags { - // 32 bit non-inherited, don't add to the struct, or the operator will break. - bool operator==(const NonInheritedFlags &other) const { return _niflags == other._niflags; } - bool operator!=(const NonInheritedFlags &other) const { return _niflags != other._niflags; } - - union { - struct { - unsigned _alignmentBaseline : 4; // EAlignmentBaseline - unsigned _dominantBaseline : 4; // EDominantBaseline - unsigned _baselineShift : 2; // EBaselineShift - // 22 bits unused - } f; - uint32_t _niflags; - }; - } svg_noninherited_flags; - - // inherited attributes - DataRef<StyleFillData> fill; - DataRef<StyleStrokeData> stroke; - DataRef<StyleMarkerData> markers; - DataRef<StyleTextData> text; - - // non-inherited attributes - DataRef<StyleStopData> stops; - DataRef<StyleClipData> clip; - DataRef<StyleMaskData> mask; - DataRef<StyleMiscData> misc; - - private: - enum CreateDefaultType { CreateDefault }; - - SVGRenderStyle(); - SVGRenderStyle(const SVGRenderStyle&); - SVGRenderStyle(CreateDefaultType); // Used to create the default style. - - void setBitDefaults() + bool operator!=(const InheritedFlags& other) const { - svg_inherited_flags._clipRule = initialClipRule(); - svg_inherited_flags._colorRendering = initialColorRendering(); - svg_inherited_flags._fillRule = initialFillRule(); - svg_inherited_flags._imageRendering = initialImageRendering(); - svg_inherited_flags._shapeRendering = initialShapeRendering(); - svg_inherited_flags._textRendering = initialTextRendering(); - svg_inherited_flags._textAnchor = initialTextAnchor(); - svg_inherited_flags._capStyle = initialCapStyle(); - svg_inherited_flags._joinStyle = initialJoinStyle(); - svg_inherited_flags._colorInterpolation = initialColorInterpolation(); - svg_inherited_flags._colorInterpolationFilters = initialColorInterpolationFilters(); - svg_inherited_flags._writingMode = initialWritingMode(); - svg_inherited_flags._glyphOrientationHorizontal = initialGlyphOrientationHorizontal(); - svg_inherited_flags._glyphOrientationVertical = initialGlyphOrientationVertical(); - - svg_noninherited_flags._niflags = 0; - svg_noninherited_flags.f._alignmentBaseline = initialAlignmentBaseline(); - svg_noninherited_flags.f._dominantBaseline = initialDominantBaseline(); - svg_noninherited_flags.f._baselineShift = initialBaselineShift(); + return !(*this == other); } - }; + + unsigned _colorRendering : 2; // EColorRendering + unsigned _imageRendering : 2; // EImageRendering + unsigned _shapeRendering : 2; // EShapeRendering + unsigned _clipRule : 1; // WindRule + unsigned _fillRule : 1; // WindRule + unsigned _capStyle : 2; // LineCap + unsigned _joinStyle : 2; // LineJoin + unsigned _textAnchor : 2; // ETextAnchor + unsigned _colorInterpolation : 2; // EColorInterpolation + unsigned _colorInterpolationFilters : 2; // EColorInterpolation + unsigned _writingMode : 3; // EWritingMode + unsigned _glyphOrientationHorizontal : 3; // EGlyphOrientation + unsigned _glyphOrientationVertical : 3; // EGlyphOrientation + } svg_inherited_flags; + + // don't inherit + struct NonInheritedFlags { + // 32 bit non-inherited, don't add to the struct, or the operator will break. + bool operator==(const NonInheritedFlags &other) const { return _niflags == other._niflags; } + bool operator!=(const NonInheritedFlags &other) const { return _niflags != other._niflags; } + + union { + struct { + unsigned _alignmentBaseline : 4; // EAlignmentBaseline + unsigned _dominantBaseline : 4; // EDominantBaseline + unsigned _baselineShift : 2; // EBaselineShift + // 22 bits unused + } f; + uint32_t _niflags; + }; + } svg_noninherited_flags; + + // inherited attributes + DataRef<StyleFillData> fill; + DataRef<StyleStrokeData> stroke; + DataRef<StyleMarkerData> markers; + DataRef<StyleTextData> text; + + // non-inherited attributes + DataRef<StyleStopData> stops; + DataRef<StyleClipData> clip; + DataRef<StyleMaskData> mask; + DataRef<StyleMiscData> misc; + DataRef<StyleShadowSVGData> shadowSVG; + +private: + enum CreateDefaultType { CreateDefault }; + + SVGRenderStyle(); + SVGRenderStyle(const SVGRenderStyle&); + SVGRenderStyle(CreateDefaultType); // Used to create the default style. + + void setBitDefaults() + { + svg_inherited_flags._clipRule = initialClipRule(); + svg_inherited_flags._colorRendering = initialColorRendering(); + svg_inherited_flags._fillRule = initialFillRule(); + svg_inherited_flags._imageRendering = initialImageRendering(); + svg_inherited_flags._shapeRendering = initialShapeRendering(); + svg_inherited_flags._textAnchor = initialTextAnchor(); + svg_inherited_flags._capStyle = initialCapStyle(); + svg_inherited_flags._joinStyle = initialJoinStyle(); + svg_inherited_flags._colorInterpolation = initialColorInterpolation(); + svg_inherited_flags._colorInterpolationFilters = initialColorInterpolationFilters(); + svg_inherited_flags._writingMode = initialWritingMode(); + svg_inherited_flags._glyphOrientationHorizontal = initialGlyphOrientationHorizontal(); + svg_inherited_flags._glyphOrientationVertical = initialGlyphOrientationVertical(); + + svg_noninherited_flags._niflags = 0; + svg_noninherited_flags.f._alignmentBaseline = initialAlignmentBaseline(); + svg_noninherited_flags.f._dominantBaseline = initialDominantBaseline(); + svg_noninherited_flags.f._baselineShift = initialBaselineShift(); + } +}; } // namespace WebCore diff --git a/WebCore/rendering/style/SVGRenderStyleDefs.cpp b/WebCore/rendering/style/SVGRenderStyleDefs.cpp index f5faad3..093f1f1 100644 --- a/WebCore/rendering/style/SVGRenderStyleDefs.cpp +++ b/WebCore/rendering/style/SVGRenderStyleDefs.cpp @@ -8,8 +8,6 @@ Copyright (C) 2002-2003 Dirk Mueller (mueller@kde.org) Copyright (C) 2002 Apple Computer, Inc. - This file is part of the KDE project - 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 @@ -213,6 +211,25 @@ bool StyleMiscData::operator==(const StyleMiscData &other) const && baselineShiftValue == other.baselineShiftValue; } +StyleShadowSVGData::StyleShadowSVGData() +{ +} + +StyleShadowSVGData::StyleShadowSVGData(const StyleShadowSVGData& other) + : RefCounted<StyleShadowSVGData>() + , shadow(other.shadow ? new ShadowData(*other.shadow) : 0) +{ +} + +bool StyleShadowSVGData::operator==(const StyleShadowSVGData& other) const +{ + if ((!shadow && other.shadow) || (shadow && !other.shadow)) + return false; + if (shadow && other.shadow && (*shadow != *other.shadow)) + return false; + return true; +} + #endif // ENABLE(SVG) // vim:ts=4:noet diff --git a/WebCore/rendering/style/SVGRenderStyleDefs.h b/WebCore/rendering/style/SVGRenderStyleDefs.h index b7bf026..8f01d9f 100644 --- a/WebCore/rendering/style/SVGRenderStyleDefs.h +++ b/WebCore/rendering/style/SVGRenderStyleDefs.h @@ -8,8 +8,6 @@ (C) 2000-2003 Dirk Mueller (mueller@kde.org) (C) 2002-2003 Apple Computer, Inc. - This file is part of the KDE project - 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 @@ -33,6 +31,9 @@ #include "Color.h" #include "Path.h" #include "PlatformString.h" +#include "ShadowData.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -65,6 +66,13 @@ } \ static Data* initial##Type() { return Initial; } +#define SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_OWNPTR(Data, Group, Variable, Type, Name, Initial) \ + Data* Name() const { return Group->Variable.get(); } \ + void set##Type(Data* obj) { \ + Group.access()->Variable.set(obj); \ + } \ + static Data* initial##Type() { return Initial; } + #define SVG_RS_SET_VARIABLE(Group, Variable, Value) \ if (!(Group->Variable == Value)) \ Group.access()->Variable = Value; @@ -95,10 +103,6 @@ namespace WebCore { SR_AUTO, SR_OPTIMIZESPEED, SR_CRISPEDGES, SR_GEOMETRICPRECISION }; - enum ETextRendering { - TR_AUTO, TR_OPTIMIZESPEED, TR_OPTIMIZELEGIBILITY, TR_GEOMETRICPRECISION - }; - enum EWritingMode { WM_LRTB, WM_LR, WM_RLTB, WM_RL, WM_TBRL, WM_TB }; @@ -283,6 +287,24 @@ namespace WebCore { StyleMiscData(); StyleMiscData(const StyleMiscData&); }; + + class StyleShadowSVGData : public RefCounted<StyleShadowSVGData> { + public: + static PassRefPtr<StyleShadowSVGData> create() { return adoptRef(new StyleShadowSVGData); } + PassRefPtr<StyleShadowSVGData> copy() const { return adoptRef(new StyleShadowSVGData(*this)); } + + bool operator==(const StyleShadowSVGData& other) const; + bool operator!=(const StyleShadowSVGData& other) const + { + return !(*this == other); + } + + OwnPtr<ShadowData> shadow; + + private: + StyleShadowSVGData(); + StyleShadowSVGData(const StyleShadowSVGData& other); + }; } // namespace WebCore diff --git a/WebCore/rendering/style/ShadowData.h b/WebCore/rendering/style/ShadowData.h index f4061f2..089cf77 100644 --- a/WebCore/rendering/style/ShadowData.h +++ b/WebCore/rendering/style/ShadowData.h @@ -26,6 +26,7 @@ #define ShadowData_h #include "Color.h" +#include <wtf/FastAllocBase.h> namespace WebCore { @@ -33,7 +34,7 @@ enum ShadowStyle { Normal, Inset }; // This struct holds information about shadows for the text-shadow and box-shadow properties. -struct ShadowData { +struct ShadowData : FastAllocBase { ShadowData() : x(0) , y(0) diff --git a/WebCore/rendering/style/StyleBackgroundData.cpp b/WebCore/rendering/style/StyleBackgroundData.cpp index 68a9ddd..08f5527 100644 --- a/WebCore/rendering/style/StyleBackgroundData.cpp +++ b/WebCore/rendering/style/StyleBackgroundData.cpp @@ -22,12 +22,14 @@ #include "config.h" #include "StyleBackgroundData.h" +#include "RenderStyle.h" #include "RenderStyleConstants.h" namespace WebCore { StyleBackgroundData::StyleBackgroundData() : m_background(BackgroundFillLayer) + , m_color(RenderStyle::initialBackgroundColor()) { } diff --git a/WebCore/rendering/style/StyleInheritedData.cpp b/WebCore/rendering/style/StyleInheritedData.cpp index f59c0c2..c73497f 100644 --- a/WebCore/rendering/style/StyleInheritedData.cpp +++ b/WebCore/rendering/style/StyleInheritedData.cpp @@ -37,7 +37,6 @@ StyleInheritedData::StyleInheritedData() , vertical_border_spacing(RenderStyle::initialVerticalBorderSpacing()) , widows(RenderStyle::initialWidows()) , orphans(RenderStyle::initialOrphans()) - , page_break_inside(RenderStyle::initialPageBreak()) { } @@ -58,7 +57,6 @@ StyleInheritedData::StyleInheritedData(const StyleInheritedData& o) , vertical_border_spacing(o.vertical_border_spacing) , widows(o.widows) , orphans(o.orphans) - , page_break_inside(o.page_break_inside) { } @@ -84,8 +82,7 @@ bool StyleInheritedData::operator==(const StyleInheritedData& o) const horizontal_border_spacing == o.horizontal_border_spacing && vertical_border_spacing == o.vertical_border_spacing && widows == o.widows && - orphans == o.orphans && - page_break_inside == o.page_break_inside; + orphans == o.orphans; } } // namespace WebCore diff --git a/WebCore/rendering/style/StyleInheritedData.h b/WebCore/rendering/style/StyleInheritedData.h index 548ca72..3b30b8f 100644 --- a/WebCore/rendering/style/StyleInheritedData.h +++ b/WebCore/rendering/style/StyleInheritedData.h @@ -68,7 +68,6 @@ public: // Paged media properties. short widows; short orphans; - unsigned page_break_inside : 2; // EPageBreak private: StyleInheritedData(); diff --git a/WebCore/rendering/style/StyleRareInheritedData.cpp b/WebCore/rendering/style/StyleRareInheritedData.cpp index 0f3b7e7..ff626b7 100644 --- a/WebCore/rendering/style/StyleRareInheritedData.cpp +++ b/WebCore/rendering/style/StyleRareInheritedData.cpp @@ -42,6 +42,7 @@ StyleRareInheritedData::StyleRareInheritedData() , textSizeAdjust(RenderStyle::initialTextSizeAdjust()) , resize(RenderStyle::initialResize()) , userSelect(RenderStyle::initialUserSelect()) + , colorSpace(DeviceColorSpace) { } @@ -64,6 +65,7 @@ StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o) , textSizeAdjust(o.textSizeAdjust) , resize(o.resize) , userSelect(o.userSelect) + , colorSpace(o.colorSpace) { } @@ -90,7 +92,8 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const && tapHighlightColor == o.tapHighlightColor #endif && resize == o.resize - && userSelect == o.userSelect; + && userSelect == o.userSelect + && colorSpace == o.colorSpace; } bool StyleRareInheritedData::shadowDataEquivalent(const StyleRareInheritedData& o) const diff --git a/WebCore/rendering/style/StyleRareInheritedData.h b/WebCore/rendering/style/StyleRareInheritedData.h index 4abd3cf..1aa7b05 100644 --- a/WebCore/rendering/style/StyleRareInheritedData.h +++ b/WebCore/rendering/style/StyleRareInheritedData.h @@ -68,6 +68,7 @@ public: bool textSizeAdjust : 1; // An Apple extension. unsigned resize : 2; // EResize unsigned userSelect : 1; // EUserSelect + unsigned colorSpace : 1; // ColorSpace private: StyleRareInheritedData(); diff --git a/WebCore/rendering/style/StyleRareNonInheritedData.h b/WebCore/rendering/style/StyleRareNonInheritedData.h index 8dd22b3..452b273 100644 --- a/WebCore/rendering/style/StyleRareNonInheritedData.h +++ b/WebCore/rendering/style/StyleRareNonInheritedData.h @@ -29,6 +29,7 @@ #include "CursorData.h" #include "DataRef.h" #include "FillLayer.h" +#include "LineClampValue.h" #include "NinePieceImage.h" #include "StyleTransformData.h" #include <wtf/OwnPtr.h> @@ -77,7 +78,7 @@ public: bool animationDataEquivalent(const StyleRareNonInheritedData&) const; bool transitionDataEquivalent(const StyleRareNonInheritedData&) const; - int lineClamp; // An Apple extension. + LineClampValue lineClamp; // An Apple extension. #if ENABLE(DASHBOARD_SUPPORT) Vector<StyleDashboardRegion> m_dashboardRegions; #endif |
