diff options
author | Kristian Monsen <kristianm@google.com> | 2010-06-28 16:42:48 +0100 |
---|---|---|
committer | Kristian Monsen <kristianm@google.com> | 2010-07-02 10:29:56 +0100 |
commit | 06ea8e899e48f1f2f396b70e63fae369f2f23232 (patch) | |
tree | 20c1428cd05c76f32394ab354ea35ed99acd86d8 /WebCore/rendering | |
parent | 72aad67af14193199e29cdd5c4ddc095a8b9a8a8 (diff) | |
download | external_webkit-06ea8e899e48f1f2f396b70e63fae369f2f23232.zip external_webkit-06ea8e899e48f1f2f396b70e63fae369f2f23232.tar.gz external_webkit-06ea8e899e48f1f2f396b70e63fae369f2f23232.tar.bz2 |
Merge WebKit at r61871: Initial merge by git.
Change-Id: I6cff43abca9cc4782e088a469ad4f03f166a65d5
Diffstat (limited to 'WebCore/rendering')
87 files changed, 3022 insertions, 2491 deletions
diff --git a/WebCore/rendering/BidiRun.h b/WebCore/rendering/BidiRun.h index 542081a..5dbb07b 100644 --- a/WebCore/rendering/BidiRun.h +++ b/WebCore/rendering/BidiRun.h @@ -38,6 +38,7 @@ struct BidiRun : BidiCharacterRun { : BidiCharacterRun(start, stop, context, dir) , m_object(object) , m_box(0) + , m_hasHyphen(false) { } @@ -58,6 +59,7 @@ private: public: RenderObject* m_object; InlineBox* m_box; + bool m_hasHyphen; }; } diff --git a/WebCore/rendering/InlineBox.h b/WebCore/rendering/InlineBox.h index e165f0c..eec9c1b 100644 --- a/WebCore/rendering/InlineBox.h +++ b/WebCore/rendering/InlineBox.h @@ -53,7 +53,7 @@ public: #endif , m_endsWithBreak(false) , m_hasSelectedChildren(false) - , m_hasEllipsisBox(false) + , m_hasEllipsisBoxOrHyphen(false) , m_dirOverride(false) , m_isText(false) , m_determinedIfNextOnLineExists(false) @@ -86,7 +86,7 @@ public: #endif , m_endsWithBreak(false) , m_hasSelectedChildren(false) - , m_hasEllipsisBox(false) + , m_hasEllipsisBoxOrHyphen(false) , m_dirOverride(false) , m_isText(false) , m_determinedIfNextOnLineExists(false) @@ -133,18 +133,28 @@ public: bool isText() const { return m_isText; } void setIsText(bool b) { m_isText = b; } - virtual bool isInlineBox() { return false; } virtual bool isInlineFlowBox() const { return false; } - virtual bool isInlineTextBox() { return false; } + virtual bool isInlineTextBox() const { return false; } virtual bool isRootInlineBox() const { return false; } -#if ENABLE(SVG) - virtual bool isSVGRootInlineBox() { return false; } +#if ENABLE(SVG) + virtual bool isSVGInlineTextBox() const { return false; } + virtual bool isSVGRootInlineBox() const { return false; } bool hasVirtualHeight() const { return m_hasVirtualHeight; } void setHasVirtualHeight() { m_hasVirtualHeight = true; } - virtual int virtualHeight() const { ASSERT_NOT_REACHED(); return 0; } + virtual int virtualHeight() const + { + ASSERT_NOT_REACHED(); + return 0; + } #endif - + + virtual IntRect calculateBoundaries() const + { + ASSERT_NOT_REACHED(); + return IntRect(); + } + bool isConstructed() { return m_constructed; } virtual void setConstructed() { @@ -274,7 +284,7 @@ protected: // for RootInlineBox bool m_endsWithBreak : 1; // Whether the line ends with a <br>. bool m_hasSelectedChildren : 1; // Whether we have any children selected (this bit will also be set if the <br> that terminates our line is selected). - bool m_hasEllipsisBox : 1; + bool m_hasEllipsisBoxOrHyphen : 1; // for InlineTextBox public: diff --git a/WebCore/rendering/InlineFlowBox.h b/WebCore/rendering/InlineFlowBox.h index e939fb4..b933c09 100644 --- a/WebCore/rendering/InlineFlowBox.h +++ b/WebCore/rendering/InlineFlowBox.h @@ -127,7 +127,7 @@ public: void determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject); int getFlowSpacingWidth(); bool onEndChain(RenderObject* endObject); - virtual int placeBoxesHorizontally(int x, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); + int placeBoxesHorizontally(int x, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); void computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, int& maxAscent, int& maxDescent, bool strictMode, GlyphOverflowAndFallbackFontsMap&); void adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, diff --git a/WebCore/rendering/InlineTextBox.cpp b/WebCore/rendering/InlineTextBox.cpp index 2fc5823..09825b6 100644 --- a/WebCore/rendering/InlineTextBox.cpp +++ b/WebCore/rendering/InlineTextBox.cpp @@ -1,7 +1,7 @@ /* * (C) 1999 Lars Knoll (knoll@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 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 @@ -103,6 +103,18 @@ RenderObject::SelectionState InlineTextBox::selectionState() return state; } +typedef Vector<UChar, 256> BufferForAppendingHyphen; + +static void adjustCharactersAndLengthForHyphen(BufferForAppendingHyphen& charactersWithHyphen, RenderStyle* style, const UChar*& characters, int& length) +{ + const AtomicString& hyphenString = style->hyphenString(); + charactersWithHyphen.reserveCapacity(length + hyphenString.length()); + charactersWithHyphen.append(characters, length); + charactersWithHyphen.append(hyphenString.characters(), hyphenString.length()); + characters = charactersWithHyphen.data(); + length += hyphenString.length(); +} + IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos) { int sPos = max(startPos - m_start, 0); @@ -114,9 +126,18 @@ IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos) RenderText* textObj = textRenderer(); int selTop = selectionTop(); int selHeight = selectionHeight(); - const Font& f = textObj->style(m_firstLine)->font(); + RenderStyle* styleToUse = textObj->style(m_firstLine); + const Font& f = styleToUse->font(); + + const UChar* characters = textObj->text()->characters() + m_start; + int len = m_len; + BufferForAppendingHyphen charactersWithHyphen; + if (ePos == len && hasHyphen()) { + adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, len); + ePos = len; + } - IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(textObj->text()->characters() + m_start, m_len, textObj->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride), + IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(characters, len, textObj->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride), IntPoint(tx + m_x, ty + selTop), selHeight, sPos, ePos)); if (r.x() > tx + m_x + m_width) r.setWidth(0); @@ -476,16 +497,21 @@ void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) } } + const UChar* characters = textRenderer()->text()->characters() + m_start; + int length = m_len; + BufferForAppendingHyphen charactersWithHyphen; + if (hasHyphen()) + adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, length); + int baseline = renderer()->style(m_firstLine)->font().ascent(); IntPoint textOrigin(m_x + tx, m_y + ty + baseline); - TextRun textRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()); + TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()); int sPos = 0; int ePos = 0; 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); @@ -600,8 +626,16 @@ void InlineTextBox::paintSelection(GraphicsContext* context, int tx, int ty, Ren // 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; + const UChar* characters = textRenderer()->text()->characters() + m_start; + + BufferForAppendingHyphen charactersWithHyphen; + if (ePos == length && hasHyphen()) { + adjustCharactersAndLengthForHyphen(charactersWithHyphen, style, characters, length); + ePos = length; + } + context->clip(IntRect(m_x + tx, y + ty, m_width, h)); - context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, length, textRenderer()->allowTabs(), textPos(), m_toAdd, + context->drawHighlightForText(font, TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), IntPoint(m_x + tx, y + ty), h, c, style->colorSpace(), sPos, ePos); context->restore(); diff --git a/WebCore/rendering/InlineTextBox.h b/WebCore/rendering/InlineTextBox.h index d03de85..2ef5ec9 100644 --- a/WebCore/rendering/InlineTextBox.h +++ b/WebCore/rendering/InlineTextBox.h @@ -65,16 +65,21 @@ public: unsigned short truncation() { return m_truncation; } + bool hasHyphen() const { return m_hasEllipsisBoxOrHyphen; } + void setHasHyphen(bool hasHyphen) { m_hasEllipsisBoxOrHyphen = hasHyphen; } + private: virtual int selectionTop(); virtual int selectionHeight(); public: + virtual IntRect calculateBoundaries() const { return IntRect(x(), y(), width(), height()); } + virtual IntRect selectionRect(int absx, int absy, int startPos, int endPos); bool isSelected(int startPos, int endPos) const; - void selectionStartEnd(int& sPos, int& ePos); + virtual void selectionStartEnd(int& sPos, int& ePos); -private: +protected: virtual void paint(RenderObject::PaintInfo&, int tx, int ty); virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); @@ -99,7 +104,7 @@ public: void setSpaceAdd(int add) { m_width -= m_toAdd; m_toAdd = add; m_width += m_toAdd; } private: - virtual bool isInlineTextBox() { return true; } + virtual bool isInlineTextBox() const { return true; } public: virtual int caretMinOffset() const; diff --git a/WebCore/rendering/MediaControlElements.cpp b/WebCore/rendering/MediaControlElements.cpp index 3413b02..383a8cf 100644 --- a/WebCore/rendering/MediaControlElements.cpp +++ b/WebCore/rendering/MediaControlElements.cpp @@ -70,7 +70,7 @@ inline MediaControlShadowRootElement::MediaControlShadowRootElement(HTMLMediaEle PassRefPtr<MediaControlShadowRootElement> MediaControlShadowRootElement::create(HTMLMediaElement* mediaElement) { - RefPtr<MediaControlShadowRootElement> element = new MediaControlShadowRootElement(mediaElement); + RefPtr<MediaControlShadowRootElement> element = adoptRef(new MediaControlShadowRootElement(mediaElement)); RefPtr<RenderStyle> rootStyle = RenderStyle::create(); rootStyle->inheritFrom(mediaElement->renderer()->style()); @@ -130,7 +130,7 @@ MediaControlElement::MediaControlElement(HTMLMediaElement* mediaElement, PseudoI PassRefPtr<MediaControlElement> MediaControlElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId) { - return new MediaControlElement(mediaElement, pseudoStyleId); + return adoptRef(new MediaControlElement(mediaElement, pseudoStyleId)); } void MediaControlElement::attachToParent(Element* parent) @@ -222,7 +222,7 @@ inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElemen PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlTimelineContainerElement(mediaElement); + return adoptRef(new MediaControlTimelineContainerElement(mediaElement)); } bool MediaControlTimelineContainerElement::rendererIsNeeded(RenderStyle* style) @@ -252,7 +252,7 @@ inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContain PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlVolumeSliderContainerElement(mediaElement); + return adoptRef(new MediaControlVolumeSliderContainerElement(mediaElement)); } PassRefPtr<RenderStyle> MediaControlVolumeSliderContainerElement::styleForElement() @@ -298,7 +298,7 @@ inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(HTMLMe PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlStatusDisplayElement(mediaElement); + return adoptRef(new MediaControlStatusDisplayElement(mediaElement)); } void MediaControlStatusDisplayElement::update() @@ -482,7 +482,7 @@ inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(HTMLMediaEle PassRefPtr<MediaControlMuteButtonElement> MediaControlMuteButtonElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlMuteButtonElement(mediaElement); + return adoptRef(new MediaControlMuteButtonElement(mediaElement)); } void MediaControlMuteButtonElement::defaultEventHandler(Event* event) @@ -508,7 +508,7 @@ inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(HTMLMediaEle PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlPlayButtonElement(mediaElement); + return adoptRef(new MediaControlPlayButtonElement(mediaElement)); } void MediaControlPlayButtonElement::defaultEventHandler(Event* event) @@ -537,7 +537,7 @@ inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(HTMLMediaEle PassRefPtr<MediaControlSeekButtonElement> MediaControlSeekButtonElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId) { - return new MediaControlSeekButtonElement(mediaElement, pseudoStyleId); + return adoptRef(new MediaControlSeekButtonElement(mediaElement, pseudoStyleId)); } inline bool MediaControlSeekButtonElement::isForwardButton() const @@ -601,7 +601,7 @@ inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(HTMLMedi PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlRewindButtonElement(mediaElement); + return adoptRef(new MediaControlRewindButtonElement(mediaElement)); } void MediaControlRewindButtonElement::defaultEventHandler(Event* event) @@ -623,7 +623,7 @@ inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeBu PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlReturnToRealtimeButtonElement(mediaElement); + return adoptRef(new MediaControlReturnToRealtimeButtonElement(mediaElement)); } void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event) @@ -645,7 +645,7 @@ inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCa PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlToggleClosedCaptionsButtonElement(mediaElement); + return adoptRef(new MediaControlToggleClosedCaptionsButtonElement(mediaElement)); } void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event) @@ -672,7 +672,7 @@ MediaControlTimelineElement::MediaControlTimelineElement(HTMLMediaElement* media PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlTimelineElement(mediaElement); + return adoptRef(new MediaControlTimelineElement(mediaElement)); } void MediaControlTimelineElement::defaultEventHandler(Event* event) @@ -725,7 +725,7 @@ inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(HTMLMedi PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlVolumeSliderElement(mediaElement); + return adoptRef(new MediaControlVolumeSliderElement(mediaElement)); } void MediaControlVolumeSliderElement::defaultEventHandler(Event* event) @@ -767,7 +767,7 @@ inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement( PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(HTMLMediaElement* mediaElement) { - return new MediaControlFullscreenButtonElement(mediaElement); + return adoptRef(new MediaControlFullscreenButtonElement(mediaElement)); } void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event) @@ -790,7 +790,7 @@ inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(HTMLMediaE PassRefPtr<MediaControlTimeDisplayElement> MediaControlTimeDisplayElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId) { - return new MediaControlTimeDisplayElement(mediaElement, pseudoStyleId); + return adoptRef(new MediaControlTimeDisplayElement(mediaElement, pseudoStyleId)); } PassRefPtr<RenderStyle> MediaControlTimeDisplayElement::styleForElement() diff --git a/WebCore/rendering/RenderApplet.cpp b/WebCore/rendering/RenderApplet.cpp index a1689a5..12eb4fb 100644 --- a/WebCore/rendering/RenderApplet.cpp +++ b/WebCore/rendering/RenderApplet.cpp @@ -72,7 +72,7 @@ void RenderApplet::createWidgetIfNecessary() Frame* frame = this->frame(); ASSERT(frame); - setWidget(frame->loader()->createJavaAppletWidget(IntSize(contentWidth, contentHeight), element, m_args)); + setWidget(frame->loader()->subframeLoader()->createJavaAppletWidget(IntSize(contentWidth, contentHeight), element, m_args)); } void RenderApplet::layout() diff --git a/WebCore/rendering/RenderBlock.h b/WebCore/rendering/RenderBlock.h index 8c61e2c..e3a11c8 100644 --- a/WebCore/rendering/RenderBlock.h +++ b/WebCore/rendering/RenderBlock.h @@ -38,7 +38,7 @@ class RenderInline; struct BidiRun; template <class Iterator, class Run> class BidiResolver; -template <class Iterator> class MidpointState; +template <class Iterator> struct MidpointState; typedef BidiResolver<InlineIterator, BidiRun> InlineBidiResolver; typedef MidpointState<InlineIterator> LineMidpointState; @@ -227,7 +227,19 @@ protected: virtual bool hasLineIfEmpty() const; bool layoutOnlyPositionedObjects(); - + +#if ENABLE(SVG) +protected: + + // Only used by RenderSVGText, which explicitely overrides RenderBlock::layoutBlock(), do NOT use for anything else. + void forceLayoutInlineChildren() + { + int repaintTop = 0; + int repaintBottom = 0; + layoutInlineChildren(true, repaintTop, repaintBottom); + } +#endif + private: virtual RenderObjectChildList* virtualChildren() { return children(); } virtual const RenderObjectChildList* virtualChildren() const { return children(); } @@ -295,7 +307,7 @@ private: void skipTrailingWhitespace(InlineIterator&, bool isLineEmpty, bool previousLineBrokeCleanly); int skipLeadingWhitespace(InlineBidiResolver&, bool firstLine, bool isLineEmpty, bool previousLineBrokeCleanly); void fitBelowFloats(int widthToFit, bool firstLine, int& availableWidth); - InlineIterator findNextLineBreak(InlineBidiResolver&, bool firstLine, bool& isLineEmpty, bool& previousLineBrokeCleanly, EClear* clear = 0); + InlineIterator findNextLineBreak(InlineBidiResolver&, bool firstLine, bool& isLineEmpty, bool& previousLineBrokeCleanly, bool& hyphenated, EClear* = 0); RootInlineBox* constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool firstLine, bool lastLine, RenderObject* endObject); InlineFlowBox* createLineBoxes(RenderObject*, bool firstLine); void computeHorizontalPositionsForLine(RootInlineBox*, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap&); diff --git a/WebCore/rendering/RenderBlockLineLayout.cpp b/WebCore/rendering/RenderBlockLineLayout.cpp index be7c76b..097b661 100644 --- a/WebCore/rendering/RenderBlockLineLayout.cpp +++ b/WebCore/rendering/RenderBlockLineLayout.cpp @@ -24,6 +24,7 @@ #include "BidiResolver.h" #include "CharacterNames.h" +#include "Hyphenation.h" #include "InlineIterator.h" #include "InlineTextBox.h" #include "Logging.h" @@ -46,6 +47,10 @@ #include "HTMLNames.h" #endif // ANDROID_LAYOUT +#if ENABLE(SVG) +#include "SVGRootInlineBox.h" +#endif + using namespace std; using namespace WTF; using namespace Unicode; @@ -298,6 +303,8 @@ RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, text->setStart(r->m_start); text->setLen(r->m_stop - r->m_start); text->m_dirOverride = r->dirOverride(visuallyOrdered); + if (r->m_hasHyphen) + text->setHasHyphen(true); } } @@ -351,22 +358,19 @@ void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, bool } HashSet<const SimpleFontData*> fallbackFonts; GlyphOverflow glyphOverflow; - r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, firstLine, &fallbackFonts, &glyphOverflow)); - if (!fallbackFonts.isEmpty() -#if ENABLE(SVG) - && !isSVGText() -#endif - ) { + int hyphenWidth = 0; + if (static_cast<InlineTextBox*>(r->m_box)->hasHyphen()) { + const AtomicString& hyphenString = rt->style()->hyphenString(); + hyphenWidth = rt->style(firstLine)->font().width(TextRun(hyphenString.characters(), hyphenString.length())); + } + r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, firstLine, &fallbackFonts, &glyphOverflow) + hyphenWidth); + if (!fallbackFonts.isEmpty()) { ASSERT(r->m_box->isText()); GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast<InlineTextBox*>(r->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).first; ASSERT(it->second.first.isEmpty()); copyToVector(fallbackFonts, it->second.first); } - if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right) -#if ENABLE(SVG) - && !isSVGText() -#endif - ) { + if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) { ASSERT(r->m_box->isText()); GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast<InlineTextBox*>(r->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).first; it->second.second = glyphOverflow; @@ -795,7 +799,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i isLineEmpty = true; EClear clear = CNONE; - end = findNextLineBreak(resolver, firstLine, isLineEmpty, previousLineBrokeCleanly, &clear); + bool hyphenated; + end = findNextLineBreak(resolver, firstLine, isLineEmpty, previousLineBrokeCleanly, hyphenated, &clear); if (resolver.position().atEnd()) { resolver.deleteRuns(); checkForFloatsFromLastLine = true; @@ -861,20 +866,37 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, i RootInlineBox* lineBox = 0; if (resolver.runCount()) { + if (hyphenated) + resolver.logicallyLastRun()->m_hasHyphen = true; lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), firstLine, !end.obj, end.obj && !end.pos ? end.obj : 0); if (lineBox) { lineBox->setEndsWithBreak(previousLineBrokeCleanly); - // Now we position all of our text runs horizontally. +#if ENABLE(SVG) + bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox(); +#else + bool isSVGRootInlineBox = false; +#endif + GlyphOverflowAndFallbackFontsMap textBoxDataMap; - computeHorizontalPositionsForLine(lineBox, firstLine, resolver.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap); + + // Now we position all of our text runs horizontally. + if (!isSVGRootInlineBox) + computeHorizontalPositionsForLine(lineBox, firstLine, resolver.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap); // Now position our text runs vertically. computeVerticalPositionsForLine(lineBox, resolver.firstRun(), textBoxDataMap); #if ENABLE(SVG) - // Special SVG text layout code - lineBox->computePerCharacterLayoutInformation(); + // SVG text layout code computes vertical & horizontal positions on its own. + // Note that we still need to execute computeVerticalPositionsForLine() as + // it calls InlineTextBox::positionLineBox(), which tracks whether the box + // contains reversed text or not. If we wouldn't do that editing and thus + // text selection in RTL boxes would not work as expected. + if (isSVGRootInlineBox) { + ASSERT(isSVGText()); + static_cast<SVGRootInlineBox*>(lineBox)->computePerCharacterLayoutInformation(); + } #endif #if PLATFORM(MAC) @@ -1262,7 +1284,7 @@ static inline bool shouldCollapseWhiteSpace(const RenderStyle* style, bool isLin static inline bool shouldPreserveNewline(RenderObject* object) { #if ENABLE(SVG) - if (object->isSVGText()) + if (object->isSVGInlineText()) return false; #endif @@ -1429,8 +1451,34 @@ static inline unsigned textWidth(RenderText* text, unsigned from, unsigned len, return font.width(TextRun(text->characters() + from, len, !collapseWhiteSpace, xPos)); } +static void tryHyphenating(RenderText* text, const Font& font, int lastSpace, int pos, int xPos, int availableWidth, bool isFixedPitch, bool collapseWhiteSpace, int lastSpaceWordSpacing, InlineIterator& lineBreak, int nextBreakable, bool& hyphenated) +{ + const AtomicString& hyphenString = text->style()->hyphenString(); + int hyphenWidth = font.width(TextRun(hyphenString.characters(), hyphenString.length())); + + unsigned prefixLength = font.offsetForPosition(TextRun(text->characters() + lastSpace, pos - lastSpace, !collapseWhiteSpace, xPos + lastSpaceWordSpacing), availableWidth - xPos - hyphenWidth - lastSpaceWordSpacing, false); + if (!prefixLength) + return; + + prefixLength = 1 + lastHyphenLocation(text->characters() + lastSpace + 1, pos - lastSpace - 1, prefixLength - 1); + if (prefixLength <= 1) + return; + +#if !ASSERT_DISABLED + int prefixWidth = hyphenWidth + textWidth(text, lastSpace, prefixLength, font, xPos, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + ASSERT(xPos + prefixWidth <= availableWidth); +#else + UNUSED_PARAM(isFixedPitch); +#endif + + lineBreak.obj = text; + lineBreak.pos = lastSpace + prefixLength; + lineBreak.nextBreakablePosition = nextBreakable; + hyphenated = true; +} + InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool firstLine, bool& isLineEmpty, bool& previousLineBrokeCleanly, - EClear* clear) + bool& hyphenated, EClear* clear) { ASSERT(resolver.position().block == this); @@ -1468,6 +1516,8 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool bool prevLineBrokeCleanly = previousLineBrokeCleanly; previousLineBrokeCleanly = false; + hyphenated = false; + bool autoWrapWasEverTrueOnLine = false; bool floatsFitOnLine = true; @@ -1486,7 +1536,7 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap; #if ENABLE(SVG) - bool preserveNewline = o->isSVGText() ? false : RenderStyle::preserveNewline(currWS); + bool preserveNewline = o->isSVGInlineText() ? false : RenderStyle::preserveNewline(currWS); #else bool preserveNewline = RenderStyle::preserveNewline(currWS); #endif @@ -1641,8 +1691,10 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool int len = strlen - pos; const UChar* str = t->characters(); - const Font& f = t->style(firstLine)->font(); + RenderStyle* style = t->style(firstLine); + const Font& f = style->font(); bool isFixedPitch = f.isFixedPitch(); + bool canHyphenate = style->hyphens() == HyphensAuto; int lastSpace = pos; int wordSpacing = o->style()->wordSpacing(); @@ -1712,6 +1764,11 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool len--; lastSpaceWordSpacing = 0; lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice. + if (style->hyphens() == HyphensNone) { + // Prevent a line break at the soft hyphen by ensuring that betweenWords is false + // in the next iteration. + atStart = true; + } continue; } @@ -1782,6 +1839,11 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } } if (lineWasTooWide || w + tmpW > width) { + if (canHyphenate && w + tmpW > width) { + tryHyphenating(t, f, lastSpace, pos, w + tmpW - additionalTmpW, width, isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, nextBreakable, hyphenated); + if (hyphenated) + goto end; + } if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && toRenderText(lBreak.obj)->textLength() && !toRenderText(lBreak.obj)->isWordBreak() && toRenderText(lBreak.obj)->characters()[lBreak.pos] == '\n') { if (!stoppedIgnoringSpaces && pos > 0) { // We need to stop right before the newline and then start up again. @@ -1887,9 +1949,15 @@ InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool } // IMPORTANT: pos is > length here! - if (!ignoringSpaces) - tmpW += textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + int additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + tmpW += additionalTmpW; tmpW += inlineWidth(o, !appliedStartWidth, true); + + if (canHyphenate && w + tmpW > width) { + tryHyphenating(t, f, lastSpace, pos, w + tmpW - additionalTmpW, width, isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, nextBreakable, hyphenated); + if (hyphenated) + goto end; + } } else ASSERT_NOT_REACHED(); diff --git a/WebCore/rendering/RenderBoxModelObject.cpp b/WebCore/rendering/RenderBoxModelObject.cpp index 196e8c5..3f1b5ae 100644 --- a/WebCore/rendering/RenderBoxModelObject.cpp +++ b/WebCore/rendering/RenderBoxModelObject.cpp @@ -4,6 +4,7 @@ * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * 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 @@ -48,77 +49,57 @@ 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) - { - } +typedef HashMap<RenderBoxModelObject*, IntSize> LastPaintSizeMap; - ~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); - } +class ImageQualityController : public Noncopyable { +public: + ImageQualityController(); + bool shouldPaintAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const IntSize&); + void objectDestroyed(RenderBoxModelObject*); private: - IntSize m_size; - AffineTransform m_transform; - double m_lastPaintTime; - bool m_lowQualityScale; - Timer<RenderBoxModelObject> m_highQualityRepaintTimer; + void highQualityRepaintTimerFired(Timer<ImageQualityController>*); + void restartTimer(); + + LastPaintSizeMap m_lastPaintSizeMap; + Timer<ImageQualityController> m_timer; + bool m_animatedResizeIsActive; }; -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; - } - } - } +ImageQualityController::ImageQualityController() + : m_timer(this, &ImageQualityController::highQualityRepaintTimerFired) + , m_animatedResizeIsActive(false) +{ +} - static void highQualityRepaintTimerFired(RenderBoxModelObject* object) - { - RenderBoxModelScaleObserver::boxModelObjectDestroyed(object); - object->repaint(); +void ImageQualityController::objectDestroyed(RenderBoxModelObject* object) +{ + m_lastPaintSizeMap.remove(object); + if (m_lastPaintSizeMap.isEmpty()) { + m_animatedResizeIsActive = false; + m_timer.stop(); + } +} + +void ImageQualityController::highQualityRepaintTimerFired(Timer<ImageQualityController>*) +{ + if (m_animatedResizeIsActive) { + m_animatedResizeIsActive = false; + for (LastPaintSizeMap::iterator it = m_lastPaintSizeMap.begin(); it != m_lastPaintSizeMap.end(); ++it) + it->first->repaint(); } +} - static HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>* gBoxModelObjects; -}; +void ImageQualityController::restartTimer() +{ + m_timer.startOneShot(cLowQualityTimeThreshold); +} -bool RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const IntSize& size) +bool ImageQualityController::shouldPaintAtLowQuality(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()) + if (!image || !image->isBitmapImage() || context->paintingDisabled()) return false; // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image @@ -126,59 +107,57 @@ bool RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality(GraphicsCont IntSize imageSize(image->width(), image->height()); // Look ourselves up in the hashtable. - RenderBoxModelScaleData* data = 0; - if (gBoxModelObjects) - data = gBoxModelObjects->get(object); + LastPaintSizeMap::iterator i = m_lastPaintSizeMap.find(object); const AffineTransform& currentTransform = context->getCTM(); bool contextIsScaled = !currentTransform.isIdentityOrTranslationOrFlipped(); 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; - } + // There is no scale in effect. If we had a scale in effect before, we can just remove this object from the list. + if (i != m_lastPaintSizeMap.end()) + m_lastPaintSizeMap.remove(object); + 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. + // 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); + // If an animated resize is active, paint in low quality and kick the timer ahead. + if (m_animatedResizeIsActive) { + m_lastPaintSizeMap.set(object, size); + restartTimer(); + return true; + } + // If this is the first time resizing this image, or its size is the + // same as the last resize, draw at high res, but record the paint + // size and set the timer. + if (i == m_lastPaintSizeMap.end() || size == i->second) { + restartTimer(); + m_lastPaintSizeMap.set(object, size); return false; } - - const AffineTransform& tr = data->transform(); - bool scaleUnchanged = tr.a() == currentTransform.a() && tr.b() == currentTransform.b() && tr.c() == currentTransform.c() && tr.d() == currentTransform.d(); - // We are scaled, but we painted already at this size, so just keep using whatever mode we last painted with. - if ((!contextIsScaled || scaleUnchanged) && 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(); + // If the timer is no longer active, draw at high quality and don't + // set the timer. + if (!m_timer.isActive()) { + objectDestroyed(object); + return false; + } + // This object has been resized to two different sizes while the timer + // is active, so draw at low quality, set the flag for animated resizes and + // the object to the list for high quality redraw. + m_lastPaintSizeMap.set(object, size); + m_animatedResizeIsActive = true; + restartTimer(); + return true; } -HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>* RenderBoxModelScaleObserver::gBoxModelObjects = 0; - -void RenderBoxModelObject::highQualityRepaintTimerFired(Timer<RenderBoxModelObject>*) +static ImageQualityController* imageQualityController() { - RenderBoxModelScaleObserver::highQualityRepaintTimerFired(this); + static ImageQualityController* controller = new ImageQualityController; + return controller; } void RenderBoxModelObject::setSelectionState(SelectionState s) @@ -203,6 +182,10 @@ void RenderBoxModelObject::setSelectionState(SelectionState s) cb->setSelectionState(s); } +bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Image* image, const IntSize& size) +{ + return imageQualityController()->shouldPaintAtLowQuality(context, this, image, size); +} RenderBoxModelObject::RenderBoxModelObject(Node* node) : RenderObject(node) @@ -215,7 +198,7 @@ RenderBoxModelObject::~RenderBoxModelObject() // Our layer should have been destroyed and cleared by now ASSERT(!hasLayer()); ASSERT(!m_layer); - RenderBoxModelScaleObserver::boxModelObjectDestroyed(this); + imageQualityController()->objectDestroyed(this); } void RenderBoxModelObject::destroyLayer() @@ -464,7 +447,6 @@ int RenderBoxModelObject::paddingRight(bool) const return padding.calcMinValue(w); } - 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; @@ -631,7 +613,7 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this; Image* image = bg->image(clientForBackgroundImage, tileSize); - bool useLowQualityScaling = RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality(context, this, image, tileSize); + bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, tileSize); context->drawTiledImage(image, style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling); } } diff --git a/WebCore/rendering/RenderBoxModelObject.h b/WebCore/rendering/RenderBoxModelObject.h index dc2cf18..f022495 100644 --- a/WebCore/rendering/RenderBoxModelObject.h +++ b/WebCore/rendering/RenderBoxModelObject.h @@ -2,6 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * Copyright (C) 2003, 2006, 2007, 2009 Apple Inc. All rights reserved. + * 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 @@ -107,6 +108,8 @@ public: protected: void calculateBackgroundImageGeometry(const FillLayer*, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize); + bool shouldPaintAtLowQuality(GraphicsContext*, Image*, const IntSize&); + private: virtual bool isBoxModelObject() const { return true; } diff --git a/WebCore/rendering/RenderEmbeddedObject.cpp b/WebCore/rendering/RenderEmbeddedObject.cpp index 3d3278a..7ab12a1 100644 --- a/WebCore/rendering/RenderEmbeddedObject.cpp +++ b/WebCore/rendering/RenderEmbeddedObject.cpp @@ -165,7 +165,7 @@ void RenderEmbeddedObject::updateWidget(bool onlyCreateNonNetscapePlugins) Vector<String> paramValues; Frame* frame = frameView()->frame(); - // The calls to FrameLoader::requestObject within this function can result in a plug-in being initialized. + // The calls to SubframeLoader::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. @@ -284,7 +284,16 @@ void RenderEmbeddedObject::updateWidget(bool onlyCreateNonNetscapePlugins) return; } - bool success = objectElement->dispatchBeforeLoadEvent(url) && frame->loader()->requestObject(this, url, objectElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); + bool beforeLoadAllowedLoad = objectElement->dispatchBeforeLoadEvent(url); + + // beforeload events can modify the DOM, potentially causing + // RenderWidget::destroy() to be called. Ensure we haven't been + // destroyed before continuing. + if (!node()) + return; + + bool success = beforeLoadAllowedLoad && frame->loader()->subframeLoader()->requestObject(this, url, objectElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); + if (!success && m_hasFallbackContent) objectElement->renderFallbackContent(); @@ -319,7 +328,7 @@ void RenderEmbeddedObject::updateWidget(bool onlyCreateNonNetscapePlugins) } if (embedElement->dispatchBeforeLoadEvent(url)) - frame->loader()->requestObject(this, url, embedElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); + frame->loader()->subframeLoader()->requestObject(this, url, embedElement->getAttribute(nameAttr), serviceType, paramNames, paramValues); } #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) else if (node()->hasTagName(videoTag) || node()->hasTagName(audioTag)) { @@ -328,7 +337,7 @@ void RenderEmbeddedObject::updateWidget(bool onlyCreateNonNetscapePlugins) mediaElement->getPluginProxyParams(kurl, paramNames, paramValues); mediaElement->setNeedWidgetUpdate(false); - frame->loader()->loadMediaPlayerProxyPlugin(node(), kurl, paramNames, paramValues); + frame->loader()->subframeLoader()->loadMediaPlayerProxyPlugin(node(), kurl, paramNames, paramValues); } #endif } diff --git a/WebCore/rendering/RenderFileUploadControl.cpp b/WebCore/rendering/RenderFileUploadControl.cpp index f42a657..c652276 100644 --- a/WebCore/rendering/RenderFileUploadControl.cpp +++ b/WebCore/rendering/RenderFileUploadControl.cpp @@ -53,7 +53,6 @@ const int buttonShadowHeight = 2; RenderFileUploadControl::RenderFileUploadControl(HTMLInputElement* input) : RenderBlock(input) - , m_button(0) { FileList* list = input->files(); Vector<String> filenames; diff --git a/WebCore/rendering/RenderImage.cpp b/WebCore/rendering/RenderImage.cpp index 98b0f47..643fac9 100644 --- a/WebCore/rendering/RenderImage.cpp +++ b/WebCore/rendering/RenderImage.cpp @@ -5,6 +5,7 @@ * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights 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 @@ -56,133 +57,6 @@ using namespace std; namespace WebCore { -static const double cInterpolationCutoff = 800. * 800.; -static const double cLowQualityTimeThreshold = 0.050; // 50 ms - -class RenderImageScaleData : public Noncopyable { -public: - RenderImageScaleData(RenderImage* image, const IntSize& size, double time, bool lowQualityScale) - : m_size(size) - , m_time(time) - , m_lowQualityScale(lowQualityScale) - , m_highQualityRepaintTimer(image, &RenderImage::highQualityRepaintTimerFired) - { - } - - ~RenderImageScaleData() - { - m_highQualityRepaintTimer.stop(); - } - - const IntSize& size() const { return m_size; } - double time() const { return m_time; } - bool useLowQualityScale() const { return m_lowQualityScale; } - Timer<RenderImage>& hiqhQualityRepaintTimer() { return m_highQualityRepaintTimer; } - - void setSize(const IntSize& s) { m_size = s; } - void setTime(double t) { m_time = t; } - void setUseLowQualityScale(bool b) - { - m_highQualityRepaintTimer.stop(); - m_lowQualityScale = b; - if (b) - m_highQualityRepaintTimer.startOneShot(cLowQualityTimeThreshold); - } - -private: - IntSize m_size; - double m_time; - bool m_lowQualityScale; - Timer<RenderImage> m_highQualityRepaintTimer; -}; - -class RenderImageScaleObserver { -public: - static bool shouldImagePaintAtLowQuality(RenderImage*, const IntSize&); - - static void imageDestroyed(RenderImage* image) - { - if (gImages) { - RenderImageScaleData* data = gImages->take(image); - delete data; - if (gImages->size() == 0) { - delete gImages; - gImages = 0; - } - } - } - - static void highQualityRepaintTimerFired(RenderImage* image) - { - RenderImageScaleObserver::imageDestroyed(image); - image->repaint(); - } - - static HashMap<RenderImage*, RenderImageScaleData*>* gImages; -}; - -bool RenderImageScaleObserver::shouldImagePaintAtLowQuality(RenderImage* 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() || !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->image()->width(), image->image()->height()); - - // Look ourselves up in the hashtable. - RenderImageScaleData* data = 0; - if (gImages) - data = gImages->get(image); - - if (imageSize == size) { - // There is no scale in effect. If we had a scale in effect before, we can just delete this data. - if (data) { - gImages->remove(image); - 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 (image->document()->page()->inLowQualityImageInterpolationMode()) { - double totalPixels = static_cast<double>(image->image()->width()) * static_cast<double>(image->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 RenderImageScaleData(image, size, currentTime(), false); - if (!gImages) - gImages = new HashMap<RenderImage*, RenderImageScaleData*>; - gImages->set(image, data); - return false; - } - - // We are scaled, but we painted already at this size, so just keep using whatever mode we last painted with. - if (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->time() < cLowQualityTimeThreshold); - data->setTime(newTime); - data->setSize(size); - return data->useLowQualityScale(); -} - -HashMap<RenderImage*, RenderImageScaleData*>* RenderImageScaleObserver::gImages = 0; - -void RenderImage::highQualityRepaintTimerFired(Timer<RenderImage>*) -{ - RenderImageScaleObserver::highQualityRepaintTimerFired(this); -} - using namespace HTMLNames; RenderImage::RenderImage(Node* node) @@ -198,7 +72,6 @@ RenderImage::~RenderImage() { if (m_cachedImage) m_cachedImage->removeClient(this); - RenderImageScaleObserver::imageDestroyed(this); } void RenderImage::setCachedImage(CachedImage* newImage) @@ -497,7 +370,7 @@ void RenderImage::paintIntoRect(GraphicsContext* context, const IntRect& rect) HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(node()) : 0; CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; - bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, rect.size()); + bool useLowQualityScaling = shouldPaintAtLowQuality(context, this->image(), rect.size()); context->drawImage(image(rect.width(), rect.height()), style()->colorSpace(), rect, compositeOperator, useLowQualityScaling); } diff --git a/WebCore/rendering/RenderIndicator.cpp b/WebCore/rendering/RenderIndicator.cpp new file mode 100644 index 0000000..afff9c2 --- /dev/null +++ b/WebCore/rendering/RenderIndicator.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#if ENABLE(PROGRESS_TAG) || ENABLE(METER_TAG) + +#include "RenderIndicator.h" + +#include "RenderTheme.h" + +using namespace std; + +namespace WebCore { + +RenderIndicator::RenderIndicator(Node* node) + : RenderBlock(node) +{ +} + +RenderIndicator::~RenderIndicator() +{ +} + +void RenderIndicator::layout() +{ + ASSERT(needsLayout()); + + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); + calcWidth(); + calcHeight(); + layoutParts(); + repainter.repaintAfterLayout(); + setNeedsLayout(false); +} + +void RenderIndicator::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + requestLayoutForParts(); +} + +void RenderIndicator::updateFromElement() +{ + requestLayoutForParts(); + repaint(); +} + +bool RenderIndicator::hasParts() const +{ + if (RenderObject* last = lastChild()) + return last->isRenderBlock(); + return false; +} + +void RenderIndicator::requestLayoutForParts() +{ + if (shouldHaveParts() || hasParts()) + setNeedsLayout(true); +} + +} // namespace WebCore + +#endif diff --git a/WebCore/rendering/RenderIndicator.h b/WebCore/rendering/RenderIndicator.h new file mode 100644 index 0000000..2d82a09 --- /dev/null +++ b/WebCore/rendering/RenderIndicator.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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 RenderIndicator_h +#define RenderIndicator_h + +#if ENABLE(PROGRESS_TAG) || ENABLE(METER_TAG) +#include "RenderBlock.h" + +namespace WebCore { + +class RenderIndicator : public RenderBlock { +public: + RenderIndicator(Node*); + virtual ~RenderIndicator(); + +protected: + virtual void layout(); + virtual void updateFromElement(); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + virtual bool requiresForcedStyleRecalcPropagation() const { return true; } + + virtual void layoutParts() = 0; + virtual bool shouldHaveParts() const = 0; + +private: + void requestLayoutForParts(); + bool hasParts() const; +}; + +} // namespace WebCore + +#endif + +#endif // RenderIndicator_h + diff --git a/WebCore/rendering/RenderLayer.cpp b/WebCore/rendering/RenderLayer.cpp index 5d206dd..fcb9c07 100644 --- a/WebCore/rendering/RenderLayer.cpp +++ b/WebCore/rendering/RenderLayer.cpp @@ -376,6 +376,14 @@ void RenderLayer::updateLayerPositions(UpdateLayerPositionsFlags flags, IntPoint *cachedOffset = oldCachedOffset; } +IntRect RenderLayer::repaintRectIncludingDescendants() const +{ + IntRect repaintRect = m_repaintRect; + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + repaintRect.unite(child->repaintRectIncludingDescendants()); + return repaintRect; +} + void RenderLayer::computeRepaintRects() { RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); diff --git a/WebCore/rendering/RenderLayer.h b/WebCore/rendering/RenderLayer.h index f300d76..45b64e0 100644 --- a/WebCore/rendering/RenderLayer.h +++ b/WebCore/rendering/RenderLayer.h @@ -403,6 +403,7 @@ public: // Return a cached repaint rect, computed relative to the layer renderer's containerForRepaint. IntRect repaintRect() const { return m_repaintRect; } + IntRect repaintRectIncludingDescendants() const; void computeRepaintRects(); void updateRepaintRectsAfterScroll(bool fixed = false); void setNeedsFullRepaint(bool f = true) { m_needsFullRepaint = f; } diff --git a/WebCore/rendering/RenderLayerBacking.cpp b/WebCore/rendering/RenderLayerBacking.cpp index b55a474..794b55a 100644 --- a/WebCore/rendering/RenderLayerBacking.cpp +++ b/WebCore/rendering/RenderLayerBacking.cpp @@ -216,8 +216,8 @@ bool RenderLayerBacking::updateGraphicsLayerConfiguration() else 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()); + if (context->graphicsContext3D()->platformLayer()) + m_graphicsLayer->setContentsToWebGL(context->graphicsContext3D()->platformLayer()); } #endif @@ -227,6 +227,18 @@ bool RenderLayerBacking::updateGraphicsLayerConfiguration() return layerConfigChanged; } +static IntRect clipBox(RenderBox* renderer) +{ + IntRect result = ClipRects::infiniteRect(); + if (renderer->hasOverflowClip()) + result = renderer->overflowClipRect(0, 0); + + if (renderer->hasClip()) + result.intersect(renderer->clipRect(0, 0)); + + return result; +} + void RenderLayerBacking::updateGraphicsLayerGeometry() { // If we haven't built z-order lists yet, wait until later. @@ -266,7 +278,8 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() if (compAncestor && compAncestor->backing()->hasClippingLayer()) { // If the compositing ancestor has a layer to clip children, we parent in that, and therefore // position relative to it. - graphicsLayerParentLocation = toRenderBox(compAncestor->renderer())->overflowClipRect(0, 0).location(); + IntRect clippingBox = clipBox(toRenderBox(compAncestor->renderer())); + graphicsLayerParentLocation = clippingBox.location(); } else graphicsLayerParentLocation = ancestorCompositingBounds.location(); @@ -302,7 +315,7 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() // If we have a layer that clips children, position it. IntRect clippingBox; if (m_clippingLayer) { - clippingBox = toRenderBox(renderer())->overflowClipRect(0, 0); + clippingBox = clipBox(toRenderBox(renderer())); m_clippingLayer->setPosition(FloatPoint() + (clippingBox.location() - localCompositingBounds.location())); m_clippingLayer->setSize(clippingBox.size()); m_clippingLayer->setOffsetFromRenderer(clippingBox.location() - IntPoint()); @@ -714,7 +727,7 @@ bool RenderLayerBacking::containsPaintedContent() const bool RenderLayerBacking::isDirectlyCompositedImage() const { RenderObject* renderObject = renderer(); - return renderObject->isImage() && !hasBoxDecorationsOrBackground(renderObject); + return renderObject->isImage() && !hasBoxDecorationsOrBackground(renderObject) && !renderObject->hasClip(); } void RenderLayerBacking::rendererContentChanged() @@ -726,7 +739,7 @@ void RenderLayerBacking::rendererContentChanged() #if ENABLE(3D_CANVAS) if (is3DCanvas(renderer())) { - m_graphicsLayer->setGraphicsContext3DNeedsDisplay(); + m_graphicsLayer->setContentsNeedsDisplay(); return; } #endif diff --git a/WebCore/rendering/RenderLayerCompositor.cpp b/WebCore/rendering/RenderLayerCompositor.cpp index f68623b..f2c0dc4 100644 --- a/WebCore/rendering/RenderLayerCompositor.cpp +++ b/WebCore/rendering/RenderLayerCompositor.cpp @@ -469,7 +469,7 @@ RenderLayer* RenderLayerCompositor::enclosingNonStackingClippingLayer(const Rend if (curr->isStackingContext()) return 0; - if (curr->renderer()->hasOverflowClip()) + if (curr->renderer()->hasOverflowClip() || curr->renderer()->hasClip()) return curr; } return 0; @@ -1229,9 +1229,8 @@ bool RenderLayerCompositor::clippedByAncestor(RenderLayer* layer) const // into the hierarchy between this layer and its children in the z-order hierarchy. bool RenderLayerCompositor::clipsCompositingDescendants(const RenderLayer* layer) const { - // FIXME: need to look at hasClip() too eventually return layer->hasCompositingDescendant() && - layer->renderer()->hasOverflowClip(); + (layer->renderer()->hasOverflowClip() || layer->renderer()->hasClip()); } bool RenderLayerCompositor::requiresCompositingForTransform(RenderObject* renderer) const diff --git a/WebCore/rendering/RenderMenuList.cpp b/WebCore/rendering/RenderMenuList.cpp index 871c10f..77fe3c2 100644 --- a/WebCore/rendering/RenderMenuList.cpp +++ b/WebCore/rendering/RenderMenuList.cpp @@ -314,7 +314,7 @@ void RenderMenuList::valueChanged(unsigned listIndex, bool fireOnChange) void RenderMenuList::listBoxSelectItem(int listIndex, bool allowMultiplySelections, bool shift, bool fireOnChangeNow) { SelectElement* select = toSelectElement(static_cast<Element*>(node())); - select->listBoxSelectItem(select->listToOptionIndex(listIndex), allowMultiplySelections, shift, fireOnChangeNow); + select->listBoxSelectItem(listIndex, allowMultiplySelections, shift, fireOnChangeNow); } bool RenderMenuList::multiple() diff --git a/WebCore/rendering/RenderMeter.cpp b/WebCore/rendering/RenderMeter.cpp index d443ceb..cfa7cba 100644 --- a/WebCore/rendering/RenderMeter.cpp +++ b/WebCore/rendering/RenderMeter.cpp @@ -27,6 +27,7 @@ #include "HTMLMeterElement.h" #include "HTMLNames.h" #include "RenderTheme.h" +#include "ShadowElement.h" using namespace std; @@ -35,10 +36,18 @@ namespace WebCore { using namespace HTMLNames; RenderMeter::RenderMeter(HTMLMeterElement* element) - : RenderBlock(element) + : RenderIndicator(element) { } +RenderMeter::~RenderMeter() +{ + if (m_valuePart) + m_valuePart->detach(); + if (m_barPart) + m_barPart->detach(); +} + void RenderMeter::calcWidth() { RenderBox::calcWidth(); @@ -51,25 +60,125 @@ void RenderMeter::calcHeight() setHeight(theme()->meterSizeForBounds(this, frameRect()).height()); } -void RenderMeter::layout() +void RenderMeter::layoutParts() { - ASSERT(needsLayout()); + // We refresh shadow node here because the state can depend + // on the frame size of this render object. + updatePartsState(); + if (m_valuePart) + m_valuePart->layoutAsPart(valuePartRect()); + if (m_barPart) + m_barPart->layoutAsPart(barPartRect()); +} - LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); +bool RenderMeter::shouldHaveParts() const +{ + bool hasTheme = theme()->supportsMeter(style()->appearance(), isHorizontal()); + if (!hasTheme) + return true; + bool shadowsHaveStyle = ShadowBlockElement::partShouldHaveStyle(this, barPseudoId()) || ShadowBlockElement::partShouldHaveStyle(this, valuePseudoId()); + if (shadowsHaveStyle) + return true; + return false; +} - calcWidth(); - calcHeight(); +double RenderMeter::valueRatio() const +{ + HTMLMeterElement* element = static_cast<HTMLMeterElement*>(node()); + double min = element->min(); + double max = element->max(); + double value = element->value(); + + if (max <= min) + return 0; + return (value - min) / (max - min); +} - m_overflow.clear(); +IntRect RenderMeter::barPartRect() const +{ + return IntRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), lround(width() - borderLeft() - paddingLeft() - borderRight() - paddingRight()), height() - borderTop() - paddingTop() - borderBottom() - paddingBottom()); +} - repainter.repaintAfterLayout(); +IntRect RenderMeter::valuePartRect() const +{ + IntRect rect = barPartRect(); + + if (rect.height() <= rect.width()) { + int width = static_cast<int>(rect.width()*valueRatio()); + if (style()->direction() == RTL) { + rect.setX(rect.x() + (rect.width() - width)); + rect.setWidth(width); + } else + rect.setWidth(width); + } else { + int height = static_cast<int>(rect.height()*valueRatio()); + rect.setY(rect.y() + (rect.height() - height)); + rect.setHeight(height); + } + + return rect; +} + +bool RenderMeter::isHorizontal() const +{ + IntRect rect = barPartRect(); + return rect.height() <= rect.width(); +} - setNeedsLayout(false); +PseudoId RenderMeter::valuePseudoId() const +{ + HTMLMeterElement* element = static_cast<HTMLMeterElement*>(node()); + + if (isHorizontal()) { + switch (element->gaugeRegion()) { + case HTMLMeterElement::GaugeRegionOptimum: + return METER_HORIZONTAL_OPTIMUM; + case HTMLMeterElement::GaugeRegionSuboptimal: + return METER_HORIZONTAL_SUBOPTIMAL; + case HTMLMeterElement::GaugeRegionEvenLessGood: + return METER_HORIZONTAL_EVEN_LESS_GOOD; + } + } else { + switch (element->gaugeRegion()) { + case HTMLMeterElement::GaugeRegionOptimum: + return METER_VERTICAL_OPTIMUM; + case HTMLMeterElement::GaugeRegionSuboptimal: + return METER_VERTICAL_SUBOPTIMAL; + case HTMLMeterElement::GaugeRegionEvenLessGood: + return METER_VERTICAL_EVEN_LESS_GOOD; + } + } + + ASSERT_NOT_REACHED(); + return NOPSEUDO; +} + +PseudoId RenderMeter::barPseudoId() const +{ + return isHorizontal() ? METER_HORIZONTAL_BAR : METER_VERTICAL_BAR; } -void RenderMeter::updateFromElement() +void RenderMeter::updatePartsState() { - repaint(); + if (shouldHaveParts() && !m_barPart) { + ASSERT(!m_valuePart); + m_barPart = ShadowBlockElement::createForPart(this->node(), barPseudoId()); + addChild(m_barPart->renderer()); + m_valuePart = ShadowBlockElement::createForPart(this->node(), valuePseudoId()); + addChild(m_valuePart->renderer()); + } else if (!shouldHaveParts() && m_barPart) { + ASSERT(m_valuePart); + m_barPart->detach(); + m_barPart = 0; + m_valuePart->detach(); + m_valuePart = 0; + } + + if (m_barPart) { + ASSERT(m_valuePart); + m_barPart->updateStyleForPart(barPseudoId()); + m_valuePart->updateStyleForPart(valuePseudoId()); + } } } // namespace WebCore diff --git a/WebCore/rendering/RenderMeter.h b/WebCore/rendering/RenderMeter.h index fd95adb..0e73e40 100644 --- a/WebCore/rendering/RenderMeter.h +++ b/WebCore/rendering/RenderMeter.h @@ -23,23 +23,39 @@ #if ENABLE(METER_TAG) #include "RenderBlock.h" +#include "RenderProgress.h" #include "RenderWidget.h" + namespace WebCore { class HTMLMeterElement; +class ShadowBlockElement; -class RenderMeter : public RenderBlock { +class RenderMeter : public RenderIndicator { public: RenderMeter(HTMLMeterElement*); + virtual ~RenderMeter(); private: virtual const char* renderName() const { return "RenderMeter"; } virtual bool isMeter() const { return true; } - virtual void layout(); - virtual void updateFromElement(); virtual void calcWidth(); virtual void calcHeight(); + + virtual void layoutParts(); + virtual bool shouldHaveParts() const; + + bool isHorizontal() const; + void updatePartsState(); + IntRect valuePartRect() const; + PseudoId valuePseudoId() const; + IntRect barPartRect() const; + PseudoId barPseudoId() const; + double valueRatio() const; + + RefPtr<ShadowBlockElement> m_barPart; + RefPtr<ShadowBlockElement> m_valuePart; }; inline RenderMeter* toRenderMeter(RenderObject* object) diff --git a/WebCore/rendering/RenderObject.cpp b/WebCore/rendering/RenderObject.cpp index e9e9ffc..143e2c0 100644 --- a/WebCore/rendering/RenderObject.cpp +++ b/WebCore/rendering/RenderObject.cpp @@ -1633,9 +1633,6 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS s_affectsParentBlock = false; 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 shouldBlitOnFixedBackgroundImage = false; #if ENABLE(FAST_MOBILE_SCROLLING) // On low-powered/mobile devices, preventing blitting on a scroll can cause noticeable delays @@ -1645,11 +1642,8 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS shouldBlitOnFixedBackgroundImage = true; #endif - bool newStyleSlowScroll = newStyle && (newStyle->position() == FixedPosition - || (!shouldBlitOnFixedBackgroundImage && newStyle->hasFixedBackgroundImage())); - bool oldStyleSlowScroll = m_style && (m_style->position() == FixedPosition - || (!shouldBlitOnFixedBackgroundImage && m_style->hasFixedBackgroundImage())); - + bool newStyleSlowScroll = newStyle && !shouldBlitOnFixedBackgroundImage && newStyle->hasFixedBackgroundImage(); + bool oldStyleSlowScroll = m_style && !shouldBlitOnFixedBackgroundImage && m_style->hasFixedBackgroundImage(); if (oldStyleSlowScroll != newStyleSlowScroll) { if (oldStyleSlowScroll) view()->frameView()->removeSlowRepaintObject(); diff --git a/WebCore/rendering/RenderObject.h b/WebCore/rendering/RenderObject.h index 9f1e8dd..3446a00 100644 --- a/WebCore/rendering/RenderObject.h +++ b/WebCore/rendering/RenderObject.h @@ -341,6 +341,7 @@ public: virtual bool isSVGHiddenContainer() const { return false; } virtual bool isRenderPath() const { return false; } virtual bool isSVGText() const { return false; } + virtual bool isSVGInlineText() const { return false; } virtual bool isSVGImage() const { return false; } virtual bool isSVGForeignObject() const { return false; } virtual bool isSVGResourceContainer() const { return false; } diff --git a/WebCore/rendering/RenderProgress.cpp b/WebCore/rendering/RenderProgress.cpp index e4e045d..2587e4b 100644 --- a/WebCore/rendering/RenderProgress.cpp +++ b/WebCore/rendering/RenderProgress.cpp @@ -23,11 +23,11 @@ #if ENABLE(PROGRESS_TAG) #include "RenderProgress.h" -#include "ShadowElement.h" + #include "HTMLNames.h" #include "HTMLProgressElement.h" #include "RenderTheme.h" -#include "RenderView.h" +#include "ShadowElement.h" #include <wtf/CurrentTime.h> #include <wtf/RefPtr.h> @@ -36,14 +36,13 @@ using namespace std; namespace WebCore { RenderProgress::RenderProgress(HTMLProgressElement* element) - : RenderBlock(element) + : RenderIndicator(element) , m_position(-1) , m_animationStartTime(0) , m_animationRepeatInterval(0) , m_animationDuration(0) , m_animating(false) , m_animationTimer(this, &RenderProgress::animationTimerFired) - , m_valuePart(0) { } @@ -53,53 +52,6 @@ RenderProgress::~RenderProgress() m_valuePart->detach(); } -void RenderProgress::layout() -{ - ASSERT(needsLayout()); - - LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); - - IntSize oldSize = size(); - - calcWidth(); - calcHeight(); - if (m_valuePart) { - IntRect valuePartRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), lround((width() - borderLeft() - paddingLeft() - borderRight() - paddingRight()) * position()), height() - borderTop() - paddingTop() - borderBottom() - paddingBottom()); - if (style()->direction() == RTL) - valuePartRect.setX(width() - borderRight() - paddingRight() - valuePartRect.width()); - toRenderBox(m_valuePart->renderer())->setFrameRect(valuePartRect); - - if (oldSize != size()) - m_valuePart->renderer()->setChildNeedsLayout(true, false); - - LayoutStateMaintainer statePusher(view(), this, size()); - - IntRect oldRect = toRenderBox(m_valuePart->renderer())->frameRect(); - - m_valuePart->renderer()->layoutIfNeeded(); - - toRenderBox(m_valuePart->renderer())->setFrameRect(valuePartRect); - if (m_valuePart->renderer()->checkForRepaintDuringLayout()) - m_valuePart->renderer()->repaintDuringLayoutIfMoved(oldRect); - - statePusher.pop(); - addOverflowFromChild(toRenderBox(m_valuePart->renderer())); - } - - updateAnimationState(); - - repainter.repaintAfterLayout(); - - setNeedsLayout(false); -} - -void RenderProgress::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) -{ - RenderBlock::styleDidChange(diff, oldStyle); - - updateValuePartState(); -} - void RenderProgress::updateFromElement() { HTMLProgressElement* element = progressElement(); @@ -108,45 +60,7 @@ void RenderProgress::updateFromElement() m_position = element->position(); updateAnimationState(); - - updateValuePartState(); - - repaint(); -} - -void RenderProgress::updateValuePartState() -{ - bool needLayout = !style()->hasAppearance() || m_valuePart; - if (!style()->hasAppearance() && !m_valuePart) { - m_valuePart = ShadowBlockElement::create(node()); - RefPtr<RenderStyle> styleForValuePart = createStyleForValuePart(style()); - m_valuePart->setRenderer(m_valuePart->createRenderer(renderArena(), styleForValuePart.get())); - m_valuePart->renderer()->setStyle(styleForValuePart.release()); - m_valuePart->setAttached(); - m_valuePart->setInDocument(); - addChild(m_valuePart->renderer()); - } else if (style()->hasAppearance() && m_valuePart) { - m_valuePart->detach(); - m_valuePart = 0; - } - if (needLayout) - setNeedsLayout(true); -} - -PassRefPtr<RenderStyle> RenderProgress::createStyleForValuePart(RenderStyle* parentStyle) -{ - RefPtr<RenderStyle> styleForValuePart; - RenderStyle* pseudoStyle = getCachedPseudoStyle(PROGRESS_BAR_VALUE); - if (pseudoStyle) - styleForValuePart = RenderStyle::clone(pseudoStyle); - else - styleForValuePart = RenderStyle::create(); - - if (parentStyle) - styleForValuePart->inheritFrom(parentStyle); - styleForValuePart->setDisplay(BLOCK); - styleForValuePart->setAppearance(ProgressBarValuePart); - return styleForValuePart.release(); + RenderIndicator::updateFromElement(); } double RenderProgress::animationProgress() const @@ -171,7 +85,31 @@ void RenderProgress::paint(PaintInfo& paintInfo, int tx, int ty) m_animationTimer.startOneShot(m_animationRepeatInterval); } - RenderBlock::paint(paintInfo, tx, ty); + RenderIndicator::paint(paintInfo, tx, ty); +} + +void RenderProgress::layoutParts() +{ + updatePartsState(); + if (m_valuePart) + m_valuePart->layoutAsPart(valuePartRect()); + updateAnimationState(); +} + +bool RenderProgress::shouldHaveParts() const +{ + return !style()->hasAppearance(); +} + +void RenderProgress::updatePartsState() +{ + if (shouldHaveParts() && !m_valuePart) { + m_valuePart = ShadowBlockElement::createForPart(this->node(), PROGRESS_BAR_VALUE); + addChild(m_valuePart->renderer()); + } else if (!shouldHaveParts() && m_valuePart) { + m_valuePart->detach(); + m_valuePart = 0; + } } void RenderProgress::updateAnimationState() @@ -191,6 +129,14 @@ void RenderProgress::updateAnimationState() m_animationTimer.stop(); } +IntRect RenderProgress::valuePartRect() const +{ + IntRect rect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), lround((width() - borderLeft() - paddingLeft() - borderRight() - paddingRight()) * position()), height() - borderTop() - paddingTop() - borderBottom() - paddingBottom()); + if (style()->direction() == RTL) + rect.setX(width() - borderRight() - paddingRight() - rect.width()); + return rect; +} + HTMLProgressElement* RenderProgress::progressElement() const { return static_cast<HTMLProgressElement*>(node()); diff --git a/WebCore/rendering/RenderProgress.h b/WebCore/rendering/RenderProgress.h index 4d1a88f..bd979e8 100644 --- a/WebCore/rendering/RenderProgress.h +++ b/WebCore/rendering/RenderProgress.h @@ -23,13 +23,14 @@ #if ENABLE(PROGRESS_TAG) #include "RenderBlock.h" +#include "RenderIndicator.h" namespace WebCore { class HTMLProgressElement; class ShadowBlockElement; -class RenderProgress : public RenderBlock { +class RenderProgress : public RenderIndicator { public: RenderProgress(HTMLProgressElement*); virtual ~RenderProgress(); @@ -45,17 +46,17 @@ public: private: virtual const char* renderName() const { return "RenderProgress"; } virtual bool isProgress() const { return true; } - virtual void layout(); virtual void updateFromElement(); virtual void paint(PaintInfo&, int tx, int ty); - virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); - virtual bool requiresForcedStyleRecalcPropagation() const { return true; } + virtual void layoutParts(); + virtual bool shouldHaveParts() const; + + IntRect valuePartRect() const; void animationTimerFired(Timer<RenderProgress>*); void updateAnimationState(); - void updateValuePartState(); - PassRefPtr<RenderStyle> createStyleForValuePart(RenderStyle*); + void updatePartsState(); double m_position; double m_animationStartTime; diff --git a/WebCore/rendering/RenderSVGBlock.cpp b/WebCore/rendering/RenderSVGBlock.cpp index 99725d6..2bae158 100644 --- a/WebCore/rendering/RenderSVGBlock.cpp +++ b/WebCore/rendering/RenderSVGBlock.cpp @@ -68,6 +68,11 @@ void RenderSVGBlock::updateBoxModelInfoFromStyle() setHasOverflowClip(false); } +void RenderSVGBlock::absoluteRects(Vector<IntRect>&, int, int) +{ + // This code path should never be taken for SVG, as we're assuming useTransforms=true everywhere, absoluteQuads should be used. + ASSERT_NOT_REACHED(); +} } #endif diff --git a/WebCore/rendering/RenderSVGBlock.h b/WebCore/rendering/RenderSVGBlock.h index a250c00..af819fd 100644 --- a/WebCore/rendering/RenderSVGBlock.h +++ b/WebCore/rendering/RenderSVGBlock.h @@ -29,13 +29,16 @@ namespace WebCore { class SVGElement; -class RenderSVGBlock : public RenderBlock, protected SVGRenderBase { +class RenderSVGBlock : public RenderBlock + , protected SVGRenderBase { public: RenderSVGBlock(SVGElement*); private: virtual void setStyle(PassRefPtr<RenderStyle>); virtual void updateBoxModelInfoFromStyle(); + + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty); }; } diff --git a/WebCore/rendering/RenderSVGHiddenContainer.cpp b/WebCore/rendering/RenderSVGHiddenContainer.cpp index bb0a15d..5d6bd25 100644 --- a/WebCore/rendering/RenderSVGHiddenContainer.cpp +++ b/WebCore/rendering/RenderSVGHiddenContainer.cpp @@ -52,11 +52,6 @@ IntRect RenderSVGHiddenContainer::clippedOverflowRectForRepaint(RenderBoxModelOb return IntRect(); } -void RenderSVGHiddenContainer::absoluteRects(Vector<IntRect>&, int, int) -{ - // This subtree does not take up space or paint -} - void RenderSVGHiddenContainer::absoluteQuads(Vector<FloatQuad>&) { // This subtree does not take up space or paint diff --git a/WebCore/rendering/RenderSVGHiddenContainer.h b/WebCore/rendering/RenderSVGHiddenContainer.h index fdbd2bc..c446f11 100644 --- a/WebCore/rendering/RenderSVGHiddenContainer.h +++ b/WebCore/rendering/RenderSVGHiddenContainer.h @@ -48,7 +48,6 @@ namespace WebCore { virtual void paint(PaintInfo&, int parentX, int parentY); virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); - virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); virtual FloatRect objectBoundingBox() const; diff --git a/WebCore/rendering/RenderSVGImage.cpp b/WebCore/rendering/RenderSVGImage.cpp index d1a3037..45898c7 100644 --- a/WebCore/rendering/RenderSVGImage.cpp +++ b/WebCore/rendering/RenderSVGImage.cpp @@ -143,11 +143,6 @@ bool RenderSVGImage::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int return false; } -FloatRect RenderSVGImage::objectBoundingBox() const -{ - return m_localBounds; -} - FloatRect RenderSVGImage::repaintRectInLocalCoordinates() const { // If we already have a cached repaint rect, return that @@ -195,14 +190,15 @@ void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, int, int) rects.append(contentRect); } -void RenderSVGImage::absoluteRects(Vector<IntRect>& rects, int, int) +void RenderSVGImage::absoluteRects(Vector<IntRect>&, int, int) { - rects.append(absoluteClippedOverflowRect()); + // This code path should never be taken for SVG, as we're assuming useTransforms=true everywhere, absoluteQuads should be used. + ASSERT_NOT_REACHED(); } void RenderSVGImage::absoluteQuads(Vector<FloatQuad>& quads) { - quads.append(FloatRect(absoluteClippedOverflowRect())); + quads.append(localToAbsoluteQuad(strokeBoundingBox())); } } diff --git a/WebCore/rendering/RenderSVGImage.h b/WebCore/rendering/RenderSVGImage.h index 512892c..37d2aee 100644 --- a/WebCore/rendering/RenderSVGImage.h +++ b/WebCore/rendering/RenderSVGImage.h @@ -34,7 +34,8 @@ namespace WebCore { class SVGImageElement; -class RenderSVGImage : public RenderImage, protected SVGRenderBase { +class RenderSVGImage : public RenderImage + , protected SVGRenderBase { public: RenderSVGImage(SVGImageElement*); @@ -46,7 +47,7 @@ private: virtual const AffineTransform& localToParentTransform() const { return m_localTransform; } - virtual FloatRect objectBoundingBox() const; + virtual FloatRect objectBoundingBox() const { return m_localBounds; } virtual FloatRect strokeBoundingBox() const { return m_localBounds; } virtual FloatRect repaintRectInLocalCoordinates() const; @@ -83,5 +84,3 @@ private: #endif // ENABLE(SVG) #endif // RenderSVGImage_h - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderSVGInline.cpp b/WebCore/rendering/RenderSVGInline.cpp index 85d148e..ca0fdb5 100644 --- a/WebCore/rendering/RenderSVGInline.cpp +++ b/WebCore/rendering/RenderSVGInline.cpp @@ -26,11 +26,7 @@ #if ENABLE(SVG) #include "RenderSVGInline.h" -#include "FloatQuad.h" -#include "RenderBlock.h" #include "SVGInlineFlowBox.h" -#include "SVGInlineTextBox.h" -#include "SVGRootInlineBox.h" namespace WebCore { @@ -46,25 +42,6 @@ InlineFlowBox* RenderSVGInline::createInlineFlowBox() return box; } -void RenderSVGInline::absoluteRects(Vector<IntRect>& rects, int, int) -{ - InlineFlowBox* firstBox = firstLineBox(); - - RootInlineBox* rootBox = firstBox ? firstBox->root() : 0; - RenderBox* object = rootBox ? rootBox->block() : 0; - - if (!object) - return; - - int xRef = object->x(); - int yRef = object->y(); - - for (InlineFlowBox* curr = firstBox; curr; curr = curr->nextLineBox()) { - FloatRect rect(xRef + curr->x(), yRef + curr->y(), curr->width(), curr->height()); - rects.append(enclosingIntRect(localToAbsoluteQuad(rect).boundingBox())); - } -} - FloatRect RenderSVGInline::objectBoundingBox() const { if (const RenderObject* object = findTextRootObject(this)) @@ -89,25 +66,33 @@ FloatRect RenderSVGInline::repaintRectInLocalCoordinates() const return FloatRect(); } -void RenderSVGInline::absoluteQuads(Vector<FloatQuad>& quads) +IntRect RenderSVGInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) { - InlineFlowBox* firstBox = firstLineBox(); + return SVGRenderBase::clippedOverflowRectForRepaint(this, repaintContainer); +} - RootInlineBox* rootBox = firstBox ? firstBox->root() : 0; - RenderBox* object = rootBox ? rootBox->block() : 0; +void RenderSVGInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) +{ + SVGRenderBase::computeRectForRepaint(this, repaintContainer, repaintRect, fixed); +} + +void RenderSVGInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState& transformState) const +{ + SVGRenderBase::mapLocalToContainer(this, repaintContainer, useTransforms, fixed, transformState); +} +void RenderSVGInline::absoluteQuads(Vector<FloatQuad>& quads) +{ + const RenderObject* object = findTextRootObject(this); if (!object) return; - int xRef = object->x(); - int yRef = object->y(); - - for (InlineFlowBox* curr = firstBox; curr; curr = curr->nextLineBox()) { - FloatRect rect(xRef + curr->x(), yRef + curr->y(), curr->width(), curr->height()); - quads.append(localToAbsoluteQuad(rect)); - } + FloatRect textBoundingBox = object->strokeBoundingBox(); + for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) + quads.append(localToAbsoluteQuad(FloatRect(textBoundingBox.x() + box->x(), textBoundingBox.y() + box->y(), box->width(), box->height()))); } + } #endif // ENABLE(SVG) diff --git a/WebCore/rendering/RenderSVGInline.h b/WebCore/rendering/RenderSVGInline.h index 1afbb97..2efb1aa 100644 --- a/WebCore/rendering/RenderSVGInline.h +++ b/WebCore/rendering/RenderSVGInline.h @@ -31,26 +31,28 @@ namespace WebCore { -class RenderSVGInline : public RenderInline, protected SVGRenderBase { +class RenderSVGInline : public RenderInline + , protected SVGRenderBase { public: RenderSVGInline(Node*); virtual const char* renderName() const { return "RenderSVGInline"; } virtual bool requiresLayer() const { return false; } - // These are shared between RenderSVGTSpan and RenderSVGTextPath - virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); - virtual void absoluteQuads(Vector<FloatQuad>&); - // Chapter 10.4 of the SVG Specification say that we should use the // object bounding box of the parent text element. - // We search for the root text element and take it's bounding box. + // We search for the root text element and take its bounding box. // It is also necessary to take the stroke and repaint rect of // this element, since we need it for filters. virtual FloatRect objectBoundingBox() const; virtual FloatRect strokeBoundingBox() const; virtual FloatRect repaintRectInLocalCoordinates() const; - + + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); + virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; + virtual void absoluteQuads(Vector<FloatQuad>&); + private: virtual InlineFlowBox* createInlineFlowBox(); }; diff --git a/WebCore/rendering/RenderSVGInlineText.cpp b/WebCore/rendering/RenderSVGInlineText.cpp index 48d01ae..ba99243 100644 --- a/WebCore/rendering/RenderSVGInlineText.cpp +++ b/WebCore/rendering/RenderSVGInlineText.cpp @@ -38,99 +38,21 @@ namespace WebCore { -static inline bool isChildOfHiddenContainer(RenderObject* start) -{ - while (start) { - if (start->isSVGHiddenContainer()) - return true; - - start = start->parent(); - } - - return false; -} - RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> str) : RenderText(n, str) { } - void RenderSVGInlineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { - // Skip RenderText's possible layout scheduling on style change - RenderObject::styleDidChange(diff, oldStyle); + RenderText::styleDidChange(diff, oldStyle); - // FIXME: SVG text is apparently always transformed? + // FIXME: Should optimize this. + // SVG text is always transformed. if (RefPtr<StringImpl> textToTransform = originalText()) setText(textToTransform.release(), true); } -void RenderSVGInlineText::absoluteRects(Vector<IntRect>& rects, int, int) -{ - rects.append(computeRepaintRectForRange(0, 0, textLength())); -} - -void RenderSVGInlineText::absoluteQuads(Vector<FloatQuad>& quads) -{ - quads.append(computeRepaintQuadForRange(0, 0, textLength())); -} - -IntRect RenderSVGInlineText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool /*clipToVisibleContent*/) -{ - ASSERT(!needsLayout()); - - if (selectionState() == SelectionNone) - return IntRect(); - - // Early exit if we're ie. a <text> within a <defs> section. - if (isChildOfHiddenContainer(this)) - return IntRect(); - - // Now calculate startPos and endPos for painting selection. - // We include a selection while endPos > 0 - int startPos, endPos; - if (selectionState() == SelectionInside) { - // We are fully selected. - startPos = 0; - endPos = textLength(); - } else { - selectionStartEnd(startPos, endPos); - if (selectionState() == SelectionStart) - endPos = textLength(); - else if (selectionState() == SelectionEnd) - startPos = 0; - } - - if (startPos == endPos) - return IntRect(); - - return computeRepaintRectForRange(repaintContainer, startPos, endPos); -} - -IntRect RenderSVGInlineText::computeRepaintRectForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos) -{ - FloatQuad repaintQuad = computeRepaintQuadForRange(repaintContainer, startPos, endPos); - return enclosingIntRect(repaintQuad.boundingBox()); -} - -FloatQuad RenderSVGInlineText::computeRepaintQuadForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos) -{ - RenderBlock* cb = containingBlock(); - if (!cb || !cb->container()) - return FloatQuad(); - - RenderSVGRoot* root = findSVGRootObject(parent()); - if (!root) - return FloatQuad(); - - IntRect rect; - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - rect.unite(box->selectionRect(0, 0, startPos, endPos)); - - return localToContainerQuad(FloatQuad(rect), repaintContainer); -} - InlineTextBox* RenderSVGInlineText::createTextBox() { InlineTextBox* box = new (renderArena()) SVGInlineTextBox(this); @@ -140,57 +62,9 @@ InlineTextBox* RenderSVGInlineText::createTextBox() IntRect RenderSVGInlineText::localCaretRect(InlineBox*, int, int*) { - // SVG doesn't have any editable content where a caret rect would be needed. - // FIXME: That's not sufficient. The localCaretRect function is also used for selection. return IntRect(); } -VisiblePosition RenderSVGInlineText::positionForPoint(const IntPoint& point) -{ - SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(firstTextBox()); - - if (!textBox || textLength() == 0) - return createVisiblePosition(0, DOWNSTREAM); - - SVGRootInlineBox* rootBox = textBox->svgRootInlineBox(); - RenderBlock* object = rootBox ? rootBox->block() : 0; - - if (!object) - return createVisiblePosition(0, DOWNSTREAM); - - int closestOffsetInBox = 0; - - // FIXME: This approach is wrong. The correct code would first find the - // closest SVGInlineTextBox to the point, and *then* ask only that inline box - // what the closest text offset to that point is. This code instead walks - // through all boxes in order, so when you click "near" a box, you'll actually - // end up returning the nearest offset in the last box, even if the - // nearest offset to your click is contained in another box. - for (SVGInlineTextBox* box = textBox; box; box = static_cast<SVGInlineTextBox*>(box->nextTextBox())) { - if (box->svgCharacterHitsPosition(point.x() + object->x(), point.y() + object->y(), closestOffsetInBox)) { - // If we're not at the end/start of the box, stop looking for other selected boxes. - if (box->direction() == LTR) { - if (closestOffsetInBox <= (int) box->end() + 1) - break; - } else { - if (closestOffsetInBox > (int) box->start()) - break; - } - } - } - - return createVisiblePosition(closestOffsetInBox, DOWNSTREAM); -} - -void RenderSVGInlineText::destroy() -{ - if (!documentBeingDestroyed()) { - setNeedsLayoutAndPrefWidthsRecalc(); - repaint(); - } - RenderText::destroy(); -} - } #endif // ENABLE(SVG) diff --git a/WebCore/rendering/RenderSVGInlineText.h b/WebCore/rendering/RenderSVGInlineText.h index b475067..08b4a47 100644 --- a/WebCore/rendering/RenderSVGInlineText.h +++ b/WebCore/rendering/RenderSVGInlineText.h @@ -36,28 +36,19 @@ public: private: virtual const char* renderName() const { return "RenderSVGInlineText"; } - + virtual void styleDidChange(StyleDifference, const RenderStyle*); // FIXME: We need objectBoundingBox for DRT results and filters at the moment. // This should be fixed to give back the objectBoundingBox of the text root. virtual FloatRect objectBoundingBox() const { return FloatRect(); } - virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); - virtual void absoluteQuads(Vector<FloatQuad>&); - virtual bool requiresLayer() const { return false; } - virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent = true); - virtual bool isSVGText() const { return true; } + virtual bool isSVGInlineText() const { return true; } virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); - virtual VisiblePosition positionForPoint(const IntPoint&); - - virtual void destroy(); virtual InlineTextBox* createTextBox(); - IntRect computeRepaintRectForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos); - FloatQuad computeRepaintQuadForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos); }; } diff --git a/WebCore/rendering/RenderSVGModelObject.cpp b/WebCore/rendering/RenderSVGModelObject.cpp index 837b36a..a80a963 100644 --- a/WebCore/rendering/RenderSVGModelObject.cpp +++ b/WebCore/rendering/RenderSVGModelObject.cpp @@ -72,14 +72,15 @@ IntRect RenderSVGModelObject::outlineBoundsForRepaint(RenderBoxModelObject* repa return containerRelativeQuad.enclosingBoundingBox(); } -void RenderSVGModelObject::absoluteRects(Vector<IntRect>& rects, int, int) +void RenderSVGModelObject::absoluteRects(Vector<IntRect>&, int, int) { - rects.append(absoluteClippedOverflowRect()); + // This code path should never be taken for SVG, as we're assuming useTransforms=true everywhere, absoluteQuads should be used. + ASSERT_NOT_REACHED(); } void RenderSVGModelObject::absoluteQuads(Vector<FloatQuad>& quads) { - quads.append(absoluteClippedOverflowRect()); + quads.append(localToAbsoluteQuad(strokeBoundingBox())); } void RenderSVGModelObject::destroy() diff --git a/WebCore/rendering/RenderSVGModelObject.h b/WebCore/rendering/RenderSVGModelObject.h index affa18d..42ca27f 100644 --- a/WebCore/rendering/RenderSVGModelObject.h +++ b/WebCore/rendering/RenderSVGModelObject.h @@ -55,7 +55,7 @@ public: virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer, IntPoint*) const; - virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); virtual void destroy(); diff --git a/WebCore/rendering/RenderSVGResourceFilter.cpp b/WebCore/rendering/RenderSVGResourceFilter.cpp index b0220a9..0062015 100644 --- a/WebCore/rendering/RenderSVGResourceFilter.cpp +++ b/WebCore/rendering/RenderSVGResourceFilter.cpp @@ -91,13 +91,13 @@ void RenderSVGResourceFilter::invalidateClient(RenderObject* object) markForLayoutAndResourceInvalidation(object); } -PassOwnPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives() +PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives() { SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; // Add effects to the builder - OwnPtr<SVGFilterBuilder> builder(new SVGFilterBuilder); + RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(); builder->clearEffects(); for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement()) diff --git a/WebCore/rendering/RenderSVGResourceFilter.h b/WebCore/rendering/RenderSVGResourceFilter.h index 105b256..7b5ab09 100644 --- a/WebCore/rendering/RenderSVGResourceFilter.h +++ b/WebCore/rendering/RenderSVGResourceFilter.h @@ -47,7 +47,7 @@ struct FilterData { } RefPtr<SVGFilter> filter; - OwnPtr<SVGFilterBuilder> builder; + RefPtr<SVGFilterBuilder> builder; OwnPtr<ImageBuffer> sourceGraphicBuffer; GraphicsContext* savedContext; FloatRect boundaries; @@ -72,7 +72,7 @@ public: virtual FloatRect resourceBoundingBox(RenderObject*); - PassOwnPtr<SVGFilterBuilder> buildPrimitives(); + PassRefPtr<SVGFilterBuilder> buildPrimitives(); SVGUnitTypes::SVGUnitType filterUnits() const { return toUnitType(static_cast<SVGFilterElement*>(node())->filterUnits()); } SVGUnitTypes::SVGUnitType primitiveUnits() const { return toUnitType(static_cast<SVGFilterElement*>(node())->primitiveUnits()); } diff --git a/WebCore/rendering/RenderSVGRoot.cpp b/WebCore/rendering/RenderSVGRoot.cpp index 6017513..70ff938 100644 --- a/WebCore/rendering/RenderSVGRoot.cpp +++ b/WebCore/rendering/RenderSVGRoot.cpp @@ -144,12 +144,16 @@ void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) if (paintInfo.context->paintingDisabled()) return; + bool isVisible = style()->visibility() == VISIBLE; IntPoint parentOriginInContainer(parentX, parentY); IntPoint borderBoxOriginInContainer = parentOriginInContainer + parentOriginToBorderBox(); - if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) + if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackground) && isVisible) paintBoxDecorations(paintInfo, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y()); + if (paintInfo.phase == PaintPhaseBlockBackground) + return; + // An empty viewport disables rendering. FIXME: Should we still render filters? if (m_viewportSize.isEmpty()) return; @@ -184,7 +188,7 @@ void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) childPaintInfo.context->restore(); - if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE) + if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && isVisible) paintOutline(paintInfo.context, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y(), width(), height()); } diff --git a/WebCore/rendering/RenderSVGText.cpp b/WebCore/rendering/RenderSVGText.cpp index 072b6d1..84f5329 100644 --- a/WebCore/rendering/RenderSVGText.cpp +++ b/WebCore/rendering/RenderSVGText.cpp @@ -44,6 +44,7 @@ #include "SVGTransformList.h" #include "SVGURIReference.h" #include "SimpleFontData.h" +#include "TransformState.h" namespace WebCore { @@ -63,7 +64,7 @@ void RenderSVGText::computeRectForRepaint(RenderBoxModelObject* repaintContainer SVGRenderBase::computeRectForRepaint(this, repaintContainer, repaintRect, fixed); } -void RenderSVGText::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const +void RenderSVGText::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const { SVGRenderBase::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState); } @@ -73,18 +74,28 @@ void RenderSVGText::layout() ASSERT(needsLayout()); LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); - // Best guess for a relative starting point - SVGTextElement* text = static_cast<SVGTextElement*>(node()); - int xOffset = (int)(text->x()->getFirst().value(text)); - int yOffset = (int)(text->y()->getFirst().value(text)); - setLocation(xOffset, yOffset); - if (m_needsTransformUpdate) { + SVGTextElement* text = static_cast<SVGTextElement*>(node()); m_localTransform = text->animatedLocalTransform(); m_needsTransformUpdate = false; } - RenderBlock::layout(); + // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text. + // All if branches that could cause early exit in RenderBlocks layoutBlock() method are turned into assertions. + ASSERT(!isInline()); + ASSERT(!layoutOnlyPositionedObjects()); + ASSERT(!scrollsOverflow()); + ASSERT(!hasControlClip()); + ASSERT(!hasColumns()); + ASSERT(!positionedObjects()); + ASSERT(!m_overflow); + ASSERT(!isAnonymousBlock()); + + if (!firstChild()) + setChildrenInline(true); + + ASSERT(childrenInline()); + forceLayoutInlineChildren(); repainter.repaintAfterLayout(); setNeedsLayout(false); @@ -128,40 +139,9 @@ bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, return false; } -void RenderSVGText::absoluteRects(Vector<IntRect>& rects, int, int) -{ - RenderSVGRoot* root = findSVGRootObject(parent()); - if (!root) - return; - - // Don't use objectBoundingBox here, as it's unites the selection rects. Makes it hard - // to spot errors, if there are any using WebInspector. Individually feed them into 'rects'. - for (InlineFlowBox* flow = firstLineBox(); flow; flow = flow->nextLineBox()) { - for (InlineBox* box = flow->firstChild(); box; box = box->nextOnLine()) { - FloatRect boxRect(box->x(), box->y(), box->width(), box->height()); - // FIXME: crawling up the parent chain to map each rect is very inefficient - // we should compute the absoluteTransform outside this loop first. - rects.append(enclosingIntRect(localToAbsoluteQuad(boxRect).boundingBox())); - } - } -} - void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads) { - RenderSVGRoot* root = findSVGRootObject(parent()); - if (!root) - return; - - // Don't use objectBoundingBox here, as it's unites the selection rects. Makes it hard - // to spot errors, if there are any using WebInspector. Individually feed them into 'rects'. - for (InlineFlowBox* flow = firstLineBox(); flow; flow = flow->nextLineBox()) { - for (InlineBox* box = flow->firstChild(); box; box = box->nextOnLine()) { - FloatRect boxRect(box->x(), box->y(), box->width(), box->height()); - // FIXME: crawling up the parent chain to map each quad is very inefficient - // we should compute the absoluteTransform outside this loop first. - quads.append(localToAbsoluteQuad(boxRect)); - } - } + quads.append(localToAbsoluteQuad(strokeBoundingBox())); } void RenderSVGText::paint(PaintInfo& paintInfo, int, int) @@ -169,48 +149,26 @@ void RenderSVGText::paint(PaintInfo& paintInfo, int, int) if (paintInfo.context->paintingDisabled()) return; - PaintInfo pi(paintInfo); - pi.context->save(); - applyTransformToPaintInfo(pi, localToParentTransform()); - RenderBlock::paint(pi, 0, 0); - pi.context->restore(); -} - -FloatRect RenderSVGText::objectBoundingBox() const -{ - FloatRect boundingBox; + if (paintInfo.phase != PaintPhaseForeground + && paintInfo.phase != PaintPhaseSelfOutline + && paintInfo.phase != PaintPhaseSelection) + return; - for (InlineFlowBox* flow = firstLineBox(); flow; flow = flow->nextLineBox()) { - for (InlineBox* box = flow->firstChild(); box; box = box->nextOnLine()) - boundingBox.unite(FloatRect(box->x(), box->y(), box->width(), box->height())); - } - - boundingBox.move(x(), y()); - return boundingBox; + PaintInfo blockInfo(paintInfo); + blockInfo.context->save(); + applyTransformToPaintInfo(blockInfo, localToParentTransform()); + RenderBlock::paint(blockInfo, 0, 0); + blockInfo.context->restore(); } FloatRect RenderSVGText::strokeBoundingBox() const { - FloatRect strokeRect = objectBoundingBox(); - - // SVG needs to include the strokeWidth(), not the textStrokeWidth(). - if (style()->svgStyle()->hasStroke()) { - float strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, style()->svgStyle()->strokeWidth(), 1.0f); + FloatRect strokeBoundaries = objectBoundingBox(); + if (!style()->svgStyle()->hasStroke()) + return strokeBoundaries; -#if ENABLE(SVG_FONTS) - const Font& font = style()->font(); - if (font.primaryFont()->isSVGFont()) { - float scale = font.unitsPerEm() > 0 ? font.size() / font.unitsPerEm() : 0.0f; - - if (scale != 0.0f) - strokeWidth /= scale; - } -#endif - - strokeRect.inflate(strokeWidth); - } - - return strokeRect; + strokeBoundaries.inflate(SVGRenderStyle::cssPrimitiveToLength(this, style()->svgStyle()->strokeWidth(), 1.0f)); + return strokeBoundaries; } FloatRect RenderSVGText::repaintRectInLocalCoordinates() const @@ -237,5 +195,3 @@ void RenderSVGText::updateFirstLetter() } #endif // ENABLE(SVG) - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderSVGText.h b/WebCore/rendering/RenderSVGText.h index 162c958..6e7d03a 100644 --- a/WebCore/rendering/RenderSVGText.h +++ b/WebCore/rendering/RenderSVGText.h @@ -38,14 +38,12 @@ public: RenderSVGText(SVGTextElement* node); virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } + virtual FloatRect repaintRectInLocalCoordinates() const; private: virtual const char* renderName() const { return "RenderSVGText"; } - virtual bool isSVGText() const { return true; } - 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); virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); @@ -55,7 +53,6 @@ private: virtual void destroy(); - virtual void absoluteRects(Vector<IntRect>&, int tx, int ty); virtual void absoluteQuads(Vector<FloatQuad>&); virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); @@ -63,12 +60,11 @@ private: virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const; - virtual FloatRect objectBoundingBox() const; + virtual FloatRect objectBoundingBox() const { return frameRect(); } virtual FloatRect strokeBoundingBox() const; - virtual FloatRect repaintRectInLocalCoordinates() const; + virtual const AffineTransform& localToParentTransform() const { return m_localTransform; } virtual AffineTransform localTransform() const { return m_localTransform; } - virtual RootInlineBox* createRootInlineBox(); virtual RenderBlock* firstLineBlock() const; @@ -82,5 +78,3 @@ private: #endif // ENABLE(SVG) #endif - -// vim:ts=4:noet diff --git a/WebCore/rendering/RenderSelectionInfo.h b/WebCore/rendering/RenderSelectionInfo.h index c06a9ae..a09fc1a 100644 --- a/WebCore/rendering/RenderSelectionInfo.h +++ b/WebCore/rendering/RenderSelectionInfo.h @@ -22,8 +22,8 @@ * */ -#ifndef SelectionInfo_h -#define SelectionInfo_h +#ifndef RenderSelectionInfo_h +#define RenderSelectionInfo_h #include "IntRect.h" #include "RenderBox.h" @@ -101,4 +101,4 @@ private: } // namespace WebCore -#endif // SelectionInfo_h +#endif // RenderSelectionInfo_h diff --git a/WebCore/rendering/RenderSlider.cpp b/WebCore/rendering/RenderSlider.cpp index 60b2369..591b1ac 100644 --- a/WebCore/rendering/RenderSlider.cpp +++ b/WebCore/rendering/RenderSlider.cpp @@ -29,7 +29,7 @@ #include "Frame.h" #include "HTMLInputElement.h" #include "HTMLNames.h" -#include "LegacyHTMLTreeConstructor.h" +#include "LegacyHTMLTreeBuilder.h" #include "MediaControlElements.h" #include "MouseEvent.h" #include "RenderLayer.h" @@ -56,7 +56,6 @@ static double sliderPosition(HTMLInputElement* element) return range.proportionFromValue(range.valueFromElement(element)); } -// FIXME: Could share code with the SliderDivElement class in RenderProgress. class SliderThumbElement : public ShadowBlockElement { public: static PassRefPtr<SliderThumbElement> create(Node* shadowParent); @@ -68,7 +67,7 @@ public: private: SliderThumbElement(Node* shadowParent); - + FloatPoint m_offsetToThumb; bool m_inDragMode; }; @@ -81,7 +80,7 @@ inline SliderThumbElement::SliderThumbElement(Node* shadowParent) inline PassRefPtr<SliderThumbElement> SliderThumbElement::create(Node* shadowParent) { - return new SliderThumbElement(shadowParent); + return adoptRef(new SliderThumbElement(shadowParent)); } void SliderThumbElement::defaultEventHandler(Event* event) @@ -111,7 +110,7 @@ void SliderThumbElement::defaultEventHandler(Event* event) } m_inDragMode = true; - document()->frame()->eventHandler()->setCapturingMouseEventsNode(shadowParentNode()); + document()->frame()->eventHandler()->setCapturingMouseEventsNode(shadowParent()); event->setDefaultHandled(); return; } diff --git a/WebCore/rendering/RenderTable.h b/WebCore/rendering/RenderTable.h index 3d0714f..dace8ba 100644 --- a/WebCore/rendering/RenderTable.h +++ b/WebCore/rendering/RenderTable.h @@ -75,7 +75,7 @@ public: { } - unsigned short span; + unsigned span; unsigned width; // the calculated position of the column }; diff --git a/WebCore/rendering/RenderTableSection.cpp b/WebCore/rendering/RenderTableSection.cpp index 5f6c1eb..fcb6c59 100644 --- a/WebCore/rendering/RenderTableSection.cpp +++ b/WebCore/rendering/RenderTableSection.cpp @@ -238,7 +238,7 @@ void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row) table()->appendColumn(cSpan); currentSpan = cSpan; } else { - if (cSpan < columns[m_cCol].span) + if (cSpan < static_cast<int>(columns[m_cCol].span)) table()->splitColumn(m_cCol, cSpan); currentSpan = columns[m_cCol].span; } diff --git a/WebCore/rendering/RenderText.cpp b/WebCore/rendering/RenderText.cpp index cfd6026..c70ac58 100644 --- a/WebCore/rendering/RenderText.cpp +++ b/WebCore/rendering/RenderText.cpp @@ -314,7 +314,7 @@ void RenderText::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, u void RenderText::absoluteQuads(Vector<FloatQuad>& quads) { for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - quads.append(localToAbsoluteQuad(FloatRect(box->x(), box->y(), box->width(), box->height()))); + quads.append(localToAbsoluteQuad(FloatRect(box->calculateBoundaries()))); } void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight) @@ -332,7 +332,7 @@ void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { // Note: box->end() returns the index of the last character, not the index past it if (start <= box->start() && box->end() < end) { - IntRect r = IntRect(box->x(), box->y(), box->width(), box->height()); + IntRect r(box->calculateBoundaries()); if (useSelectionHeight) { IntRect selectionRect = box->selectionRect(0, 0, start, end); r.setHeight(selectionRect.height()); @@ -1051,7 +1051,7 @@ void RenderText::setTextInternal(PassRefPtr<StringImpl> text) ASSERT(m_text); #if ENABLE(SVG) - if (isSVGText()) { + if (isSVGInlineText()) { if (style() && style()->whiteSpace() == PRE) { // Spec: When xml:space="preserve", the SVG user agent will do the following using a // copy of the original character data content. It will convert all newline and tab diff --git a/WebCore/rendering/RenderTextControlSingleLine.cpp b/WebCore/rendering/RenderTextControlSingleLine.cpp index d3892e9..ab56036 100644 --- a/WebCore/rendering/RenderTextControlSingleLine.cpp +++ b/WebCore/rendering/RenderTextControlSingleLine.cpp @@ -1,6 +1,7 @@ /** * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * 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 @@ -299,9 +300,13 @@ bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, Hit // If we found a spin button, we're done. if (m_outerSpinButton && result.innerNode() == m_outerSpinButton) return true; - // If we're not a search field, or we already found the results or cancel buttons, we're done. + // If we're not a search field, or we already found the speech, results or cancel buttons, we're done. if (!m_innerBlock || result.innerNode() == m_resultsButton || result.innerNode() == m_cancelButton) return true; +#if ENABLE(INPUT_SPEECH) + if (m_innerBlock && m_speechButton && result.innerNode() == m_speechButton) + return true; +#endif Node* innerNode = 0; RenderBox* innerBlockRenderer = m_innerBlock->renderBox(); @@ -314,6 +319,19 @@ bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, Hit if (m_resultsButton && m_resultsButton->renderer() && xPos < textLeft) innerNode = m_resultsButton.get(); +#if ENABLE(INPUT_SPEECH) + if (!innerNode && m_speechButton && m_speechButton->renderer()) { + int buttonLeft = textLeft + innerTextRenderer->width(); + if (m_cancelButton) { + RenderBox* cancelRenderer = m_cancelButton->renderBox(); + cancelRenderer->calcWidth(); + buttonLeft += cancelRenderer->width() + cancelRenderer->marginLeft() + cancelRenderer->marginRight(); + } + if (xPos > buttonLeft) + innerNode = m_speechButton.get(); + } +#endif + if (!innerNode) { int textRight = textLeft + innerTextRenderer->width(); if (m_cancelButton && m_cancelButton->renderer() && xPos > textRight) @@ -350,8 +368,20 @@ void RenderTextControlSingleLine::forwardEvent(Event* event) FloatPoint localPoint = innerTextRenderer->absoluteToLocal(static_cast<MouseEvent*>(event)->absoluteLocation(), false, true); int textRight = innerTextRenderer->borderBoxRect().right(); +#if ENABLE(INPUT_SPEECH) + int cancelRight = textRight; + if (m_cancelButton && m_cancelButton->renderBox()) { + RenderBox* cancelRenderer = m_cancelButton->renderBox(); + cancelRight += cancelRenderer->width() + cancelRenderer->marginLeft() + cancelRenderer->marginRight(); + } +#endif + if (m_resultsButton && localPoint.x() < innerTextRenderer->borderBoxRect().x()) m_resultsButton->defaultEventHandler(event); +#if ENABLE(INPUT_SPEECH) + else if (m_speechButton && localPoint.x() > cancelRight) + m_speechButton->defaultEventHandler(event); +#endif else if (m_cancelButton && localPoint.x() > textRight) m_cancelButton->defaultEventHandler(event); else if (m_outerSpinButton && localPoint.x() > textRight) @@ -381,6 +411,11 @@ void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const Ren if (RenderObject* spinRenderer = m_outerSpinButton ? m_outerSpinButton->renderer() : 0) spinRenderer->setStyle(createOuterSpinButtonStyle()); +#if ENABLE(INPUT_SPEECH) + if (RenderObject* speechRenderer = m_speechButton ? m_speechButton->renderer() : 0) + speechRenderer->setStyle(createSpeechButtonStyle(style())); +#endif + setHasOverflowClip(false); } @@ -408,9 +443,19 @@ void RenderTextControlSingleLine::capsLockStateMayHaveChanged() } } +bool RenderTextControlSingleLine::hasControlClip() const +{ + bool clip = m_cancelButton; +#if ENABLE(INPUT_SPEECH) + if (m_speechButton) + clip = true; +#endif + return clip; +} + IntRect RenderTextControlSingleLine::controlClipRect(int tx, int ty) const { - // This should only get called for search inputs. + // This should only get called for search & speech inputs. ASSERT(hasControlClip()); IntRect clipRect = IntRect(m_innerBlock->renderBox()->frameRect()); @@ -432,6 +477,13 @@ int RenderTextControlSingleLine::textBlockWidth() const width -= cancelRenderer->width() + cancelRenderer->marginLeft() + cancelRenderer->marginRight(); } +#if ENABLE(INPUT_SPEECH) + if (RenderBox* speechRenderer = m_speechButton ? m_speechButton->renderBox() : 0) { + speechRenderer->calcWidth(); + width -= speechRenderer->width() + speechRenderer->marginLeft() + speechRenderer->marginRight(); + } +#endif + return width - decorationWidthRight(); } @@ -490,6 +542,12 @@ int RenderTextControlSingleLine::preferredContentWidth(float charWidth) const result += cancelRenderer->borderLeft() + cancelRenderer->borderRight() + cancelRenderer->paddingLeft() + cancelRenderer->paddingRight(); +#if ENABLE(INPUT_SPEECH) + if (RenderBox* speechRenderer = m_speechButton ? m_speechButton->renderBox() : 0) { + result += speechRenderer->borderLeft() + speechRenderer->borderRight() + + speechRenderer->paddingLeft() + speechRenderer->paddingRight(); + } +#endif return result; } @@ -525,12 +583,28 @@ void RenderTextControlSingleLine::adjustControlHeightBasedOnLineHeight(int lineH lineHeight = max(lineHeight, cancelRenderer->height()); } +#if ENABLE(INPUT_SPEECH) + if (RenderBox* speechRenderer = m_speechButton ? m_speechButton->renderBox() : 0) { + toRenderBlock(speechRenderer)->calcHeight(); + setHeight(max(height(), + speechRenderer->borderTop() + speechRenderer->borderBottom() + + speechRenderer->paddingTop() + speechRenderer->paddingBottom() + + speechRenderer->marginTop() + speechRenderer->marginBottom())); + lineHeight = max(lineHeight, speechRenderer->height()); + } +#endif + setHeight(height() + lineHeight); } void RenderTextControlSingleLine::createSubtreeIfNeeded() { - if (!inputElement()->isSearchField()) { + bool createSubtree = inputElement()->isSearchField(); +#if ENABLE(INPUT_SPEECH) + if (inputElement()->isSpeechEnabled()) + createSubtree = true; +#endif + if (!createSubtree) { RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get()); if (inputElement()->hasSpinButton() && !m_outerSpinButton) { m_outerSpinButton = SpinButtonElement::create(node()); @@ -545,20 +619,31 @@ void RenderTextControlSingleLine::createSubtreeIfNeeded() m_innerBlock->attachInnerElement(node(), createInnerBlockStyle(style()), renderArena()); } - if (!m_resultsButton) { - // Create the search results button element - m_resultsButton = SearchFieldResultsButtonElement::create(document()); - m_resultsButton->attachInnerElement(m_innerBlock.get(), createResultsButtonStyle(m_innerBlock->renderer()->style()), renderArena()); + if (inputElement()->isSearchField()) { + if (!m_resultsButton) { + // Create the search results button element. + m_resultsButton = SearchFieldResultsButtonElement::create(document()); + m_resultsButton->attachInnerElement(m_innerBlock.get(), createResultsButtonStyle(m_innerBlock->renderer()->style()), renderArena()); + } } - // Create innerText element before adding the cancel button + // Create innerText element before adding the other buttons. RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get()); - if (!m_cancelButton) { - // Create the cancel button element - m_cancelButton = SearchFieldCancelButtonElement::create(document()); - m_cancelButton->attachInnerElement(m_innerBlock.get(), createCancelButtonStyle(m_innerBlock->renderer()->style()), renderArena()); + if (inputElement()->isSearchField()) { + if (!m_cancelButton) { + // Create the cancel button element. + m_cancelButton = SearchFieldCancelButtonElement::create(document()); + m_cancelButton->attachInnerElement(m_innerBlock.get(), createCancelButtonStyle(m_innerBlock->renderer()->style()), renderArena()); + } } +#if ENABLE(INPUT_SPEECH) + if (inputElement()->isSpeechEnabled() && !m_speechButton) { + // Create the speech button element. + m_speechButton = InputFieldSpeechButtonElement::create(document()); + m_speechButton->attachInnerElement(m_innerBlock.get(), createSpeechButtonStyle(m_innerBlock->renderer()->style()), renderArena()); + } +#endif } void RenderTextControlSingleLine::updateFromElement() @@ -696,6 +781,19 @@ PassRefPtr<RenderStyle> RenderTextControlSingleLine::createOuterSpinButtonStyle( return buttonStyle.release(); } +#if ENABLE(INPUT_SPEECH) +PassRefPtr<RenderStyle> RenderTextControlSingleLine::createSpeechButtonStyle(const RenderStyle* startStyle) const +{ + ASSERT(node()->isHTMLElement()); + RefPtr<RenderStyle> buttonStyle = getCachedPseudoStyle(INPUT_SPEECH_BUTTON); + if (!buttonStyle) + buttonStyle = RenderStyle::create(); + if (startStyle) + buttonStyle->inheritFrom(startStyle); + return buttonStyle.release(); +} +#endif + void RenderTextControlSingleLine::updateCancelButtonVisibility() const { if (!m_cancelButton->renderer()) @@ -834,6 +932,10 @@ int RenderTextControlSingleLine::clientPaddingRight() const if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) padding += cancelRenderer->width(); +#if ENABLE(INPUT_SPEECH) + if (RenderBox* speechRenderer = m_speechButton ? m_speechButton->renderBox() : 0) + padding += speechRenderer->width(); +#endif return padding; } diff --git a/WebCore/rendering/RenderTextControlSingleLine.h b/WebCore/rendering/RenderTextControlSingleLine.h index dff9e5f..ab9f711 100644 --- a/WebCore/rendering/RenderTextControlSingleLine.h +++ b/WebCore/rendering/RenderTextControlSingleLine.h @@ -29,6 +29,7 @@ namespace WebCore { class InputElement; +class InputFieldSpeechButtonElement; class SearchFieldCancelButtonElement; class SearchFieldResultsButtonElement; class SearchPopupMenu; @@ -59,7 +60,7 @@ public: private: int preferredDecorationWidthRight() const; - virtual bool hasControlClip() const { return m_cancelButton; } + virtual bool hasControlClip() const; virtual IntRect controlClipRect(int tx, int ty) const; virtual bool isTextField() const { return true; } @@ -98,6 +99,9 @@ private: PassRefPtr<RenderStyle> createResultsButtonStyle(const RenderStyle* startStyle) const; PassRefPtr<RenderStyle> createCancelButtonStyle(const RenderStyle* startStyle) const; PassRefPtr<RenderStyle> createOuterSpinButtonStyle() const; +#if ENABLE(INPUT_SPEECH) + PassRefPtr<RenderStyle> createSpeechButtonStyle(const RenderStyle* startStyle) const; +#endif void updateCancelButtonVisibility() const; EVisibility visibilityForCancelButton() const; @@ -142,6 +146,9 @@ private: RefPtr<SearchFieldResultsButtonElement> m_resultsButton; RefPtr<SearchFieldCancelButtonElement> m_cancelButton; RefPtr<TextControlInnerElement> m_outerSpinButton; +#if ENABLE(INPUT_SPEECH) + RefPtr<InputFieldSpeechButtonElement> m_speechButton; +#endif Timer<RenderTextControlSingleLine> m_searchEventTimer; RefPtr<SearchPopupMenu> m_searchPopup; diff --git a/WebCore/rendering/RenderTheme.cpp b/WebCore/rendering/RenderTheme.cpp index 2b5efc9..407f273 100644 --- a/WebCore/rendering/RenderTheme.cpp +++ b/WebCore/rendering/RenderTheme.cpp @@ -232,6 +232,10 @@ void RenderTheme::adjustStyle(CSSStyleSelector* selector, RenderStyle* style, El case RatingLevelIndicatorPart: return adjustMeterStyle(selector, style, e); #endif +#if ENABLE(INPUT_SPEECH) + case InputSpeechButtonPart: + // FIXME: Adjust the speech button's style and sizes. +#endif default: break; } @@ -362,6 +366,10 @@ bool RenderTheme::paint(RenderObject* o, const RenderObject::PaintInfo& paintInf return paintSearchFieldResultsDecoration(o, paintInfo, r); case SearchFieldResultsButtonPart: return paintSearchFieldResultsButton(o, paintInfo, r); +#if ENABLE(INPUT_SPEECH) + case InputSpeechButtonPart: + // FIXME: Add painting code to draw the speech button. +#endif default: break; } @@ -410,6 +418,9 @@ bool RenderTheme::paintBorderOnly(RenderObject* o, const RenderObject::PaintInfo case SearchFieldDecorationPart: case SearchFieldResultsDecorationPart: case SearchFieldResultsButtonPart: +#if ENABLE(INPUT_SPEECH) + case InputSpeechButtonPart: +#endif default: break; } @@ -456,6 +467,9 @@ bool RenderTheme::paintDecorations(RenderObject* o, const RenderObject::PaintInf case SearchFieldDecorationPart: case SearchFieldResultsDecorationPart: case SearchFieldResultsButtonPart: +#if ENABLE(INPUT_SPEECH) + case InputSpeechButtonPart: +#endif default: break; } @@ -655,6 +669,11 @@ bool RenderTheme::isControlStyled(const RenderStyle* style, const BorderData& bo case ListboxPart: case MenulistPart: case ProgressBarPart: + case MeterPart: + case RelevancyLevelIndicatorPart: + case ContinuousCapacityLevelIndicatorPart: + case DiscreteCapacityLevelIndicatorPart: + case RatingLevelIndicatorPart: // FIXME: Uncomment this when making search fields style-able. // case SearchFieldPart: case TextFieldPart: @@ -918,56 +937,16 @@ IntSize RenderTheme::meterSizeForBounds(const RenderMeter*, const IntRect& bound return bounds.size(); } -bool RenderTheme::paintMeter(RenderObject* renderObject, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +bool RenderTheme::supportsMeter(ControlPart, bool) const { - if (!renderObject->isMeter()) - return true; - - // Some platforms do not have a native gauge widget, so we draw here a default implementation. - RenderMeter* renderMeter = toRenderMeter(renderObject); - RenderStyle* style = renderObject->style(); - int left = style->borderLeft().width() + style->paddingLeft().value(); - int top = style->borderTop().width() + style->paddingTop().value(); - int right = style->borderRight().width() + style->paddingRight().value(); - int bottom = style->borderBottom().width() + style->paddingBottom().value(); - FloatRect innerRect(rect.x() + left, rect.y() + top, rect.width() - left - right, rect.height() - top - bottom); - - HTMLMeterElement* element = static_cast<HTMLMeterElement*>(renderMeter->node()); - double min = element->min(); - double max = element->max(); - double value = element->value(); - - if (min >= max) { - paintInfo.context->fillRect(innerRect, Color::black, style->colorSpace()); - return false; - } - - // Paint the background first - paintInfo.context->fillRect(innerRect, Color::lightGray, style->colorSpace()); - - FloatRect valueRect; - - if (rect.width() < rect.height()) { - // Vertical gauge - double scale = innerRect.height() / (max - min); - valueRect.setLocation(FloatPoint(innerRect.x(), innerRect.y() + narrowPrecisionToFloat((max - value) * scale))); - valueRect.setSize(FloatSize(innerRect.width(), narrowPrecisionToFloat((value - min) * scale))); - } else if (renderMeter->style()->direction() == RTL) { - // right to left horizontal gauge - double scale = innerRect.width() / (max - min); - valueRect.setLocation(FloatPoint(innerRect.x() + narrowPrecisionToFloat((max - value) * scale), innerRect.y())); - valueRect.setSize(FloatSize(narrowPrecisionToFloat((value - min) * scale), innerRect.height())); - } else { - // left to right horizontal gauge - double scale = innerRect.width() / (max - min); - valueRect.setLocation(innerRect.location()); - valueRect.setSize(FloatSize(narrowPrecisionToFloat((value - min) * scale), innerRect.height())); - } - if (!valueRect.isEmpty()) - paintInfo.context->fillRect(valueRect, Color::black, style->colorSpace()); - return false; } + +bool RenderTheme::paintMeter(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) +{ + return true; +} + #endif #if ENABLE(PROGRESS_TAG) diff --git a/WebCore/rendering/RenderTheme.h b/WebCore/rendering/RenderTheme.h index 2d196c7..72a33dc 100644 --- a/WebCore/rendering/RenderTheme.h +++ b/WebCore/rendering/RenderTheme.h @@ -195,6 +195,7 @@ public: #if ENABLE(METER_TAG) virtual IntSize meterSizeForBounds(const RenderMeter*, const IntRect&) const; + virtual bool supportsMeter(ControlPart, bool isHorizontal) const; #endif protected: diff --git a/WebCore/rendering/RenderThemeChromiumMac.mm b/WebCore/rendering/RenderThemeChromiumMac.mm index 47a872d..e572b69 100644 --- a/WebCore/rendering/RenderThemeChromiumMac.mm +++ b/WebCore/rendering/RenderThemeChromiumMac.mm @@ -91,7 +91,7 @@ void RenderThemeChromiumMac::updateActiveState(NSCell* cell, const RenderObject* { NSControlTint oldTint = [cell controlTint]; NSControlTint tint = isActive(o) ? [NSColor currentControlTint] : - NSClearControlTint; + static_cast<NSControlTint>(NSClearControlTint); if (tint != oldTint) [cell setControlTint:tint]; diff --git a/WebCore/rendering/RenderThemeMac.h b/WebCore/rendering/RenderThemeMac.h index 7cb4e3b..34dbed3 100644 --- a/WebCore/rendering/RenderThemeMac.h +++ b/WebCore/rendering/RenderThemeMac.h @@ -83,6 +83,7 @@ public: #if ENABLE(METER_TAG) virtual IntSize meterSizeForBounds(const RenderMeter*, const IntRect&) const; virtual bool paintMeter(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool supportsMeter(ControlPart, bool isHorizontal) const; #endif #if ENABLE(PROGRESS_TAG) diff --git a/WebCore/rendering/RenderThemeMac.mm b/WebCore/rendering/RenderThemeMac.mm index d0289de..9c79386 100644 --- a/WebCore/rendering/RenderThemeMac.mm +++ b/WebCore/rendering/RenderThemeMac.mm @@ -839,6 +839,20 @@ bool RenderThemeMac::paintMeter(RenderObject* renderObject, const RenderObject:: return false; } +bool RenderThemeMac::supportsMeter(ControlPart part, bool isHorizontal) const +{ + switch (part) { + case RelevancyLevelIndicatorPart: + case DiscreteCapacityLevelIndicatorPart: + case RatingLevelIndicatorPart: + case MeterPart: + case ContinuousCapacityLevelIndicatorPart: + return isHorizontal; + default: + return false; + } +} + NSLevelIndicatorStyle RenderThemeMac::levelIndicatorStyleFor(ControlPart part) const { switch (part) { diff --git a/WebCore/rendering/RenderTreeAsText.cpp b/WebCore/rendering/RenderTreeAsText.cpp index b05d97a..26f40ab 100644 --- a/WebCore/rendering/RenderTreeAsText.cpp +++ b/WebCore/rendering/RenderTreeAsText.cpp @@ -416,10 +416,11 @@ void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavi return; } if (o.isSVGText()) { - if (!o.isText()) - writeSVGText(ts, *toRenderBlock(&o), indent); - else - writeSVGInlineText(ts, *toRenderText(&o), indent); + writeSVGText(ts, *toRenderBlock(&o), indent); + return; + } + if (o.isSVGInlineText()) { + writeSVGInlineText(ts, *toRenderText(&o), indent); return; } if (o.isSVGImage()) { diff --git a/WebCore/rendering/RenderView.cpp b/WebCore/rendering/RenderView.cpp index 8079760..e77f6c7 100644 --- a/WebCore/rendering/RenderView.cpp +++ b/WebCore/rendering/RenderView.cpp @@ -193,9 +193,17 @@ void RenderView::paint(PaintInfo& paintInfo, int tx, int ty) paintObject(paintInfo, tx, ty); } +static inline bool isComposited(RenderObject* object) +{ + return object->hasLayer() && toRenderBoxModelObject(object)->layer()->isComposited(); +} + static inline bool rendererObscuresBackground(RenderObject* object) { - return object && object->style()->visibility() == VISIBLE && object->style()->opacity() == 1 && !object->style()->hasTransform(); + return object && object->style()->visibility() == VISIBLE + && object->style()->opacity() == 1 + && !object->style()->hasTransform() + && !isComposited(object); } void RenderView::paintBoxDecorations(PaintInfo& paintInfo, int, int) @@ -223,8 +231,19 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, int, int) #endif } + if (document()->ownerElement() || !view()) + return; + + bool rootFillsViewport = false; + Node* documentElement = document()->documentElement(); + if (RenderObject* rootRenderer = documentElement ? documentElement->renderer() : 0) { + // The document element's renderer is currently forced to be a block, but may not always be. + RenderBox* rootBox = rootRenderer->isBox() ? toRenderBox(rootRenderer) : 0; + rootFillsViewport = rootBox && !rootBox->x() && !rootBox->y() && rootBox->width() >= width() && rootBox->height() >= height(); + } + // If painting will entirely fill the view, no need to fill the background. - if (elt || rendererObscuresBackground(firstChild()) || !view()) + if (rootFillsViewport && rendererObscuresBackground(firstChild())) return; // This code typically only executes if the root element's visibility has been set to hidden, diff --git a/WebCore/rendering/RootInlineBox.cpp b/WebCore/rendering/RootInlineBox.cpp index 24e49c6..3a8a4f5 100644 --- a/WebCore/rendering/RootInlineBox.cpp +++ b/WebCore/rendering/RootInlineBox.cpp @@ -47,11 +47,11 @@ void RootInlineBox::destroy(RenderArena* arena) void RootInlineBox::detachEllipsisBox(RenderArena* arena) { - if (m_hasEllipsisBox) { + if (hasEllipsisBox()) { EllipsisBox* box = gEllipsisBoxMap->take(this); box->setParent(0); box->destroy(arena); - m_hasEllipsisBox = false; + setHasEllipsisBox(false); } } @@ -62,7 +62,7 @@ RenderLineBoxList* RootInlineBox::rendererLineBoxes() const void RootInlineBox::clearTruncation() { - if (m_hasEllipsisBox) { + if (hasEllipsisBox()) { detachEllipsisBox(renderer()->renderArena()); InlineFlowBox::clearTruncation(); } @@ -92,7 +92,7 @@ void RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, in if (!gEllipsisBoxMap) gEllipsisBoxMap = new EllipsisBoxMap(); gEllipsisBoxMap->add(this, ellipsisBox); - m_hasEllipsisBox = true; + setHasEllipsisBox(true); // FIXME: Do we need an RTL version of this? if (ltr && (x() + width() + ellipsisWidth) <= blockRightEdge) { @@ -117,8 +117,8 @@ int RootInlineBox::placeEllipsisBox(bool ltr, int blockLeftEdge, int blockRightE void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo& paintInfo, int tx, int ty) const { - if (m_hasEllipsisBox && renderer()->shouldPaintWithinRoot(paintInfo) && renderer()->style()->visibility() == VISIBLE && - paintInfo.phase == PaintPhaseForeground) + if (hasEllipsisBox() && renderer()->shouldPaintWithinRoot(paintInfo) && renderer()->style()->visibility() == VISIBLE + && paintInfo.phase == PaintPhaseForeground) ellipsisBox()->paint(paintInfo, tx, ty); } @@ -174,7 +174,7 @@ void RootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) { - if (m_hasEllipsisBox && visibleToHitTesting()) { + if (hasEllipsisBox() && visibleToHitTesting()) { if (ellipsisBox()->nodeAtPoint(request, result, x, y, tx, ty)) { renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; @@ -204,6 +204,12 @@ void RootInlineBox::childRemoved(InlineBox* box) int RootInlineBox::verticallyAlignBoxes(int heightOfBlock, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) { +#if ENABLE(SVG) + // SVG will handle vertical alignment on its own. + if (isSVGRootInlineBox()) + return 0; +#endif + int maxPositionTop = 0; int maxPositionBottom = 0; int maxAscent = 0; @@ -410,8 +416,8 @@ void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, const EllipsisBox* RootInlineBox::ellipsisBox() const { - if (!m_hasEllipsisBox) - return false; + if (!hasEllipsisBox()) + return 0; return gEllipsisBoxMap->get(this); } diff --git a/WebCore/rendering/RootInlineBox.h b/WebCore/rendering/RootInlineBox.h index cfa550b..fa2510a 100644 --- a/WebCore/rendering/RootInlineBox.h +++ b/WebCore/rendering/RootInlineBox.h @@ -61,15 +61,11 @@ public: int selectionBottom() const { return lineBottom(); } int selectionHeight() const { return max(0, selectionBottom() - selectionTop()); } - virtual int verticallyAlignBoxes(int heightOfBlock, GlyphOverflowAndFallbackFontsMap&); + int verticallyAlignBoxes(int heightOfBlock, GlyphOverflowAndFallbackFontsMap&); void setLineTopBottomPositions(int top, int bottom); virtual RenderLineBoxList* rendererLineBoxes() const; -#if ENABLE(SVG) - virtual void computePerCharacterLayoutInformation() { } -#endif - RenderObject* lineBreakObj() const { return m_lineBreakObj; } BidiStatus lineBreakBidiStatus() const; void setLineBreakInfo(RenderObject*, unsigned breakPos, const BidiStatus&); @@ -132,7 +128,10 @@ public: virtual void attachLineBoxToRenderObject(); virtual void removeLineBoxFromRenderObject(); -protected: +private: + bool hasEllipsisBox() const { return m_hasEllipsisBoxOrHyphen; } + void setHasEllipsisBox(bool hasEllipsisBox) { m_hasEllipsisBoxOrHyphen = hasEllipsisBox; } + // 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; diff --git a/WebCore/rendering/SVGCharacterData.h b/WebCore/rendering/SVGCharacterData.h index 68682c9..5143042 100644 --- a/WebCore/rendering/SVGCharacterData.h +++ b/WebCore/rendering/SVGCharacterData.h @@ -90,12 +90,6 @@ struct SVGChar { AffineTransform characterTransform() const; }; -struct SVGTextDecorationInfo { - // ETextDecoration is meant to be used here - HashMap<int, RenderObject*> fillServerMap; - HashMap<int, RenderObject*> strokeServerMap; -}; - } // namespace WebCore #endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGCharacterLayoutInfo.cpp b/WebCore/rendering/SVGCharacterLayoutInfo.cpp index 04f0067..18315df 100644 --- a/WebCore/rendering/SVGCharacterLayoutInfo.cpp +++ b/WebCore/rendering/SVGCharacterLayoutInfo.cpp @@ -67,7 +67,7 @@ static float calculateBaselineShift(RenderObject* item) return baselineShift; } -SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector<SVGChar>& chars) +SVGCharacterLayoutInfo::SVGCharacterLayoutInfo() : curx(0.0f) , cury(0.0f) , angle(0.0f) @@ -78,7 +78,6 @@ SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector<SVGChar>& chars) , pathExtraAdvance(0.0f) , pathTextLength(0.0f) , pathChunkLength(0.0f) - , svgChars(chars) , nextDrawnSeperated(false) , xStackChanged(false) , yStackChanged(false) diff --git a/WebCore/rendering/SVGCharacterLayoutInfo.h b/WebCore/rendering/SVGCharacterLayoutInfo.h index 16fd740..7549283 100644 --- a/WebCore/rendering/SVGCharacterLayoutInfo.h +++ b/WebCore/rendering/SVGCharacterLayoutInfo.h @@ -67,7 +67,7 @@ class PositionedFloatVector : public PositionedVector<float> { }; struct SVGChar; struct SVGCharacterLayoutInfo { - SVGCharacterLayoutInfo(Vector<SVGChar>&); + SVGCharacterLayoutInfo(); enum StackType { XStack, YStack, DxStack, DyStack, AngleStack, BaselineShiftStack }; @@ -120,7 +120,7 @@ struct SVGCharacterLayoutInfo { float pathChunkLength; // Result vector - Vector<SVGChar>& svgChars; + Vector<SVGChar> svgChars; bool nextDrawnSeperated : 1; private: diff --git a/WebCore/rendering/SVGInlineFlowBox.cpp b/WebCore/rendering/SVGInlineFlowBox.cpp index bbd61b3..1cd938a 100644 --- a/WebCore/rendering/SVGInlineFlowBox.cpp +++ b/WebCore/rendering/SVGInlineFlowBox.cpp @@ -1,9 +1,8 @@ /* - * This file is part of the WebKit project. - * * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> * (C) 2006 Apple Computer Inc. * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * 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 @@ -23,24 +22,43 @@ */ #include "config.h" +#include "SVGInlineFlowBox.h" #if ENABLE(SVG) -#include "SVGInlineFlowBox.h" -#include "SVGNames.h" +#include "GraphicsContext.h" +#include "SVGRenderSupport.h" namespace WebCore { -using namespace SVGNames; - -void SVGInlineFlowBox::paint(RenderObject::PaintInfo&, int, int) +void SVGInlineFlowBox::paint(RenderObject::PaintInfo& paintInfo, int, int) { - ASSERT_NOT_REACHED(); + ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); + ASSERT(!paintInfo.context->paintingDisabled()); + + RenderObject* boxRenderer = renderer(); + ASSERT(boxRenderer); + + RenderObject::PaintInfo childPaintInfo(paintInfo); + childPaintInfo.context->save(); + + RenderSVGResourceFilter* filter = 0; + FloatRect repaintRect = boxRenderer->repaintRectInLocalCoordinates(); + + if (SVGRenderBase::prepareToRenderSVGContent(boxRenderer, childPaintInfo, repaintRect, filter)) { + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->paint(childPaintInfo, 0, 0); + } + + SVGRenderBase::finishRenderSVGContent(boxRenderer, childPaintInfo, filter, paintInfo.context); + childPaintInfo.context->restore(); } -int SVGInlineFlowBox::placeBoxesHorizontally(int, int&, int&, bool&, GlyphOverflowAndFallbackFontsMap&) +IntRect SVGInlineFlowBox::calculateBoundaries() const { - // no-op - return 0; + IntRect childRect; + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + childRect.unite(child->calculateBoundaries()); + return childRect; } } // namespace WebCore diff --git a/WebCore/rendering/SVGInlineFlowBox.h b/WebCore/rendering/SVGInlineFlowBox.h index 17ad528..ba4c5b2 100644 --- a/WebCore/rendering/SVGInlineFlowBox.h +++ b/WebCore/rendering/SVGInlineFlowBox.h @@ -41,8 +41,10 @@ public: void setHeight(int h) { m_height = h; } virtual void paint(RenderObject::PaintInfo&, int tx, int ty); - virtual int placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); - + + virtual IntRect calculateBoundaries() const; + void layoutFlowBox(); + private: int m_height; }; diff --git a/WebCore/rendering/SVGInlineTextBox.cpp b/WebCore/rendering/SVGInlineTextBox.cpp index 8bd7fef..68b4fd0 100644 --- a/WebCore/rendering/SVGInlineTextBox.cpp +++ b/WebCore/rendering/SVGInlineTextBox.cpp @@ -21,25 +21,28 @@ */ #include "config.h" - -#if ENABLE(SVG) #include "SVGInlineTextBox.h" -#include "Frame.h" +#if ENABLE(SVG) +#include "FloatConversion.h" #include "GraphicsContext.h" #include "InlineFlowBox.h" +#include "RenderBlock.h" #include "RenderSVGResource.h" #include "SVGRootInlineBox.h" #include "SVGTextLayoutUtilities.h" -#include "Text.h" #include <float.h> +using namespace std; + namespace WebCore { -SVGInlineTextBox::SVGInlineTextBox(RenderObject* obj) - : InlineTextBox(obj) +SVGInlineTextBox::SVGInlineTextBox(RenderObject* object) + : InlineTextBox(object) , m_height(0) + , m_paintingResource(0) + , m_paintingResourceMode(ApplyToDefaultMode) { } @@ -60,522 +63,550 @@ SVGRootInlineBox* SVGInlineTextBox::svgRootInlineBox() const return static_cast<SVGRootInlineBox*>(parentBox); } -float SVGInlineTextBox::calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed, String& glyphName) const +void SVGInlineTextBox::measureCharacter(RenderStyle* style, int position, int& charsConsumed, String& glyphName, String& unicodeString, float& glyphWidth, float& glyphHeight) const { ASSERT(style); - return style->font().floatWidth(svgTextRunForInlineTextBox(textRenderer()->characters() + offset, 1, style, this, 0), extraCharsAvailable, charsConsumed, glyphName); -} -float SVGInlineTextBox::calculateGlyphHeight(RenderStyle* style, int, int) const -{ - // This is just a guess, and the only purpose of this function is to centralize this hack. - // In real-life top-top-bottom scripts this won't be enough, I fear. - return style->font().ascent() + style->font().descent(); + int offset = direction() == RTL ? end() - position : start() + position; + int extraCharsAvailable = len() - position - 1; + const UChar* characters = textRenderer()->characters(); + + const Font& font = style->font(); + glyphWidth = font.floatWidth(svgTextRunForInlineTextBox(characters + offset, 1, style, this), extraCharsAvailable, charsConsumed, glyphName); + glyphHeight = font.height(); + + // The unicodeString / glyphName pair is needed for kerning calculations. + unicodeString = String(characters + offset, charsConsumed); } -FloatRect SVGInlineTextBox::calculateGlyphBoundaries(RenderStyle* style, int offset, const SVGChar& svgChar) const +int SVGInlineTextBox::offsetForPosition(int xCoordinate, bool includePartialGlyphs) const { - const Font& font = style->font(); + ASSERT(!m_currentChunkPart.isValid()); + float x = xCoordinate; - // Take RTL text into account and pick right glyph width/height. - float glyphWidth = 0.0f; + RenderText* textRenderer = this->textRenderer(); + ASSERT(textRenderer); - // FIXME: account for multi-character glyphs - int charsConsumed; - String glyphName; - if (direction() == LTR) - glyphWidth = calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName); - else - glyphWidth = calculateGlyphWidth(style, start() + end() - offset, 0, charsConsumed, glyphName); + RenderStyle* style = textRenderer->style(); + ASSERT(style); - float x1 = svgChar.x; - float x2 = svgChar.x + glyphWidth; + RenderBlock* containingBlock = textRenderer->containingBlock(); + ASSERT(containingBlock); - float y1 = svgChar.y - font.ascent(); - float y2 = svgChar.y + font.descent(); + // Move incoming relative x position to absolute position, as the character origins stored in the chunk parts use absolute coordinates + x += containingBlock->x(); - FloatRect glyphRect(x1, y1, x2 - x1, y2 - y1); + // Figure out which text chunk part is hit + SVGTextChunkPart hitPart; - // Take per-character transformations into account - glyphRect = svgChar.characterTransform().mapRect(glyphRect); + const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); + for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) { + const SVGTextChunkPart& part = *it; - return glyphRect; -} + // Check whether we're past the hit part. + if (x < part.firstCharacter->x) + break; -// Helper class for closestCharacterToPosition() -struct SVGInlineTextBoxClosestCharacterToPositionWalker { - SVGInlineTextBoxClosestCharacterToPositionWalker(int x, int y) - : m_character(0) - , m_distance(FLT_MAX) - , m_x(x) - , m_y(y) - , m_offsetOfHitCharacter(0) - { + hitPart = part; } - void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm, - const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) - { - RenderStyle* style = textBox->textRenderer()->style(); + // If we did not hit anything, just exit. + if (!hitPart.isValid()) + return 0; - Vector<SVGChar>::iterator closestCharacter = 0; - unsigned int closestOffset = UINT_MAX; + // Before calling Font::offsetForPosition(), subtract the start position of the first character + // in the hit text chunk part, to pass in coordinates, which are relative to the text chunk part, as + // constructTextRun() only builds a TextRun for the current chunk part, not the whole inline text box. + x -= hitPart.firstCharacter->x; - for (Vector<SVGChar>::iterator it = start; it != end; ++it) { - if (it->isHidden()) - continue; + m_currentChunkPart = hitPart; + TextRun textRun(constructTextRun(style)); + m_currentChunkPart = SVGTextChunkPart(); - unsigned int newOffset = textBox->start() + (it - start) + startOffset; - FloatRect glyphRect = chunkCtm.mapRect(textBox->calculateGlyphBoundaries(style, newOffset, *it)); + // Eventually handle lengthAdjust="spacingAndGlyphs". + if (!m_chunkTransformation.isIdentity()) + textRun.setGlyphScale(narrowPrecisionToFloat(isVerticalWritingMode(style->svgStyle()) ? m_chunkTransformation.d() : m_chunkTransformation.a())); - // Take RTL text into account and pick right glyph width/height. - // NOTE: This offset has to be corrected _after_ calling calculateGlyphBoundaries - if (textBox->direction() == RTL) - newOffset = textBox->start() + textBox->end() - newOffset; + return hitPart.offset + style->font().offsetForPosition(textRun, x, includePartialGlyphs); +} - // Calculate distances relative to the glyph mid-point. I hope this is accurate enough. - float xDistance = glyphRect.x() + glyphRect.width() / 2.0f - m_x; - float yDistance = glyphRect.y() - glyphRect.height() / 2.0f - m_y; +int SVGInlineTextBox::positionForOffset(int) const +{ + // SVG doesn't use the offset <-> position selection system. + ASSERT_NOT_REACHED(); + return 0; +} - float newDistance = sqrtf(xDistance * xDistance + yDistance * yDistance); - if (newDistance <= m_distance) { - m_distance = newDistance; - closestOffset = newOffset; - closestCharacter = it; - } - } +FloatRect SVGInlineTextBox::selectionRectForTextChunkPart(const SVGTextChunkPart& part, int partStartPos, int partEndPos, RenderStyle* style) +{ + // Map startPos/endPos positions into chunk part + mapStartEndPositionsIntoChunkPartCoordinates(partStartPos, partEndPos, part); - if (closestOffset != UINT_MAX) { - // Record current chunk, if it contains the current closest character next to the mouse. - m_character = closestCharacter; - m_offsetOfHitCharacter = closestOffset; - } - } + if (partStartPos >= partEndPos) + return FloatRect(); - SVGChar* character() const - { - return m_character; - } + // Set current chunk part, so constructTextRun() works properly. + m_currentChunkPart = part; - int offsetOfHitCharacter() const - { - if (!m_character) - return 0; + const Font& font = style->font(); + Vector<SVGChar>::const_iterator character = part.firstCharacter; + FloatPoint textOrigin(character->x, character->y - font.ascent()); - return m_offsetOfHitCharacter; - } + FloatRect partRect(font.selectionRectForText(constructTextRun(style), textOrigin, part.height, partStartPos, partEndPos)); + m_currentChunkPart = SVGTextChunkPart(); -private: - Vector<SVGChar>::iterator m_character; - float m_distance; + return character->characterTransform().mapRect(partRect); +} - int m_x; - int m_y; - int m_offsetOfHitCharacter; -}; +IntRect SVGInlineTextBox::selectionRect(int, int, int startPos, int endPos) +{ + ASSERT(!m_currentChunkPart.isValid()); -// Helper class for selectionRect() -struct SVGInlineTextBoxSelectionRectWalker { - SVGInlineTextBoxSelectionRectWalker() - { - } + int boxStart = start(); + startPos = max(startPos - boxStart, 0); + endPos = min(endPos - boxStart, static_cast<int>(len())); - void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm, - const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) - { - RenderStyle* style = textBox->textRenderer()->style(); + if (startPos >= endPos) + return IntRect(); - for (Vector<SVGChar>::iterator it = start; it != end; ++it) { - if (it->isHidden()) - continue; + RenderText* text = textRenderer(); + ASSERT(text); - unsigned int newOffset = textBox->start() + (it - start) + startOffset; - m_selectionRect.unite(textBox->calculateGlyphBoundaries(style, newOffset, *it)); - } + RenderStyle* style = text->style(); + ASSERT(style); - m_selectionRect = chunkCtm.mapRect(m_selectionRect); - } + FloatRect selectionRect; - FloatRect selectionRect() const - { - return m_selectionRect; - } + // Figure out which text chunk part is hit + const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); + for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) + selectionRect.unite(selectionRectForTextChunkPart(*it, startPos, endPos, style)); + + // Resepect possible chunk transformation + if (m_chunkTransformation.isIdentity()) + return enclosingIntRect(selectionRect); -private: - FloatRect m_selectionRect; -}; + return enclosingIntRect(m_chunkTransformation.mapRect(selectionRect)); +} -SVGChar* SVGInlineTextBox::closestCharacterToPosition(int x, int y, int& offsetOfHitCharacter) const +void SVGInlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int, int) { - SVGRootInlineBox* rootBox = svgRootInlineBox(); - if (!rootBox) - return 0; + ASSERT(renderer()->shouldPaintWithinRoot(paintInfo)); + ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); + ASSERT(truncation() == cNoTruncation); - SVGInlineTextBoxClosestCharacterToPositionWalker walkerCallback(x, y); - SVGTextChunkWalker<SVGInlineTextBoxClosestCharacterToPositionWalker> walker(&walkerCallback, &SVGInlineTextBoxClosestCharacterToPositionWalker::chunkPortionCallback); + if (renderer()->style()->visibility() != VISIBLE) + return; - rootBox->walkTextChunks(&walker, this); + // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox. + // If we ever need that for SVG, it's very easy to refactor and reuse the code. - offsetOfHitCharacter = walkerCallback.offsetOfHitCharacter(); - return walkerCallback.character(); -} + RenderObject* parentRenderer = parent()->renderer(); + ASSERT(parentRenderer); -bool SVGInlineTextBox::svgCharacterHitsPosition(int x, int y, int& closestOffsetInBox) const -{ - int offsetOfHitCharacter = 0; - SVGChar* charAtPosPtr = closestCharacterToPosition(x, y, offsetOfHitCharacter); - if (!charAtPosPtr) - return false; + RenderStyle* style = parentRenderer->style(); + ASSERT(style); - SVGChar& charAtPos = *charAtPosPtr; - RenderStyle* style = textRenderer()->style(m_firstLine); - FloatRect glyphRect = calculateGlyphBoundaries(style, offsetOfHitCharacter, charAtPos); + const SVGRenderStyle* svgStyle = style->svgStyle(); + ASSERT(svgStyle); - // FIXME: Why? - if (direction() == RTL) - offsetOfHitCharacter++; + bool hasFill = svgStyle->hasFill(); + bool hasStroke = svgStyle->hasStroke(); + bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; - // The caller actually the closest offset before/after the hit char - // closestCharacterToPosition returns us offsetOfHitCharacter. - closestOffsetInBox = offsetOfHitCharacter; + // Determine whether or not we're selected. + bool isPrinting = parentRenderer->document()->printing(); + bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone; + if (!hasSelection && paintSelectedTextOnly) + return; - // FIXME: (bug 13910) This code does not handle bottom-to-top/top-to-bottom vertical text. + RenderStyle* selectionStyle = style; + if (hasSelection) { + selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION); + if (selectionStyle) { + const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle(); + ASSERT(svgSelectionStyle); + + if (!hasFill) + hasFill = svgSelectionStyle->hasFill(); + if (!hasStroke) + hasStroke = svgSelectionStyle->hasStroke(); + } else + selectionStyle = style; + } - // Check whether y position hits the current character - if (y < charAtPos.y - glyphRect.height() || y > charAtPos.y) - return false; + // Compute text match marker rects. It needs to traverse all text chunk parts to figure + // out the union selection rect of all text chunk parts that contribute to the selection. + computeTextMatchMarkerRect(style); + ASSERT(!m_currentChunkPart.isValid()); + + const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); + for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) { + ASSERT(!m_paintingResource); + + // constructTextRun() uses the current chunk part to figure out what text to render. + m_currentChunkPart = *it; + paintInfo.context->save(); + + // Prepare context and draw text + if (!m_chunkTransformation.isIdentity()) + paintInfo.context->concatCTM(m_chunkTransformation); + + Vector<SVGChar>::const_iterator firstCharacter = m_currentChunkPart.firstCharacter; + AffineTransform characterTransform = firstCharacter->characterTransform(); + if (!characterTransform.isIdentity()) + paintInfo.context->concatCTM(characterTransform); + + FloatPoint textOrigin(firstCharacter->x, firstCharacter->y); + + // Draw background once (not in both fill/stroke phases) + if (!isPrinting && !paintSelectedTextOnly && hasSelection) + paintSelection(paintInfo.context, textOrigin, style); + + // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations. + int decorations = style->textDecorationsInEffect(); + if (decorations & UNDERLINE) + paintDecoration(paintInfo.context, textOrigin, UNDERLINE, hasSelection); + if (decorations & OVERLINE) + paintDecoration(paintInfo.context, textOrigin, OVERLINE, hasSelection); + + // Fill text + if (hasFill) { + m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode; + paintText(paintInfo.context, textOrigin, style, selectionStyle, hasSelection, paintSelectedTextOnly); + } - // Check whether x position hits the current character - if (x < charAtPos.x) { - if (closestOffsetInBox > 0 && direction() == LTR) - return true; - if (closestOffsetInBox < static_cast<int>(end()) && direction() == RTL) - return true; + // Stroke text + if (hasStroke) { + m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode; + paintText(paintInfo.context, textOrigin, style, selectionStyle, hasSelection, paintSelectedTextOnly); + } - return false; + // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text. + if (decorations & LINE_THROUGH) + paintDecoration(paintInfo.context, textOrigin, LINE_THROUGH, hasSelection); + + m_paintingResourceMode = ApplyToDefaultMode; + paintInfo.context->restore(); } - // Adjust the closest offset to after the char if x was after the char midpoint - if (x >= charAtPos.x + glyphRect.width() / 2.0) - closestOffsetInBox += direction() == RTL ? -1 : 1; + m_currentChunkPart = SVGTextChunkPart(); + ASSERT(!m_paintingResource); +} + +bool SVGInlineTextBox::acquirePaintingResource(GraphicsContext*& context, RenderStyle* style) +{ + ASSERT(m_paintingResourceMode != ApplyToDefaultMode); + + RenderObject* parentRenderer = parent()->renderer(); + ASSERT(parentRenderer); + + if (m_paintingResourceMode & ApplyToFillMode) + m_paintingResource = RenderSVGResource::fillPaintingResource(parentRenderer, style); + else if (m_paintingResourceMode & ApplyToStrokeMode) + m_paintingResource = RenderSVGResource::strokePaintingResource(parentRenderer, style); + else { + // We're either called for stroking or filling. + ASSERT_NOT_REACHED(); + } - // If we are past the last glyph of this box, don't mark it as 'hit' - if (x >= charAtPos.x + glyphRect.width() && closestOffsetInBox == (int) end()) + if (!m_paintingResource) return false; + m_paintingResource->applyResource(parentRenderer, style, context, m_paintingResourceMode); return true; } -int SVGInlineTextBox::offsetForPosition(int, bool) const +void SVGInlineTextBox::releasePaintingResource(GraphicsContext*& context) { - // SVG doesn't use the offset <-> position selection system. - ASSERT_NOT_REACHED(); - return 0; + ASSERT(m_paintingResource); + + RenderObject* parentRenderer = parent()->renderer(); + ASSERT(parentRenderer); + + m_paintingResource->postApplyResource(parentRenderer, context, m_paintingResourceMode); + m_paintingResource = 0; } -int SVGInlineTextBox::positionForOffset(int) const +bool SVGInlineTextBox::prepareGraphicsContextForTextPainting(GraphicsContext*& context, TextRun& textRun, RenderStyle* style) { - // SVG doesn't use the offset <-> position selection system. - ASSERT_NOT_REACHED(); - return 0; + bool acquiredResource = acquirePaintingResource(context, style); + +#if ENABLE(SVG_FONTS) + // SVG Fonts need access to the painting resource used to draw the current text chunk. + if (acquiredResource) + textRun.setActivePaintingResource(m_paintingResource); +#endif + + return acquiredResource; } -bool SVGInlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int x, int y, int tx, int ty) +void SVGInlineTextBox::restoreGraphicsContextAfterTextPainting(GraphicsContext*& context, TextRun& textRun) { - ASSERT(!isLineBreak()); + releasePaintingResource(context); - IntRect rect = selectionRect(0, 0, 0, len()); - if (renderer()->style()->visibility() == VISIBLE && rect.contains(x, y)) { - renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); - return true; - } +#if ENABLE(SVG_FONTS) + textRun.setActivePaintingResource(0); +#endif +} - return false; +TextRun SVGInlineTextBox::constructTextRun(RenderStyle* style) const +{ + ASSERT(m_currentChunkPart.isValid()); + return svgTextRunForInlineTextBox(textRenderer()->text()->characters() + start() + m_currentChunkPart.offset, m_currentChunkPart.length, style, this); } -IntRect SVGInlineTextBox::selectionRect(int, int, int startPos, int endPos) +void SVGInlineTextBox::mapStartEndPositionsIntoChunkPartCoordinates(int& startPos, int& endPos, const SVGTextChunkPart& part) const { if (startPos >= endPos) - return IntRect(); + return; - // TODO: Actually respect startPos/endPos - we're returning the _full_ selectionRect - // here. This won't lead to visible bugs, but to extra work being done. Investigate. - SVGRootInlineBox* rootBox = svgRootInlineBox(); - if (!rootBox) - return IntRect(); + // Take <text x="10 50 100">ABC</text> as example. We're called three times from paint(), because all absolute positioned + // characters are drawn on their own. For each of them we want to find out whehter it's selected. startPos=0, endPos=1 + // could mean A, B or C is hit, depending on which chunk part is processed at the moment. With the offset & length values + // of each chunk part we can easily find out which one is meant to be selected. Bail out for the other chunk parts. + // If starPos is behind the current chunk or the endPos ends before this text chunk part, we're not meant to be selected. + if (startPos >= part.offset + part.length || endPos <= part.offset) { + startPos = 0; + endPos = -1; + return; + } + + // The current processed chunk part is hit. When painting the selection, constructTextRun() builds + // a TextRun object whose startPos is 0 and endPos is chunk part length. The code below maps the incoming + // startPos/endPos range into a [0, part length] coordinate system, relative to the current chunk part. + if (startPos < part.offset) + startPos = 0; + else + startPos -= part.offset; - SVGInlineTextBoxSelectionRectWalker walkerCallback; - SVGTextChunkWalker<SVGInlineTextBoxSelectionRectWalker> walker(&walkerCallback, &SVGInlineTextBoxSelectionRectWalker::chunkPortionCallback); + if (endPos > part.offset + part.length) + endPos = part.length; + else { + ASSERT(endPos >= part.offset); + endPos -= part.offset; + } - rootBox->walkTextChunks(&walker, this); - return enclosingIntRect(walkerCallback.selectionRect()); + ASSERT(startPos < endPos); } -bool SVGInlineTextBox::chunkSelectionStartEnd(const UChar* chunk, int chunkLength, int& selectionStart, int& selectionEnd) +void SVGInlineTextBox::selectionStartEnd(int& startPos, int& endPos) { - // 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); + InlineTextBox::selectionStartEnd(startPos, endPos); - 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; + if (!m_currentChunkPart.isValid()) + return; - return selectionStart < selectionEnd; + mapStartEndPositionsIntoChunkPartCoordinates(startPos, endPos, m_currentChunkPart); } -void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int tx, int ty, const SVGChar& svgChar, const UChar* chars, int length, SVGTextPaintInfo& textPaintInfo) +void SVGInlineTextBox::computeTextMatchMarkerRect(RenderStyle* style) { - if (renderer()->style()->visibility() != VISIBLE || paintInfo.phase == PaintPhaseOutline) + ASSERT(!m_currentChunkPart.isValid()); + Node* node = renderer()->node(); + if (!node || !node->inDocument()) return; - ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines); + Document* document = renderer()->document(); + Vector<DocumentMarker> markers = document->markersForNode(renderer()->node()); - RenderText* text = textRenderer(); - ASSERT(text); + Vector<DocumentMarker>::iterator markerEnd = markers.end(); + for (Vector<DocumentMarker>::iterator markerIt = markers.begin(); markerIt != markerEnd; ++markerIt) { + const DocumentMarker& marker = *markerIt; - bool isPrinting = text->document()->printing(); + // SVG is only interessted in the TextMatch marker, for now. + if (marker.type != DocumentMarker::TextMatch) + continue; - // Determine whether or not we're selected. - bool haveSelection = !isPrinting && selectionState() != RenderObject::SelectionNone; - if (!haveSelection && paintInfo.phase == PaintPhaseSelection) - // When only painting the selection, don't bother to paint if there is none. - return; + FloatRect markerRect; + int partStartPos = max(marker.startOffset - start(), static_cast<unsigned>(0)); + int partEndPos = min(marker.endOffset - start(), static_cast<unsigned>(len())); - // Determine whether or not we have a composition. - bool containsComposition = text->document()->frame()->editor()->compositionNode() == text->node(); - bool useCustomUnderlines = containsComposition && text->document()->frame()->editor()->compositionUsesCustomUnderlines(); - - // Set our font - RenderStyle* styleToUse = text->style(isFirstLineStyle()); - const Font& font = styleToUse->font(); - - 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 && textPaintInfo.subphase == SVGTextPaintSubphaseBackground) { -#if PLATFORM(MAC) - // Custom highlighters go behind everything else. - if (styleToUse->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) - paintCustomHighlight(tx, ty, styleToUse->highlight()); -#endif + // Iterate over all text chunk parts, to see which ones have to be highlighted + const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); + for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) + markerRect.unite(selectionRectForTextChunkPart(*it, partStartPos, partEndPos, style)); - if (containsComposition && !useCustomUnderlines) - paintCompositionBackground(paintInfo.context, tx, ty, styleToUse, font, - text->document()->frame()->editor()->compositionStart(), - text->document()->frame()->editor()->compositionEnd()); - - paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, true); + if (!m_chunkTransformation.isIdentity()) + markerRect = m_chunkTransformation.mapRect(markerRect); - if (haveSelection && !useCustomUnderlines) { - int boxStartOffset = chars - text->characters() - start(); - paintSelection(boxStartOffset, svgChar, chars, length, paintInfo.context, styleToUse, font); - } + document->setRenderedRectForMarker(node, marker, renderer()->localToAbsoluteQuad(markerRect).enclosingBoundingBox()); } +} - 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. See how it's done in InlineTextBox.cpp. - 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); +static inline float positionOffsetForDecoration(ETextDecoration decoration, const Font& font, float thickness) +{ + // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified. + // Compatible with Batik/Opera. + if (decoration == UNDERLINE) + return font.ascent() + thickness * 1.5f; + if (decoration == OVERLINE) + return thickness; + if (decoration == LINE_THROUGH) + return font.ascent() * 5.0f / 8.0f; -#if ENABLE(SVG_FONTS) - // SVG Fonts need access to the painting resource used to draw the current text chunk. - ASSERT(textPaintInfo.activePaintingResource); - run.setActivePaintingResource(textPaintInfo.activePaintingResource); -#endif + ASSERT_NOT_REACHED(); + return 0.0f; +} - 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); - } +static inline float thicknessForDecoration(ETextDecoration, const Font& font) +{ + // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified. + // Compatible with Batik/Opera + return font.size() / 20.0f; +} + +static inline RenderObject* findRenderObjectDefininingTextDecoration(InlineFlowBox* parentBox, ETextDecoration decoration) +{ + // Lookup render object which has text-decoration set. + RenderObject* renderer = 0; + while (parentBox) { + renderer = parentBox->renderer(); - if (setShadow) - paintInfo.context->clearShadow(); - } + // Explicitely check textDecoration() not textDecorationsInEffect(), which is inherited to + // children, as we want to lookup the render object whose style defined the text-decoration. + if (renderer->style() && renderer->style()->textDecoration() & decoration) + break; - if (paintInfo.phase != PaintPhaseSelection && textPaintInfo.subphase == SVGTextPaintSubphaseForeground) { - paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, false); - - if (useCustomUnderlines) { - const Vector<CompositionUnderline>& underlines = text->document()->frame()->editor()->customCompositionUnderlines(); - size_t numUnderlines = underlines.size(); - - for (size_t index = 0; index < numUnderlines; ++index) { - const CompositionUnderline& underline = underlines[index]; - - if (underline.endOffset <= start()) - // underline is completely before this run. This might be an underline that sits - // before the first run we draw, or underlines that were within runs we skipped - // due to truncation. - continue; - - if (underline.startOffset <= end()) { - // underline intersects this run. Paint it. - paintCompositionUnderline(paintInfo.context, tx, ty, underline); - if (underline.endOffset > end() + 1) - // underline also runs into the next run. Bail now, no more marker advancement. - break; - } else - // underline is completely after this run, bail. A later run will paint it. - break; - } - } - + parentBox = parentBox->parent(); } - if (!ctm.isIdentity()) - paintInfo.context->concatCTM(ctm.inverse()); + ASSERT(renderer); + return renderer; } -void SVGInlineTextBox::paintSelection(int boxStartOffset, const SVGChar& svgChar, const UChar*, int length, GraphicsContext* p, RenderStyle* style, const Font& font) +void SVGInlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& textOrigin, ETextDecoration decoration, bool hasSelection) { - if (selectionState() == RenderObject::SelectionNone) - return; - - int startPos, endPos; - selectionStartEnd(startPos, endPos); - - if (startPos >= endPos) - return; - - Color textColor = style->visitedDependentColor(CSSPropertyColor); - Color color = renderer()->selectionBackgroundColor(); - if (!color.isValid() || !color.alpha()) - return; + // Find out which render style defined the text-decoration, as its fill/stroke properties have to be used for drawing instead of ours. + RenderObject* decorationRenderer = findRenderObjectDefininingTextDecoration(parent(), decoration); + RenderStyle* decorationStyle = decorationRenderer->style(); + ASSERT(decorationStyle); - // If the text color ends up being the same as the selection background, invert the selection - // background. This should basically never happen, since the selection has transparency. - if (textColor == color) - color = Color(0xff - color.red(), 0xff - color.green(), 0xff - color.blue()); + const SVGRenderStyle* svgDecorationStyle = decorationStyle->svgStyle(); + ASSERT(svgDecorationStyle); - // Map from text box positions and a given start offset to chunk positions - // 'boxStartOffset' represents the beginning of the text chunk. - if ((startPos > boxStartOffset && endPos > boxStartOffset + length) || boxStartOffset >= endPos) - return; + bool hasDecorationFill = svgDecorationStyle->hasFill(); + bool hasDecorationStroke = svgDecorationStyle->hasStroke(); - if (endPos > boxStartOffset + length) - endPos = boxStartOffset + length; + if (hasSelection) { + if (RenderStyle* pseudoStyle = decorationRenderer->getCachedPseudoStyle(SELECTION)) { + decorationStyle = pseudoStyle; - if (startPos < boxStartOffset) - startPos = boxStartOffset; + svgDecorationStyle = decorationStyle->svgStyle(); + ASSERT(svgDecorationStyle); - ASSERT(startPos >= boxStartOffset); - ASSERT(endPos <= boxStartOffset + length); - ASSERT(startPos < endPos); + if (!hasDecorationFill) + hasDecorationFill = svgDecorationStyle->hasFill(); + if (!hasDecorationStroke) + hasDecorationStroke = svgDecorationStyle->hasStroke(); + } + } - p->save(); + if (decorationStyle->visibility() == HIDDEN) + return; - int adjust = startPos >= boxStartOffset ? boxStartOffset : 0; - p->drawHighlightForText(font, svgTextRunForInlineTextBox(textRenderer()->characters() + start() + boxStartOffset, length, style, this, svgChar.x), - IntPoint((int) svgChar.x, (int) svgChar.y - font.ascent()), - font.ascent() + font.descent(), color, style->colorSpace(), startPos - adjust, endPos - adjust); + if (hasDecorationFill) { + m_paintingResourceMode = ApplyToFillMode; + paintDecorationWithStyle(context, textOrigin, decorationStyle, decoration); + } - p->restore(); + if (hasDecorationStroke) { + m_paintingResourceMode = ApplyToStrokeMode; + paintDecorationWithStyle(context, textOrigin, decorationStyle, decoration); + } } -static inline Path pathForDecoration(ETextDecoration decoration, RenderObject* object, float x, float y, float width) +void SVGInlineTextBox::paintDecorationWithStyle(GraphicsContext* context, const FloatPoint& textOrigin, RenderStyle* decorationStyle, ETextDecoration decoration) { - float thickness = SVGRenderStyle::cssPrimitiveToLength(object, object->style()->svgStyle()->strokeWidth(), 1.0f); + ASSERT(!m_paintingResource); + ASSERT(m_paintingResourceMode != ApplyToDefaultMode); + ASSERT(m_currentChunkPart.isValid()); - const Font& font = object->style()->font(); - thickness = max(thickness * powf(font.size(), 2.0f) / font.unitsPerEm(), 1.0f); + const Font& font = decorationStyle->font(); - if (decoration == UNDERLINE) - y += thickness * 1.5f; // For compatibility with Batik/Opera - else if (decoration == OVERLINE) - y += thickness; + // The initial y value refers to overline position. + float thickness = thicknessForDecoration(decoration, font); + float x = textOrigin.x(); + float y = textOrigin.y() - font.ascent() + positionOffsetForDecoration(decoration, font, thickness); + + context->save(); + context->beginPath(); + context->addPath(Path::createRectangle(FloatRect(x, y, m_currentChunkPart.width, thickness))); - float halfThickness = thickness / 2.0f; - return Path::createRectangle(FloatRect(x + halfThickness, y, width - 2.0f * halfThickness, thickness)); + if (acquirePaintingResource(context, decorationStyle)) + releasePaintingResource(context); + + context->restore(); } -void SVGInlineTextBox::paintDecoration(ETextDecoration decoration, GraphicsContext* context, int tx, int ty, int width, const SVGChar& svgChar, const SVGTextDecorationInfo& info) +void SVGInlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& textOrigin, RenderStyle* style) { - if (renderer()->style()->visibility() != VISIBLE) + // See if we have a selection to paint at all. + int startPos, endPos; + selectionStartEnd(startPos, endPos); + if (startPos >= endPos) return; - // This function does NOT accept combinated text decorations. It's meant to be invoked for just one. - ASSERT(decoration == TDNONE || decoration == UNDERLINE || decoration == OVERLINE || decoration == LINE_THROUGH || decoration == BLINK); - - bool isFilled = info.fillServerMap.contains(decoration); - bool isStroked = info.strokeServerMap.contains(decoration); - - if (!isFilled && !isStroked) + Color backgroundColor = renderer()->selectionBackgroundColor(); + if (!backgroundColor.isValid() || !backgroundColor.alpha()) return; - int baseline = renderer()->style(m_firstLine)->font().ascent(); - if (decoration == UNDERLINE) - ty += baseline; - else if (decoration == LINE_THROUGH) - ty += 2 * baseline / 3; + const Font& font = style->font(); + + FloatPoint selectionOrigin = textOrigin; + selectionOrigin.move(0, -font.ascent()); context->save(); - context->beginPath(); + context->setFillColor(backgroundColor, style->colorSpace()); + context->fillRect(font.selectionRectForText(constructTextRun(style), selectionOrigin, m_currentChunkPart.height, startPos, endPos), backgroundColor, style->colorSpace()); + context->restore(); +} - AffineTransform ctm = svgChar.characterTransform(); - if (!ctm.isIdentity()) - context->concatCTM(ctm); +void SVGInlineTextBox::paintText(GraphicsContext* context, const FloatPoint& textOrigin, RenderStyle* style, RenderStyle* selectionStyle, bool hasSelection, bool paintSelectedTextOnly) +{ + ASSERT(style); + ASSERT(selectionStyle); + ASSERT(m_currentChunkPart.isValid()); - if (isFilled) { - if (RenderObject* fillObject = info.fillServerMap.get(decoration)) { - if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(fillObject, fillObject->style())) { - context->addPath(pathForDecoration(decoration, fillObject, tx, ty, width)); - if (fillPaintingResource->applyResource(fillObject, fillObject->style(), context, ApplyToFillMode)) - fillPaintingResource->postApplyResource(fillObject, context, ApplyToFillMode); - } + int startPos = 0; + int endPos = 0; + selectionStartEnd(startPos, endPos); + + const Font& font = style->font(); + TextRun textRun(constructTextRun(style)); + + // Fast path if there is no selection, just draw the whole chunk part using the regular style + if (!hasSelection || startPos >= endPos) { + if (prepareGraphicsContextForTextPainting(context, textRun, style)) { + font.drawText(context, textRun, textOrigin, 0, m_currentChunkPart.length); + restoreGraphicsContextAfterTextPainting(context, textRun); } + + return; } - if (isStroked) { - if (RenderObject* strokeObject = info.strokeServerMap.get(decoration)) { - if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(strokeObject, strokeObject->style())) { - context->addPath(pathForDecoration(decoration, strokeObject, tx, ty, width)); - if (strokePaintingResource->applyResource(strokeObject, strokeObject->style(), context, ApplyToStrokeMode)) - strokePaintingResource->postApplyResource(strokeObject, context, ApplyToStrokeMode); - } + // Eventually draw text using regular style until the start position of the selection + if (startPos > 0 && !paintSelectedTextOnly) { + if (prepareGraphicsContextForTextPainting(context, textRun, style)) { + font.drawText(context, textRun, textOrigin, 0, startPos); + restoreGraphicsContextAfterTextPainting(context, textRun); } } - context->restore(); + // Draw text using selection style from the start to the end position of the selection + TextRun selectionTextRun(constructTextRun(selectionStyle)); + if (prepareGraphicsContextForTextPainting(context, selectionTextRun, selectionStyle)) { + selectionStyle->font().drawText(context, selectionTextRun, textOrigin, startPos, endPos); + restoreGraphicsContextAfterTextPainting(context, selectionTextRun); + } + + // Eventually draw text using regular style from the end position of the selection to the end of the current chunk part + if (endPos < m_currentChunkPart.length && !paintSelectedTextOnly) { + if (prepareGraphicsContextForTextPainting(context, textRun, style)) { + font.drawText(context, textRun, textOrigin, endPos, m_currentChunkPart.length); + restoreGraphicsContextAfterTextPainting(context, textRun); + } + } } void SVGInlineTextBox::buildLayoutInformation(SVGCharacterLayoutInfo& info, SVGLastGlyphInfo& lastGlyph) @@ -583,7 +614,7 @@ void SVGInlineTextBox::buildLayoutInformation(SVGCharacterLayoutInfo& info, SVGL RenderText* textRenderer = this->textRenderer(); ASSERT(textRenderer); - RenderStyle* style = textRenderer->style(isFirstLineStyle()); + RenderStyle* style = textRenderer->style(); ASSERT(style); const Font& font = style->font(); @@ -606,20 +637,9 @@ void SVGInlineTextBox::buildLayoutInformation(SVGCharacterLayoutInfo& info, SVGL float glyphWidth = 0.0f; float glyphHeight = 0.0f; - - int extraCharsAvailable = length - i - 1; - - String unicodeStr; String glyphName; - if (textDirection == RTL) { - glyphWidth = calculateGlyphWidth(style, endPosition - i, extraCharsAvailable, charsConsumed, glyphName); - glyphHeight = calculateGlyphHeight(style, endPosition - i, extraCharsAvailable); - unicodeStr = String(characters + endPosition - i, charsConsumed); - } else { - glyphWidth = calculateGlyphWidth(style, startPosition + i, extraCharsAvailable, charsConsumed, glyphName); - glyphHeight = calculateGlyphHeight(style, startPosition + i, extraCharsAvailable); - unicodeStr = String(characters + startPosition + i, charsConsumed); - } + String unicodeString; + measureCharacter(style, i, charsConsumed, glyphName, unicodeString, glyphWidth, glyphHeight); bool assignedX = false; bool assignedY = false; @@ -682,7 +702,7 @@ void SVGInlineTextBox::buildLayoutInformation(SVGCharacterLayoutInfo& info, SVGL } // FIXME: SVG Kerning doesn't get applied on texts on path. - bool appliedSVGKerning = applySVGKerning(info, style, lastGlyph, unicodeStr, glyphName, isVerticalText); + bool appliedSVGKerning = applySVGKerning(info, style, lastGlyph, unicodeString, glyphName, isVerticalText); if (info.nextDrawnSeperated || spacing != 0.0f || appliedSVGKerning) { info.nextDrawnSeperated = false; svgChar.drawnSeperated = true; @@ -805,6 +825,38 @@ void SVGInlineTextBox::buildLayoutInformation(SVGCharacterLayoutInfo& info, SVGL } } +FloatRect SVGInlineTextBox::calculateGlyphBoundaries(RenderStyle* style, int position, const SVGChar& character) const +{ + int charsConsumed = 0; + String glyphName; + String unicodeString; + float glyphWidth = 0.0f; + float glyphHeight = 0.0f; + measureCharacter(style, position, charsConsumed, glyphName, unicodeString, glyphWidth, glyphHeight); + + FloatRect glyphRect(character.x, character.y - style->font().ascent(), glyphWidth, glyphHeight); + glyphRect = character.characterTransform().mapRect(glyphRect); + if (m_chunkTransformation.isIdentity()) + return glyphRect; + + return m_chunkTransformation.mapRect(glyphRect); +} + +IntRect SVGInlineTextBox::calculateBoundaries() const +{ + FloatRect textRect; + int baseline = baselinePosition(true); + + const Vector<SVGTextChunkPart>::const_iterator end = m_svgTextChunkParts.end(); + for (Vector<SVGTextChunkPart>::const_iterator it = m_svgTextChunkParts.begin(); it != end; ++it) + textRect.unite(it->firstCharacter->characterTransform().mapRect(FloatRect(it->firstCharacter->x, it->firstCharacter->y - baseline, it->width, it->height))); + + if (m_chunkTransformation.isIdentity()) + return enclosingIntRect(textRect); + + return enclosingIntRect(m_chunkTransformation.mapRect(textRect)); +} + } // namespace WebCore #endif diff --git a/WebCore/rendering/SVGInlineTextBox.h b/WebCore/rendering/SVGInlineTextBox.h index 77b6498..704b1f5 100644 --- a/WebCore/rendering/SVGInlineTextBox.h +++ b/WebCore/rendering/SVGInlineTextBox.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2007 Rob Buis <buis@kde.org> * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,60 +25,77 @@ #if ENABLE(SVG) #include "InlineTextBox.h" -#include "RenderSVGResource.h" +#include "SVGTextChunkLayoutInfo.h" +#include "SVGTextLayoutUtilities.h" namespace WebCore { +class RenderSVGResource; class SVGRootInlineBox; -struct SVGChar; struct SVGCharacterLayoutInfo; struct SVGLastGlyphInfo; -struct SVGTextDecorationInfo; -struct SVGTextPaintInfo; class SVGInlineTextBox : public InlineTextBox { public: - SVGInlineTextBox(RenderObject* obj); + SVGInlineTextBox(RenderObject*); + + virtual bool isSVGInlineTextBox() const { return true; } virtual int virtualHeight() const { return m_height; } - void setHeight(int h) { m_height = h; } + void setHeight(int height) { m_height = height; } virtual int selectionTop() { return m_y; } virtual int selectionHeight() { return m_height; } - virtual int offsetForPosition(int x, bool includePartialGlyphs = true) const; virtual int positionForOffset(int offset) const; - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); 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, SVGTextPaintInfo&); - - // SVGs custom paint selection method - void paintSelection(int boxStartOffset, const SVGChar&, const UChar*, int length, GraphicsContext*, RenderStyle*, const Font&); + virtual void selectionStartEnd(int& startPos, int& endPos); + void mapStartEndPositionsIntoChunkPartCoordinates(int& startPos, int& endPos, const SVGTextChunkPart&) const; - // SVGs custom paint decoration method - void paintDecoration(ETextDecoration, GraphicsContext*, int tx, int ty, int width, const SVGChar&, const SVGTextDecorationInfo&); - SVGRootInlineBox* svgRootInlineBox() const; // Helper functions shared with SVGRootInlineBox - float calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed, String& glyphName) const; - float calculateGlyphHeight(RenderStyle*, int offset, int extraCharsAvailable) const; - - FloatRect calculateGlyphBoundaries(RenderStyle*, int offset, const SVGChar&) const; - SVGChar* closestCharacterToPosition(int x, int y, int& offset) const; + void measureCharacter(RenderStyle*, int position, int& charsConsumed, String& glyphName, String& unicodeString, float& glyphWidth, float& glyphHeight) const; + FloatRect calculateGlyphBoundaries(RenderStyle*, int position, const SVGChar&) const; void buildLayoutInformation(SVGCharacterLayoutInfo&, SVGLastGlyphInfo&); + const AffineTransform& chunkTransformation() const { return m_chunkTransformation; } + void setChunkTransformation(const AffineTransform& transform) { m_chunkTransformation = transform; } + void addChunkPartInformation(const SVGTextChunkPart& part) { m_svgTextChunkParts.append(part); } + const Vector<SVGTextChunkPart>& svgTextChunkParts() const { return m_svgTextChunkParts; } + + virtual IntRect calculateBoundaries() const; + +private: + TextRun constructTextRun(RenderStyle*) const; + AffineTransform buildChunkTransformation(SVGChar& firstCharacter) const; + + bool acquirePaintingResource(GraphicsContext*&, RenderStyle*); + void releasePaintingResource(GraphicsContext*&); + + bool prepareGraphicsContextForTextPainting(GraphicsContext*&, TextRun&, RenderStyle*); + void restoreGraphicsContextAfterTextPainting(GraphicsContext*&, TextRun&); + + void computeTextMatchMarkerRect(RenderStyle*); + void paintDecoration(GraphicsContext*, const FloatPoint& textOrigin, ETextDecoration, bool hasSelection); + void paintDecorationWithStyle(GraphicsContext*, const FloatPoint& textOrigin, RenderStyle*, ETextDecoration); + void paintSelection(GraphicsContext*, const FloatPoint& textOrigin, RenderStyle*); + void paintText(GraphicsContext*, const FloatPoint& textOrigin, RenderStyle*, RenderStyle* selectionStyle, bool hasSelection, bool paintSelectedTextOnly); + + FloatRect selectionRectForTextChunkPart(const SVGTextChunkPart&, int partStartPos, int partEndPos, RenderStyle*); + 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; + AffineTransform m_chunkTransformation; + Vector<SVGTextChunkPart> m_svgTextChunkParts; + mutable SVGTextChunkPart m_currentChunkPart; + RenderSVGResource* m_paintingResource; + int m_paintingResourceMode; }; } // namespace WebCore diff --git a/WebCore/rendering/SVGRenderSupport.h b/WebCore/rendering/SVGRenderSupport.h index e961c73..dce8b8f 100644 --- a/WebCore/rendering/SVGRenderSupport.h +++ b/WebCore/rendering/SVGRenderSupport.h @@ -21,8 +21,8 @@ * */ -#ifndef SVGRenderBase_h -#define SVGRenderBase_h +#ifndef SVGRenderSupport_h +#define SVGRenderSupport_h #if ENABLE(SVG) #include "DashArray.h" @@ -55,9 +55,6 @@ public: // Helper function determining wheter overflow is hidden static bool isOverflowHidden(const RenderObject*); - // strokeBoundingBox() includes the marker boundaries for a RenderPath object - virtual FloatRect strokeBoundingBox() const { return FloatRect(); } - // Calculates the repaintRect in combination with filter, clipper and masker in local coordinates. void intersectRepaintRectWithResources(const RenderObject*, FloatRect&) const; @@ -93,4 +90,4 @@ const RenderObject* findTextRootObject(const RenderObject* start); #endif // ENABLE(SVG) -#endif // SVGRenderBase_h +#endif // SVGRenderSupport_h diff --git a/WebCore/rendering/SVGRenderTreeAsText.cpp b/WebCore/rendering/SVGRenderTreeAsText.cpp index 2a71dee..e110ce6 100644 --- a/WebCore/rendering/SVGRenderTreeAsText.cpp +++ b/WebCore/rendering/SVGRenderTreeAsText.cpp @@ -598,7 +598,7 @@ void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int i writeNameValuePair(ts, "filterUnits", filter->filterUnits()); writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits()); ts << "\n"; - if (OwnPtr<SVGFilterBuilder> builder = filter->buildPrimitives()) { + if (RefPtr<SVGFilterBuilder> builder = filter->buildPrimitives()) { if (FilterEffect* lastEffect = builder->lastEffect()) lastEffect->externalRepresentation(ts, indent + 1); } diff --git a/WebCore/rendering/SVGRootInlineBox.cpp b/WebCore/rendering/SVGRootInlineBox.cpp index 6b750be..8fd71ea 100644 --- a/WebCore/rendering/SVGRootInlineBox.cpp +++ b/WebCore/rendering/SVGRootInlineBox.cpp @@ -22,643 +22,44 @@ */ #include "config.h" - -#if ENABLE(SVG) #include "SVGRootInlineBox.h" +#if ENABLE(SVG) #include "GraphicsContext.h" #include "RenderBlock.h" #include "RenderSVGResourceFilter.h" -#include "RenderSVGRoot.h" #include "SVGInlineFlowBox.h" #include "SVGInlineTextBox.h" #include "SVGRenderSupport.h" #include "SVGTextLayoutUtilities.h" #include "SVGTextPositioningElement.h" -// Text chunk creation is complex and the whole process -// can easily be traced by setting this variable > 0. -#define DEBUG_CHUNK_BUILDING 0 +// Text chunk part propagation can be traced by setting this variable > 0. +#define DEBUG_CHUNK_PART_PROPAGATION 0 namespace WebCore { -static inline void startTextChunk(SVGTextChunkLayoutInfo& info) +void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int, int) { - info.chunk.boxes.clear(); - info.chunk.boxes.append(SVGInlineBoxCharacterRange()); - - info.chunk.start = info.it; - info.assignChunkProperties = true; -} - -static inline void closeTextChunk(SVGTextChunkLayoutInfo& info) -{ - ASSERT(!info.chunk.boxes.last().isOpen()); - ASSERT(info.chunk.boxes.last().isClosed()); - - info.chunk.end = info.it; - ASSERT(info.chunk.end >= info.chunk.start); - - info.svgTextChunks.append(info.chunk); -} - -RenderSVGRoot* findSVGRootObject(RenderObject* start) -{ - // Find associated root inline box. - while (start && !start->isSVGRoot()) - start = start->parent(); - ASSERT(start); - return toRenderSVGRoot(start); -} - -// Helper class for paint() -struct SVGRootInlineBoxPaintWalker { - SVGRootInlineBoxPaintWalker(SVGRootInlineBox* rootBox, RenderObject::PaintInfo paintInfo, int tx, int ty) - : m_rootBox(rootBox) - , m_chunkStarted(false) - , m_paintInfo(paintInfo) - , m_savedInfo(paintInfo) - , m_boundingBox(tx + rootBox->x(), ty + rootBox->y(), rootBox->width(), rootBox->height()) - , m_filter(0) - , m_fillPaintingResource(0) - , m_strokePaintingResource(0) - , m_fillPaintingResourceObject(0) - , m_strokePaintingResourceObject(0) - , m_tx(tx) - , m_ty(ty) - { - } - - ~SVGRootInlineBoxPaintWalker() - { - ASSERT(!m_filter); - ASSERT(!m_fillPaintingResource); - ASSERT(!m_fillPaintingResourceObject); - ASSERT(!m_strokePaintingResource); - ASSERT(!m_strokePaintingResourceObject); - ASSERT(!m_chunkStarted); - } - - bool mayHaveSelection(SVGInlineTextBox* box) const - { - int selectionStart = 0, selectionEnd = 0; - box->selectionStartEnd(selectionStart, selectionEnd); - return selectionStart < selectionEnd; - } - - void teardownFillPaintServer() - { - if (!m_fillPaintingResource) - return; - - m_fillPaintingResource->postApplyResource(m_fillPaintingResourceObject, m_paintInfo.context, ApplyToFillMode | ApplyToTextMode); - m_fillPaintingResource = 0; - m_fillPaintingResourceObject = 0; - } - - void teardownStrokePaintServer() - { - if (!m_strokePaintingResource) - return; - - m_strokePaintingResource->postApplyResource(m_strokePaintingResourceObject, m_paintInfo.context, ApplyToStrokeMode | ApplyToTextMode); - m_strokePaintingResource = 0; - m_strokePaintingResourceObject = 0; - } - - void chunkStartCallback(InlineBox* box) - { - ASSERT(!m_chunkStarted); - m_chunkStarted = true; - - InlineFlowBox* flowBox = box->parent(); - - // Initialize text rendering - RenderObject* object = flowBox->renderer(); - ASSERT(object); - - m_savedInfo = m_paintInfo; - m_paintInfo.context->save(); - - // FIXME: Why is this done here instead of in RenderSVGText? - if (!flowBox->isRootInlineBox()) - SVGRenderBase::prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter); - } - - void chunkEndCallback(InlineBox* box) - { - ASSERT(m_chunkStarted); - m_chunkStarted = false; - - InlineFlowBox* flowBox = box->parent(); - - RenderObject* object = flowBox->renderer(); - ASSERT(object); - - // Clean up last used paint server - teardownFillPaintServer(); - teardownStrokePaintServer(); - - // Finalize text rendering - if (!flowBox->isRootInlineBox()) { - SVGRenderBase::finishRenderSVGContent(object, m_paintInfo, m_filter, m_savedInfo.context); - m_filter = 0; - } - - // Restore context & repaint rect - m_paintInfo.context->restore(); - m_paintInfo.rect = m_savedInfo.rect; - } - - bool setupBackground(SVGInlineTextBox*) - { - m_textPaintInfo.subphase = SVGTextPaintSubphaseBackground; - return true; - } - - bool setupFill(SVGInlineTextBox* box) - { - InlineFlowBox* flowBox = box->parent(); - - // Setup fill paint server - RenderObject* object = flowBox->renderer(); - ASSERT(object); - - ASSERT(!m_strokePaintingResource); - teardownFillPaintServer(); - - m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphFill; - m_fillPaintingResource = RenderSVGResource::fillPaintingResource(object, object->style()); - if (m_fillPaintingResource) { - m_fillPaintingResource->applyResource(object, object->style(), m_paintInfo.context, ApplyToFillMode | ApplyToTextMode); - m_fillPaintingResourceObject = object; - return true; - } - - return false; - } - - bool setupFillSelection(SVGInlineTextBox* 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_strokePaintingResource); - teardownFillPaintServer(); - - if (!mayHaveSelection(box)) - return false; - - m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphFillSelection; - m_fillPaintingResource = RenderSVGResource::fillPaintingResource(object, style); - if (m_fillPaintingResource) { - m_fillPaintingResource->applyResource(object, style, m_paintInfo.context, ApplyToFillMode | ApplyToTextMode); - m_fillPaintingResourceObject = object; - return true; - } - - return false; - } - - bool setupStroke(SVGInlineTextBox* box) - { - InlineFlowBox* flowBox = box->parent(); - - // Setup stroke paint server - RenderObject* object = flowBox->renderer(); - ASSERT(object); - - // If we're both stroked & filled, teardown fill paint server before stroking. - teardownFillPaintServer(); - teardownStrokePaintServer(); - - m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphStroke; - m_strokePaintingResource = RenderSVGResource::strokePaintingResource(object, object->style()); - if (m_strokePaintingResource) { - m_strokePaintingResource->applyResource(object, object->style(), m_paintInfo.context, ApplyToStrokeMode | ApplyToTextMode); - m_strokePaintingResourceObject = object; - return true; - } - - return false; - } - - bool setupStrokeSelection(SVGInlineTextBox* 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_strokePaintingResource = RenderSVGResource::strokePaintingResource(object, style); - if (m_strokePaintingResource) { - m_strokePaintingResource->applyResource(object, style, m_paintInfo.context, ApplyToStrokeMode | ApplyToTextMode); - m_strokePaintingResourceObject = object; - return true; - } - - return false; - } - - bool setupForeground(SVGInlineTextBox*) - { - teardownFillPaintServer(); - teardownStrokePaintServer(); - - m_textPaintInfo.subphase = SVGTextPaintSubphaseForeground; - - return true; - } - - RenderSVGResource* activePaintingResource() const - { - switch (m_textPaintInfo.subphase) { - case SVGTextPaintSubphaseGlyphFill: - case SVGTextPaintSubphaseGlyphFillSelection: - ASSERT(m_fillPaintingResource); - return m_fillPaintingResource; - case SVGTextPaintSubphaseGlyphStroke: - case SVGTextPaintSubphaseGlyphStrokeSelection: - ASSERT(m_strokePaintingResource); - return m_strokePaintingResource; - 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) - { - if (setupBackground(textBox)) - paintChunk(textBox, startOffset, chunkCtm, start, end); - - if (setupFill(textBox)) - paintChunk(textBox, startOffset, chunkCtm, start, end); - - if (setupFillSelection(textBox)) - paintChunk(textBox, startOffset, chunkCtm, start, end); + ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); + ASSERT(!paintInfo.context->paintingDisabled()); - if (setupStroke(textBox)) - paintChunk(textBox, startOffset, chunkCtm, start, end); + RenderObject* boxRenderer = renderer(); + ASSERT(boxRenderer); - if (setupStrokeSelection(textBox)) - paintChunk(textBox, startOffset, chunkCtm, start, end); + RenderObject::PaintInfo childPaintInfo(paintInfo); + childPaintInfo.context->save(); - if (setupForeground(textBox)) - paintChunk(textBox, startOffset, chunkCtm, start, end); - } - - void paintChunk(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm, - const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) - { - RenderText* text = textBox->textRenderer(); - ASSERT(text); - - RenderStyle* styleToUse = text->style(textBox->isFirstLineStyle()); - ASSERT(styleToUse); - - startOffset += textBox->start(); - - int textDecorations = styleToUse->textDecorationsInEffect(); - - int textWidth = 0; - IntPoint decorationOrigin; - SVGTextDecorationInfo info; - - if (!chunkCtm.isIdentity()) - m_paintInfo.context->concatCTM(chunkCtm); - - for (Vector<SVGChar>::iterator it = start; it != end; ++it) { - if (it->isHidden()) - continue; - - // Determine how many characters - starting from the current - can be drawn at once. - Vector<SVGChar>::iterator itSearch = it + 1; - while (itSearch != end) { - if (itSearch->drawnSeperated || itSearch->isHidden()) - break; - - itSearch++; - } - - const UChar* stringStart = text->characters() + startOffset + (it - start); - unsigned int stringLength = itSearch - it; - - // Paint decorations, that have to be drawn before the text gets drawn - if (textDecorations != TDNONE && m_paintInfo.phase != PaintPhaseSelection) { - textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x)); - decorationOrigin = IntPoint((int) (*it).x, (int) (*it).y - styleToUse->font().ascent()); - info = m_rootBox->retrievePaintServersForTextDecoration(text); - } - - if (textDecorations & UNDERLINE && textWidth != 0.0f) - textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); - - if (textDecorations & OVERLINE && textWidth != 0.0f) - textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); - - // Paint text - m_textPaintInfo.activePaintingResource = activePaintingResource(); - 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) - textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); - - // Skip processed characters - it = itSearch - 1; - } - - if (!chunkCtm.isIdentity()) - m_paintInfo.context->concatCTM(chunkCtm.inverse()); - } - -private: - SVGRootInlineBox* m_rootBox; - bool m_chunkStarted : 1; - - RenderObject::PaintInfo m_paintInfo; - RenderObject::PaintInfo m_savedInfo; - - FloatRect m_boundingBox; - RenderSVGResourceFilter* m_filter; - - RenderSVGResource* m_fillPaintingResource; - RenderSVGResource* m_strokePaintingResource; - - RenderObject* m_fillPaintingResourceObject; - RenderObject* m_strokePaintingResourceObject; - - int m_tx; - int m_ty; - - SVGTextPaintInfo m_textPaintInfo; -}; - -void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) -{ - if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground) - return; - - RenderObject::PaintInfo savedInfo(paintInfo); - paintInfo.context->save(); + FloatRect repaintRect = boxRenderer->repaintRectInLocalCoordinates(); RenderSVGResourceFilter* filter = 0; - FloatRect boundingBox(tx + x(), ty + y(), width(), height()); - - // Initialize text rendering - if (SVGRenderBase::prepareToRenderSVGContent(renderer(), paintInfo, boundingBox, filter)) { - // Render text, chunk-by-chunk - SVGRootInlineBoxPaintWalker walkerCallback(this, paintInfo, tx, ty); - SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback, - &SVGRootInlineBoxPaintWalker::chunkPortionCallback, - &SVGRootInlineBoxPaintWalker::chunkStartCallback, - &SVGRootInlineBoxPaintWalker::chunkEndCallback); - - walkTextChunks(&walker); - } - - // Finalize text rendering - SVGRenderBase::finishRenderSVGContent(renderer(), paintInfo, filter, savedInfo.context); - paintInfo.context->restore(); -} - -int SVGRootInlineBox::placeBoxesHorizontally(int, int& leftPosition, int& rightPosition, bool&, GlyphOverflowAndFallbackFontsMap&) -{ - // Remove any offsets caused by RTL text layout - leftPosition = 0; - rightPosition = 0; - return 0; -} - -int SVGRootInlineBox::verticallyAlignBoxes(int, GlyphOverflowAndFallbackFontsMap&) -{ - // height is set by layoutInlineBoxes. - return height(); -} - -static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly) -{ - float length = 0.0f; - Vector<SVGChar>::iterator charIt = chunk.start; - - Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin(); - Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end(); - - for (; it != end; ++it) { - SVGInlineBoxCharacterRange& range = *it; - - SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box); - RenderStyle* style = box->renderer()->style(); - - for (int i = range.startOffset; i < range.endOffset; ++i) { - ASSERT(charIt <= chunk.end); - - // Determine how many characters - starting from the current - can be measured at once. - // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width - // of a string is not the sum of the boundaries of all contained glyphs. - Vector<SVGChar>::iterator itSearch = charIt + 1; - Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i; - while (itSearch != endSearch) { - // No need to check for 'isHidden()' here as this function is not called for text paths. - if (itSearch->drawnSeperated) - break; - - itSearch++; - } - - unsigned int positionOffset = itSearch - charIt; - - // Calculate width/height of subrange - SVGInlineBoxCharacterRange subRange; - subRange.box = range.box; - subRange.startOffset = i; - subRange.endOffset = i + positionOffset; - - if (calcWidthOnly) - length += cummulatedWidthOfInlineBoxCharacterRange(subRange); - else - length += cummulatedHeightOfInlineBoxCharacterRange(subRange); - - // Calculate gap between the previous & current range - // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account - // so add "40" as width, and analogous for B & C, add "20" as width. - if (itSearch > chunk.start && itSearch < chunk.end) { - SVGChar& lastCharacter = *(itSearch - 1); - SVGChar& currentCharacter = *itSearch; - - int offset = box->direction() == RTL ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1; - - // FIXME: does this need to change to handle multichar glyphs? - int charsConsumed = 1; - String glyphName; - if (calcWidthOnly) { - float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName); - length += currentCharacter.x - lastCharacter.x - lastGlyphWidth; - } else { - float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0); - length += currentCharacter.y - lastCharacter.y - lastGlyphHeight; - } - } - - // Advance processed characters - i += positionOffset - 1; - charIt = itSearch; - } - } - - ASSERT(charIt == chunk.end); - return length; -} - -static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk) -{ - return cummulatedWidthOrHeightOfTextChunk(chunk, true); -} - -static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk) -{ - return cummulatedWidthOrHeightOfTextChunk(chunk, false); -} - -static float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor) -{ - float shift = 0.0f; - - if (chunk.isVerticalText) - shift = cummulatedHeightOfTextChunk(chunk); - else - shift = cummulatedWidthOfTextChunk(chunk); - - if (anchor == TA_MIDDLE) - shift *= -0.5f; - else - shift *= -1.0f; - - return shift; -} - -static void applyTextAnchorToTextChunk(SVGTextChunk& chunk) -{ - // This method is not called for chunks containing chars aligned on a path. - // -> all characters are visible, no need to check for "isHidden()" anywhere. - - if (chunk.anchor == TA_START) - return; - - float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor); - - // Apply correction to chunk - Vector<SVGChar>::iterator chunkIt = chunk.start; - for (; chunkIt != chunk.end; ++chunkIt) { - SVGChar& curChar = *chunkIt; - - if (chunk.isVerticalText) - curChar.y += shift; - else - curChar.x += shift; - } - - // Move inline boxes - Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); - Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); - - for (; boxIt != boxEnd; ++boxIt) { - SVGInlineBoxCharacterRange& range = *boxIt; - - InlineBox* curBox = range.box; - ASSERT(curBox->isInlineTextBox()); - - // Move target box - if (chunk.isVerticalText) - curBox->setY(curBox->y() + static_cast<int>(shift)); - else - curBox->setX(curBox->x() + static_cast<int>(shift)); - } -} - -static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength) -{ - if (chunk.textLength <= 0.0f) - return 0.0f; - - float computedWidth = cummulatedWidthOfTextChunk(chunk); - float computedHeight = cummulatedHeightOfTextChunk(chunk); - - if ((computedWidth <= 0.0f && !chunk.isVerticalText) - || (computedHeight <= 0.0f && chunk.isVerticalText)) - return 0.0f; - - if (chunk.isVerticalText) - computedLength = computedHeight; - else - computedLength = computedWidth; - - if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { - if (chunk.isVerticalText) - chunk.ctm.scaleNonUniform(1.0f, chunk.textLength / computedLength); - else - chunk.ctm.scaleNonUniform(chunk.textLength / computedLength, 1.0f); - - return 0.0f; + if (SVGRenderBase::prepareToRenderSVGContent(boxRenderer, childPaintInfo, repaintRect, filter)) { + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->paint(childPaintInfo, 0, 0); } - return (chunk.textLength - computedLength) / float(chunk.end - chunk.start); -} - -static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk) -{ - // This method is not called for chunks containing chars aligned on a path. - // -> all characters are visible, no need to check for "isHidden()" anywhere. - - // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm - float computedLength = 0.0f; - float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength); - - if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { - SVGChar& firstChar = *(chunk.start); - - // Assure we apply the chunk scaling in the right origin - AffineTransform newChunkCtm(chunk.ctm); - newChunkCtm.translateRight(firstChar.x, firstChar.y); - newChunkCtm.translate(-firstChar.x, -firstChar.y); - - chunk.ctm = newChunkCtm; - } - - // Apply correction to chunk - if (spacingToApply != 0.0f) { - Vector<SVGChar>::iterator chunkIt = chunk.start; - for (; chunkIt != chunk.end; ++chunkIt) { - SVGChar& curChar = *chunkIt; - curChar.drawnSeperated = true; - - if (chunk.isVerticalText) - curChar.y += (chunkIt - chunk.start) * spacingToApply; - else - curChar.x += (chunkIt - chunk.start) * spacingToApply; - } - } + SVGRenderBase::finishRenderSVGContent(boxRenderer, childPaintInfo, filter, paintInfo.context); + childPaintInfo.context->restore(); } void SVGRootInlineBox::computePerCharacterLayoutInformation() @@ -668,26 +69,29 @@ void SVGRootInlineBox::computePerCharacterLayoutInformation() m_svgTextChunks.clear(); // Build layout information for all contained render objects - SVGCharacterLayoutInfo info(m_svgChars); - buildLayoutInformation(this, info); + SVGCharacterLayoutInfo charInfo; + buildLayoutInformation(this, charInfo); + m_svgChars = charInfo.svgChars; // Now all layout information are available for every character // contained in any of our child inline/flow boxes. Build list // of text chunks now, to be able to apply text-anchor shifts. - buildTextChunks(m_svgChars, m_svgTextChunks, this); + SVGTextChunkLayoutInfo chunkInfo; + chunkInfo.buildTextChunks(m_svgChars.begin(), m_svgChars.end(), this); // Layout all text chunks // text-anchor needs to be applied to individual chunks. - layoutTextChunks(); + chunkInfo.layoutTextChunks(); + m_svgTextChunks = chunkInfo.textChunks(); - // Finally the top left position of our box is known. - // Propogate this knownledge to our RenderSVGText parent. - FloatPoint topLeft = topLeftPositionOfCharacterRange(m_svgChars.begin(), m_svgChars.end()); - block()->setLocation((int) floorf(topLeft.x()), (int) floorf(topLeft.y())); + // Propagate text chunk part information to all SVGInlineTextBoxes, see SVGTextChunkLayoutInfo.h for details + propagateTextChunkPartInformation(); - // Layout all InlineText/Flow boxes - // BEWARE: This requires the root top/left position to be set correctly before! - layoutInlineBoxes(); + // Layout all child boxes. + layoutChildBoxes(this); + + // Resize our root box and our RenderSVGText parent block + layoutRootBox(); } void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info) @@ -711,8 +115,9 @@ void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacter ASSERT(curr->isInlineFlowBox()); InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); + // Skip generated content. if (!flowBox->renderer()->node()) - continue; // Skip generated content. + continue; bool isAnchor = flowBox->renderer()->node()->hasTagName(SVGNames::aTag); bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag); @@ -740,13 +145,14 @@ void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacter // Initialize sub-layout. We need to create text chunks from the textPath // children using our standard layout code, to be able to measure the // text length using our normal methods and not textPath specific hacks. - Vector<SVGChar> tempChars; Vector<SVGTextChunk> tempChunks; - SVGCharacterLayoutInfo tempInfo(tempChars); - buildLayoutInformation(flowBox, tempInfo); + SVGCharacterLayoutInfo tempCharInfo; + buildLayoutInformation(flowBox, tempCharInfo); - buildTextChunks(tempChars, tempChunks, flowBox); + SVGTextChunkLayoutInfo tempChunkInfo; + tempChunkInfo.buildTextChunks(tempCharInfo.svgChars.begin(), tempCharInfo.svgChars.end(), flowBox); + tempChunks = tempChunkInfo.textChunks(); Vector<SVGTextChunk>::iterator it = tempChunks.begin(); Vector<SVGTextChunk>::iterator end = tempChunks.end(); @@ -786,467 +192,175 @@ void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacter } } -void SVGRootInlineBox::layoutInlineBoxes() +void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start) { - int lowX = INT_MAX; - int lowY = INT_MAX; - int highX = INT_MIN; - int highY = INT_MIN; - - // Layout all child boxes - Vector<SVGChar>::iterator it = m_svgChars.begin(); - layoutInlineBoxes(this, it, lowX, highX, lowY, highY); - ASSERT(it == m_svgChars.end()); -} - -void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& lowX, int& highX, int& lowY, int& highY) -{ - for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { - RenderStyle* style = curr->renderer()->style(); - if (curr->renderer()->isText()) { - SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(curr); - unsigned length = textBox->len(); - - SVGChar curChar = *it; - ASSERT(it != m_svgChars.end()); - - FloatRect stringRect; - for (unsigned i = 0; i < length; ++i) { - ASSERT(it != m_svgChars.end()); - - if (it->isHidden()) { - ++it; - continue; - } - - stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it)); - ++it; - } - - IntRect enclosedStringRect = enclosingIntRect(stringRect); - - int minX = enclosedStringRect.x(); - int maxX = minX + enclosedStringRect.width(); - - int minY = enclosedStringRect.y(); - int maxY = minY + enclosedStringRect.height(); - - curr->setX(minX - block()->x()); - curr->setWidth(enclosedStringRect.width()); - - curr->setY(minY - block()->y()); - textBox->setHeight(enclosedStringRect.height()); - - if (minX < lowX) - lowX = minX; - - if (maxX > highX) - highX = maxX; - - if (minY < lowY) - lowY = minY; - - if (maxY > highY) - highY = maxY; + for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { + if (child->renderer()->isText()) { + SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child); + IntRect boxRect = textBox->calculateBoundaries(); + textBox->setX(boxRect.x()); + textBox->setY(boxRect.y()); + textBox->setWidth(boxRect.width()); + textBox->setHeight(boxRect.height()); } else { - ASSERT(curr->isInlineFlowBox()); - - int minX = INT_MAX; - int minY = INT_MAX; - int maxX = INT_MIN; - int maxY = INT_MIN; - - InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); - - if (!flowBox->renderer()->node()) - continue; // Skip generated content. - - layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY); - - curr->setX(minX - block()->x()); - curr->setWidth(maxX - minX); - - curr->setY(minY - block()->y()); - static_cast<SVGInlineFlowBox*>(curr)->setHeight(maxY - minY); - - if (minX < lowX) - lowX = minX; - - if (maxX > highX) - highX = maxX; + ASSERT(child->isInlineFlowBox()); + + // Skip generated content. + if (!child->renderer()->node()) + continue; - if (minY < lowY) - lowY = minY; + SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child); + layoutChildBoxes(flowBox); - if (maxY > highY) - highY = maxY; + IntRect boxRect = flowBox->calculateBoundaries(); + flowBox->setX(boxRect.x()); + flowBox->setY(boxRect.y()); + flowBox->setWidth(boxRect.width()); + flowBox->setHeight(boxRect.height()); } } +} - if (start->isSVGRootInlineBox()) { - int top = lowY - block()->y(); - int bottom = highY - block()->y(); +void SVGRootInlineBox::layoutRootBox() +{ + RenderBlock* parentBlock = block(); + ASSERT(parentBlock); - start->setX(lowX - block()->x()); - start->setY(top); + IntRect childRect; + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + // Skip generated content. + if (!child->renderer()->node()) + continue; + childRect.unite(child->calculateBoundaries()); + } - start->setWidth(highX - lowX); - static_cast<SVGRootInlineBox*>(start)->setHeight(highY - lowY); + int xBlock = childRect.x(); + int yBlock = childRect.y(); + int widthBlock = childRect.width(); + int heightBlock = childRect.height(); - GlyphOverflowAndFallbackFontsMap textBoxDataMap; - start->computeVerticalOverflow(top, bottom, true, textBoxDataMap); - static_cast<SVGRootInlineBox*>(start)->setLineTopBottomPositions(top, bottom); - } -} + // Finally, assign the root block position, now that all content is laid out. + parentBlock->setLocation(xBlock, yBlock); + parentBlock->setWidth(widthBlock); + parentBlock->setHeight(heightBlock); -void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, Vector<SVGTextChunk>& svgTextChunks, InlineFlowBox* start) -{ - SVGTextChunkLayoutInfo info(svgTextChunks); - info.it = svgChars.begin(); - info.chunk.start = svgChars.begin(); - info.chunk.end = svgChars.begin(); + // Position all children relative to the parent block. + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { + // Skip generated content. + if (!child->renderer()->node()) + continue; + child->adjustPosition(-xBlock, -yBlock); + } - buildTextChunks(svgChars, start, info); - ASSERT(info.it == svgChars.end()); + // Position ourselves. + setX(0); + setY(0); + setWidth(widthBlock); + setHeight(heightBlock); + setBlockHeight(heightBlock); + setLineTopBottomPositions(0, heightBlock); } -void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* start, SVGTextChunkLayoutInfo& info) +void SVGRootInlineBox::propagateTextChunkPartInformation() { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> buildTextChunks(start=%p)\n", start); +#if DEBUG_CHUNK_PART_PROPAGATION > 0 + ListHashSet<SVGInlineTextBox*> boxes; #endif - for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer()->isText()) { - InlineTextBox* textBox = static_cast<InlineTextBox*>(curr); + // Loop through all text chunks + const Vector<SVGTextChunk>::const_iterator end = m_svgTextChunks.end(); + for (Vector<SVGTextChunk>::const_iterator it = m_svgTextChunks.begin(); it != end; ++it) { + const SVGTextChunk& chunk = *it; + int processedChunkCharacters = 0; - unsigned length = textBox->len(); - if (!length) - continue; + // Loop through all ranges contained in this chunk + const Vector<SVGInlineBoxCharacterRange>::const_iterator boxEnd = chunk.boxes.end(); + for (Vector<SVGInlineBoxCharacterRange>::const_iterator boxIt = chunk.boxes.begin(); boxIt != boxEnd; ++boxIt) { + const SVGInlineBoxCharacterRange& range = *boxIt; + ASSERT(range.box->isSVGInlineTextBox()); -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n", - textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath); -#endif + // Access style & font information of this text box + SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box); + rangeTextBox->setChunkTransformation(chunk.ctm); - RenderText* text = textBox->textRenderer(); + RenderText* text = rangeTextBox->textRenderer(); ASSERT(text); - ASSERT(text->node()); - SVGTextContentElement* textContent = 0; - Node* node = text->node()->parent(); - while (node && node->isSVGElement() && !textContent) { - if (static_cast<SVGElement*>(node)->isTextContent()) - textContent = static_cast<SVGTextContentElement*>(node); - else - node = node->parentNode(); - } - ASSERT(textContent); - - // Start new character range for the first chunk - bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end; - if (isFirstCharacter) { - ASSERT(info.chunk.boxes.isEmpty()); - info.chunk.boxes.append(SVGInlineBoxCharacterRange()); - } else - ASSERT(!info.chunk.boxes.isEmpty()); - - // Walk string to find out new chunk positions, if existent - for (unsigned i = 0; i < length; ++i) { - ASSERT(info.it != svgChars.end()); - - SVGInlineBoxCharacterRange& range = info.chunk.boxes.last(); - if (range.isOpen()) { - range.box = curr; - range.startOffset = i == 0 ? 0 : i - 1; - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset); -#endif - } + RenderStyle* style = text->style(); + ASSERT(style); - // If a new (or the first) chunk has been started, record it's text-anchor and writing mode. - if (info.assignChunkProperties) { - info.assignChunkProperties = false; - - info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); - info.chunk.isTextPath = info.handlingTextPath; - info.chunk.anchor = text->style()->svgStyle()->textAnchor(); - info.chunk.textLength = textContent->textLength().value(textContent); - info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor); -#endif + const Font& font = style->font(); + + // Figure out first and last character of this range in this chunk + int rangeLength = range.endOffset - range.startOffset; + Vector<SVGChar>::iterator itCharBegin = chunk.start + processedChunkCharacters; + Vector<SVGChar>::iterator itCharEnd = chunk.start + processedChunkCharacters + rangeLength; + ASSERT(itCharEnd <= chunk.end); + + // Loop through all characters in range + int processedRangeCharacters = 0; + for (Vector<SVGChar>::iterator itChar = itCharBegin; itChar != itCharEnd; ++itChar) { + if (itChar->isHidden()) { + ++processedRangeCharacters; + continue; } - if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) { - // Close mid chunk & character range - ASSERT(!range.isOpen()); - ASSERT(!range.isClosed()); + // Determine how many characters - starting from the current - can be drawn at once. + Vector<SVGChar>::iterator itSearch = itChar + 1; + while (itSearch != itCharEnd) { + if (itSearch->drawnSeperated || itSearch->isHidden()) + break; - range.endOffset = i; - closeTextChunk(info); - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset); -#endif - - // Prepare for next chunk, if we're not at the end - startTextChunk(info); - if (i + 1 == length) { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " | -> Record last chunk of inline text box!\n"); -#endif - - startTextChunk(info); - SVGInlineBoxCharacterRange& range = info.chunk.boxes.last(); - - info.assignChunkProperties = false; - info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); - info.chunk.isTextPath = info.handlingTextPath; - info.chunk.anchor = text->style()->svgStyle()->textAnchor(); - info.chunk.textLength = textContent->textLength().value(textContent); - info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); - - range.box = curr; - range.startOffset = i; - - ASSERT(!range.isOpen()); - ASSERT(!range.isClosed()); - } + ++itSearch; } - // This should only hold true for the first character of the first chunk - if (isFirstCharacter) - isFirstCharacter = false; - - ++info.it; - } - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Finished inline text box!\n"); -#endif - - SVGInlineBoxCharacterRange& range = info.chunk.boxes.last(); - if (!range.isOpen() && !range.isClosed()) { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length); -#endif + // Calculate text chunk part information for this chunk sub-range + const UChar* partStart = text->characters() + rangeTextBox->start() + range.startOffset + processedRangeCharacters; - // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk. - range.endOffset = length; + SVGTextChunkPart part; + part.firstCharacter = itChar; + part.length = itSearch - itChar; + part.width = font.floatWidth(svgTextRunForInlineTextBox(partStart, part.length, style, rangeTextBox)); + part.height = font.height(); + part.offset = range.startOffset + processedRangeCharacters; + rangeTextBox->addChunkPartInformation(part); + processedRangeCharacters += part.length; - if (info.it != svgChars.end()) { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Not at last character yet!\n"); -#endif - - // If we're not at the end of the last box to be processed, and if the next - // character starts a new chunk, then close the current chunk and start a new one. - if ((*info.it).newTextChunk) { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n"); -#endif - - closeTextChunk(info); - startTextChunk(info); - } else { - // Just start a new character range - info.chunk.boxes.append(SVGInlineBoxCharacterRange()); - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n"); -#endif - } - } else { -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Closing final chunk! Finished processing!\n"); -#endif - - // Close final chunk, once we're at the end of the last box - closeTextChunk(info); - } + // Skip processed characters + itChar = itSearch - 1; } - } else { - ASSERT(curr->isInlineFlowBox()); - InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); - - if (!flowBox->renderer()->node()) - continue; // Skip generated content. - - bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag); - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath); -#endif - if (isTextPath) - info.handlingTextPath = true; - - buildTextChunks(svgChars, flowBox, info); + ASSERT(processedRangeCharacters == rangeLength); + processedChunkCharacters += rangeLength; - if (isTextPath) - info.handlingTextPath = false; - } - } - -#if DEBUG_CHUNK_BUILDING > 1 - fprintf(stderr, " <- buildTextChunks(start=%p)\n", start); +#if DEBUG_CHUNK_PART_PROPAGATION > 0 + boxes.add(rangeTextBox); #endif -} - -void SVGRootInlineBox::layoutTextChunks() -{ - Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin(); - Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end(); - - for (; it != end; ++it) { - SVGTextChunk& chunk = *it; - -#if DEBUG_CHUNK_BUILDING > 0 - { - fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n", - (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText, - (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start)); - - Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); - Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); - - unsigned int i = 0; - for (; boxIt != boxEnd; ++boxIt) { - SVGInlineBoxCharacterRange& range = *boxIt; - ++i; - fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box); - } - } -#endif - - if (chunk.isTextPath) - continue; - - // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts. - applyTextLengthCorrectionToTextChunk(chunk); - - // text-anchor is already handled for textPath layouts. - applyTextAnchorToTextChunk(chunk); - } -} - -static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo& info, RenderObject* object) -{ - if (object->style()->svgStyle()->hasFill()) - info.fillServerMap.set(decoration, object); - - if (object->style()->svgStyle()->hasStroke()) - info.strokeServerMap.set(decoration, object); -} - -SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject* start) -{ - ASSERT(start); - - Vector<RenderObject*> parentChain; - while ((start = start->parent())) { - parentChain.prepend(start); - - // Stop at our direct <text> parent. - if (start->isSVGText()) - break; - } - - Vector<RenderObject*>::iterator it = parentChain.begin(); - Vector<RenderObject*>::iterator end = parentChain.end(); - - SVGTextDecorationInfo info; - - for (; it != end; ++it) { - RenderObject* object = *it; - ASSERT(object); - - RenderStyle* style = object->style(); - ASSERT(style); - - int decorations = style->textDecoration(); - if (decorations != NONE) { - if (decorations & OVERLINE) - addPaintServerToTextDecorationInfo(OVERLINE, info, object); - - if (decorations & UNDERLINE) - addPaintServerToTextDecorationInfo(UNDERLINE, info, object); - - if (decorations & LINE_THROUGH) - addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object); } } - return info; -} - -void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGInlineTextBox* textBox) -{ - ASSERT(walker); - - Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin(); - Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end(); - - for (; it != itEnd; ++it) { - SVGTextChunk& curChunk = *it; - - Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin(); - Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end(); - - InlineBox* lastNotifiedBox = 0; - InlineBox* prevBox = 0; - - unsigned int chunkOffset = 0; - bool startedFirstChunk = false; - - for (; boxIt != boxEnd; ++boxIt) { - SVGInlineBoxCharacterRange& range = *boxIt; - - ASSERT(range.box->isInlineTextBox()); - SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box); - - if (textBox && rangeTextBox != textBox) { - chunkOffset += range.endOffset - range.startOffset; - continue; - } - - // Eventually notify that we started a new chunk - if (!textBox && !startedFirstChunk) { - startedFirstChunk = true; - - lastNotifiedBox = range.box; - walker->start(range.box); - } else { - // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling) - if (prevBox && prevBox != range.box) { - lastNotifiedBox = range.box; - - walker->end(prevBox); - walker->start(lastNotifiedBox); - } +#if DEBUG_CHUNK_PART_PROPAGATION > 0 + { + fprintf(stderr, "Propagated text chunk part information:\n"); + + ListHashSet<SVGInlineTextBox*>::const_iterator it = boxes.begin(); + const ListHashSet<SVGInlineTextBox*>::const_iterator end = boxes.end(); + + for (; it != end; ++it) { + const SVGInlineTextBox* box = *it; + const Vector<SVGTextChunkPart>& parts = box->svgTextChunkParts(); + + fprintf(stderr, " Box %p contains %i text chunk parts:\n", box, static_cast<int>(parts.size())); + Vector<SVGTextChunkPart>::const_iterator partIt = parts.begin(); + const Vector<SVGTextChunkPart>::const_iterator partEnd = parts.end(); + for (; partIt != partEnd; ++partIt) { + const SVGTextChunkPart& part = *partIt; + fprintf(stderr, " -> firstCharacter x=%lf, y=%lf, offset=%i, length=%i, width=%lf, height=%lf, textRenderer=%p\n" + , part.firstCharacter->x, part.firstCharacter->y, part.offset, part.length, part.width, part.height, box->textRenderer()); } - - unsigned int length = range.endOffset - range.startOffset; - - Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset; - Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length; - ASSERT(itCharEnd <= curChunk.end); - - // Process this chunk portion - (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); - - chunkOffset += length; - - if (!textBox) - prevBox = range.box; } - - if (!textBox && startedFirstChunk) - walker->end(lastNotifiedBox); } +#endif } } // namespace WebCore diff --git a/WebCore/rendering/SVGRootInlineBox.h b/WebCore/rendering/SVGRootInlineBox.h index c3e1028..75e85fd 100644 --- a/WebCore/rendering/SVGRootInlineBox.h +++ b/WebCore/rendering/SVGRootInlineBox.h @@ -1,9 +1,8 @@ /* - * This file is part of the WebKit project. - * * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> * (C) 2006 Apple Computer Inc. * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * 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 @@ -34,12 +33,10 @@ namespace WebCore { -class InlineTextBox; -class RenderSVGRoot; class SVGInlineTextBox; -struct SVGLastGlyphInfo; -class SVGRootInlineBox : public RootInlineBox, protected SVGRenderBase { +class SVGRootInlineBox : public RootInlineBox + , protected SVGRenderBase { public: SVGRootInlineBox(RenderObject* obj) : RootInlineBox(obj) @@ -47,39 +44,27 @@ public: { } - virtual bool isSVGRootInlineBox() { return true; } + virtual bool isSVGRootInlineBox() const { return true; } virtual int virtualHeight() const { return m_height; } - void setHeight(int h) { m_height = h; } - - virtual void paint(RenderObject::PaintInfo&, int tx, int ty); + void setHeight(int height) { m_height = height; } - virtual int placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); - virtual int verticallyAlignBoxes(int heightOfBlock, GlyphOverflowAndFallbackFontsMap&); + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); - virtual void computePerCharacterLayoutInformation(); + void computePerCharacterLayoutInformation(); virtual FloatRect objectBoundingBox() const { return FloatRect(); } virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); } - // Used by SVGInlineTextBox + // Used by SVGRenderTreeAsText const Vector<SVGTextChunk>& svgTextChunks() const { return m_svgTextChunks; } - void walkTextChunks(SVGTextChunkWalkerBase*, const SVGInlineTextBox* textBox = 0); - private: - friend struct SVGRootInlineBoxPaintWalker; - - void layoutInlineBoxes(); - void layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& minX, int& maxX, int& minY, int& maxY); - + void propagateTextChunkPartInformation(); void buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo&); - void buildTextChunks(Vector<SVGChar>&, Vector<SVGTextChunk>&, InlineFlowBox* start); - void buildTextChunks(Vector<SVGChar>&, InlineFlowBox* start, SVGTextChunkLayoutInfo&); - void layoutTextChunks(); - - SVGTextDecorationInfo retrievePaintServersForTextDecoration(RenderObject* start); + void layoutRootBox(); + void layoutChildBoxes(InlineFlowBox* start); private: int m_height; @@ -87,9 +72,6 @@ private: Vector<SVGTextChunk> m_svgTextChunks; }; -// Shared with SVGRenderTreeAsText / SVGInlineTextBox -RenderSVGRoot* findSVGRootObject(RenderObject* start); - } // namespace WebCore #endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGTextChunkLayoutInfo.cpp b/WebCore/rendering/SVGTextChunkLayoutInfo.cpp new file mode 100644 index 0000000..a7a6d87 --- /dev/null +++ b/WebCore/rendering/SVGTextChunkLayoutInfo.cpp @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "SVGTextChunkLayoutInfo.h" + +#if ENABLE(SVG) +#include "InlineFlowBox.h" +#include "SVGInlineTextBox.h" +#include "SVGRenderStyle.h" + +// Text chunk creation is complex and the whole process +// can easily be traced by setting this variable > 0. +#define DEBUG_CHUNK_BUILDING 0 + +namespace WebCore { + +static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly) +{ + float length = 0.0f; + Vector<SVGChar>::iterator charIt = chunk.start; + + Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin(); + Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end(); + + for (; it != end; ++it) { + SVGInlineBoxCharacterRange& range = *it; + + SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box); + RenderStyle* style = box->renderer()->style(); + + for (int i = range.startOffset; i < range.endOffset; ++i) { + ASSERT(charIt <= chunk.end); + + // Determine how many characters - starting from the current - can be measured at once. + // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width + // of a string is not the sum of the boundaries of all contained glyphs. + Vector<SVGChar>::iterator itSearch = charIt + 1; + Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i; + while (itSearch != endSearch) { + // No need to check for 'isHidden()' here as this function is not called for text paths. + if (itSearch->drawnSeperated) + break; + + itSearch++; + } + + unsigned int positionOffset = itSearch - charIt; + + // Calculate width/height of subrange + SVGInlineBoxCharacterRange subRange; + subRange.box = range.box; + subRange.startOffset = i; + subRange.endOffset = i + positionOffset; + + if (calcWidthOnly) + length += cummulatedWidthOfInlineBoxCharacterRange(subRange); + else + length += cummulatedHeightOfInlineBoxCharacterRange(subRange); + + // Calculate gap between the previous & current range + // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account + // so add "40" as width, and analogous for B & C, add "20" as width. + if (itSearch > chunk.start && itSearch < chunk.end) { + SVGChar& lastCharacter = *(itSearch - 1); + SVGChar& currentCharacter = *itSearch; + + int charsConsumed = 0; + float glyphWidth = 0.0f; + float glyphHeight = 0.0f; + String glyphName; + String unicodeString; + box->measureCharacter(style, i + positionOffset - 1, charsConsumed, glyphName, unicodeString, glyphWidth, glyphHeight); + + if (calcWidthOnly) + length += currentCharacter.x - lastCharacter.x - glyphWidth; + else + length += currentCharacter.y - lastCharacter.y - glyphHeight; + } + + // Advance processed characters + i += positionOffset - 1; + charIt = itSearch; + } + } + + ASSERT(charIt == chunk.end); + return length; +} + +static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk) +{ + return cummulatedWidthOrHeightOfTextChunk(chunk, true); +} + +static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk) +{ + return cummulatedWidthOrHeightOfTextChunk(chunk, false); +} + +float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor) +{ + float shift = 0.0f; + + if (chunk.isVerticalText) + shift = cummulatedHeightOfTextChunk(chunk); + else + shift = cummulatedWidthOfTextChunk(chunk); + + if (anchor == TA_MIDDLE) + shift *= -0.5f; + else + shift *= -1.0f; + + return shift; +} + +static void applyTextAnchorToTextChunk(SVGTextChunk& chunk) +{ + // This method is not called for chunks containing chars aligned on a path. + // -> all characters are visible, no need to check for "isHidden()" anywhere. + + if (chunk.anchor == TA_START) + return; + + float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor); + + // Apply correction to chunk + Vector<SVGChar>::iterator chunkIt = chunk.start; + for (; chunkIt != chunk.end; ++chunkIt) { + SVGChar& curChar = *chunkIt; + + if (chunk.isVerticalText) + curChar.y += shift; + else + curChar.x += shift; + } + + // Move inline boxes + Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); + Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); + + for (; boxIt != boxEnd; ++boxIt) { + SVGInlineBoxCharacterRange& range = *boxIt; + + InlineBox* curBox = range.box; + ASSERT(curBox->isSVGInlineTextBox()); + + // Move target box + if (chunk.isVerticalText) + curBox->setY(curBox->y() + static_cast<int>(shift)); + else + curBox->setX(curBox->x() + static_cast<int>(shift)); + } +} + +float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength) +{ + if (chunk.textLength <= 0.0f) + return 0.0f; + + computedLength = 0.0f; + + float computedWidth = cummulatedWidthOfTextChunk(chunk); + float computedHeight = cummulatedHeightOfTextChunk(chunk); + if ((computedWidth <= 0.0f && !chunk.isVerticalText) + || (computedHeight <= 0.0f && chunk.isVerticalText)) + return 0.0f; + + computedLength = chunk.isVerticalText ? computedHeight : computedWidth; + if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { + if (chunk.isVerticalText) + chunk.ctm.scaleNonUniform(1.0f, chunk.textLength / computedLength); + else + chunk.ctm.scaleNonUniform(chunk.textLength / computedLength, 1.0f); + + return 0.0f; + } + + return (chunk.textLength - computedLength) / float(chunk.end - chunk.start); +} + +static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk) +{ + // This method is not called for chunks containing chars aligned on a path. + // -> all characters are visible, no need to check for "isHidden()" anywhere. + + // lengthAdjust="spacingAndGlyphs" is handled by setting a scale factor for the whole chunk + float textLength = 0.0f; + float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, textLength); + + if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { + SVGChar& firstChar = *(chunk.start); + + // Assure we apply the chunk scaling in the right origin + AffineTransform newChunkCtm(chunk.ctm); + newChunkCtm.translateRight(firstChar.x, firstChar.y); + newChunkCtm.translate(-firstChar.x, -firstChar.y); + + chunk.ctm = newChunkCtm; + } + + // Apply correction to chunk + if (spacingToApply != 0.0f) { + Vector<SVGChar>::iterator chunkIt = chunk.start; + for (; chunkIt != chunk.end; ++chunkIt) { + SVGChar& curChar = *chunkIt; + curChar.drawnSeperated = true; + + if (chunk.isVerticalText) + curChar.y += (chunkIt - chunk.start) * spacingToApply; + else + curChar.x += (chunkIt - chunk.start) * spacingToApply; + } + } +} + +void SVGTextChunkLayoutInfo::startTextChunk() +{ + m_chunk.boxes.clear(); + m_chunk.boxes.append(SVGInlineBoxCharacterRange()); + + m_chunk.start = m_charsIt; + m_assignChunkProperties = true; +} + +void SVGTextChunkLayoutInfo::closeTextChunk() +{ + ASSERT(!m_chunk.boxes.last().isOpen()); + ASSERT(m_chunk.boxes.last().isClosed()); + + m_chunk.end = m_charsIt; + ASSERT(m_chunk.end >= m_chunk.start); + + m_svgTextChunks.append(m_chunk); +} + +void SVGTextChunkLayoutInfo::buildTextChunks(Vector<SVGChar>::iterator begin, Vector<SVGChar>::iterator end, InlineFlowBox* start) +{ + m_charsBegin = begin; + m_charsEnd = end; + + m_charsIt = begin; + m_chunk = SVGTextChunk(begin); + + recursiveBuildTextChunks(start); + ASSERT(m_charsIt == m_charsEnd); +} + +void SVGTextChunkLayoutInfo::recursiveBuildTextChunks(InlineFlowBox* start) +{ +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> buildTextChunks(start=%p)\n", start); +#endif + + for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isText()) { + InlineTextBox* textBox = static_cast<InlineTextBox*>(curr); + + unsigned length = textBox->len(); + ASSERT(length > 0); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n", + textBox, length, textBox->start(), textBox->end(), (int) m_handlingTextPath); +#endif + + RenderText* text = textBox->textRenderer(); + ASSERT(text); + ASSERT(text->node()); + + SVGTextContentElement* textContent = 0; + Node* node = text->node()->parent(); + while (node && node->isSVGElement() && !textContent) { + if (static_cast<SVGElement*>(node)->isTextContent()) + textContent = static_cast<SVGTextContentElement*>(node); + else + node = node->parentNode(); + } + ASSERT(textContent); + + // Start new character range for the first chunk + bool isFirstCharacter = m_svgTextChunks.isEmpty() && m_chunk.start == m_charsIt && m_chunk.start == m_chunk.end; + if (isFirstCharacter) { + ASSERT(m_chunk.boxes.isEmpty()); + m_chunk.boxes.append(SVGInlineBoxCharacterRange()); + } else + ASSERT(!m_chunk.boxes.isEmpty()); + + // Walk string to find out new chunk positions, if existent + for (unsigned i = 0; i < length; ++i) { + ASSERT(m_charsIt != m_charsEnd); + + SVGInlineBoxCharacterRange& range = m_chunk.boxes.last(); + if (range.isOpen()) { + range.box = curr; + range.startOffset = !i ? 0 : i - 1; + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset); +#endif + } + + // If a new (or the first) chunk has been started, record it's text-anchor and writing mode. + if (m_assignChunkProperties) { + m_assignChunkProperties = false; + + m_chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); + m_chunk.isTextPath = m_handlingTextPath; + m_chunk.anchor = text->style()->svgStyle()->textAnchor(); + m_chunk.textLength = textContent->textLength().value(textContent); + m_chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", m_chunk.isVerticalText, m_chunk.anchor); +#endif + } + + if (i > 0 && !isFirstCharacter && m_charsIt->newTextChunk) { + // Close mid chunk & character range + ASSERT(!range.isOpen()); + ASSERT(!range.isClosed()); + + range.endOffset = i; + closeTextChunk(); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset); +#endif + + // Prepare for next chunk, if we're not at the end + startTextChunk(); + if (i + 1 == length) { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " | -> Record last chunk of inline text box!\n"); +#endif + + startTextChunk(); + SVGInlineBoxCharacterRange& range = m_chunk.boxes.last(); + + m_assignChunkProperties = false; + m_chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); + m_chunk.isTextPath = m_handlingTextPath; + m_chunk.anchor = text->style()->svgStyle()->textAnchor(); + m_chunk.textLength = textContent->textLength().value(textContent); + m_chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); + + range.box = curr; + range.startOffset = i; + + ASSERT(!range.isOpen()); + ASSERT(!range.isClosed()); + } + } + + // This should only hold true for the first character of the first chunk + if (isFirstCharacter) + isFirstCharacter = false; + + ++m_charsIt; + } + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Finished inline text box!\n"); +#endif + + SVGInlineBoxCharacterRange& range = m_chunk.boxes.last(); + if (!range.isOpen() && !range.isClosed()) { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length); +#endif + + // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk. + range.endOffset = length; + + if (m_charsIt != m_charsEnd) { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Not at last character yet!\n"); +#endif + + // If we're not at the end of the last box to be processed, and if the next + // character starts a new chunk, then close the current chunk and start a new one. + if (m_charsIt->newTextChunk) { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n"); +#endif + + closeTextChunk(); + startTextChunk(); + } else { + // Just start a new character range + m_chunk.boxes.append(SVGInlineBoxCharacterRange()); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n"); +#endif + } + } else { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Closing final chunk! Finished processing!\n"); +#endif + + // Close final chunk, once we're at the end of the last box + closeTextChunk(); + } + } + } else { + ASSERT(curr->isInlineFlowBox()); + InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); + + // Skip generated content. + if (!flowBox->renderer()->node()) + continue; + + bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath); +#endif + + if (isTextPath) + m_handlingTextPath = true; + + recursiveBuildTextChunks(flowBox); + + if (isTextPath) + m_handlingTextPath = false; + } + } + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " <- buildTextChunks(start=%p)\n", start); +#endif +} + +void SVGTextChunkLayoutInfo::layoutTextChunks() +{ + Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin(); + Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end(); + + for (; it != end; ++it) { + SVGTextChunk& chunk = *it; + +#if DEBUG_CHUNK_BUILDING > 0 + { + fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n", + (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText, + (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start)); + + Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); + Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); + + unsigned int i = 0; + for (; boxIt != boxEnd; ++boxIt) { + SVGInlineBoxCharacterRange& range = *boxIt; + ++i; + fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box); + } + } +#endif + + if (chunk.isTextPath) + continue; + + // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts. + applyTextLengthCorrectionToTextChunk(chunk); + + // text-anchor is already handled for textPath layouts. + applyTextAnchorToTextChunk(chunk); + } +} + +} // namespace WebCore + +#endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGTextChunkLayoutInfo.h b/WebCore/rendering/SVGTextChunkLayoutInfo.h index 524c983..e06dd1a 100644 --- a/WebCore/rendering/SVGTextChunkLayoutInfo.h +++ b/WebCore/rendering/SVGTextChunkLayoutInfo.h @@ -24,6 +24,8 @@ #if ENABLE(SVG) #include "AffineTransform.h" +#include "SVGCharacterData.h" +#include "SVGRenderStyle.h" #include "SVGTextContentElement.h" #include <wtf/Assertions.h> @@ -32,8 +34,75 @@ namespace WebCore { class InlineBox; +class InlineFlowBox; class SVGInlineTextBox; +// A SVGTextChunk directly corresponds to the definition of a "text chunk" per SVG 1.1 specification +// For example, each absolute positioned character starts a text chunk (much more to respect, see spec). +// Each SVGTextChunk contains a Vector of SVGInlineBoxCharacterRange, describing how many boxes are spanned +// by this chunk. Following two examples should clarify the code a bit: +// +// 1. <text x="10 20 30">ABC</text> - one InlineTextBox is created, three SVGTextChunks each with one SVGInlineBoxCharaterRange +// [SVGTextChunk 1] +// [SVGInlineBoxCharacterRange 1, startOffset=0, endOffset=1, box=0x1] +// [SVGTextChunk 2] +// [SVGInlineBoxCharacterRange 1, startOffset=1, endOffset=2, box=0x1] +// [SVGTextChunk 3] +// [SVGInlineBoxCharacterRange 1, startOffset=2, endOffset=3, box=0x1] +// +// 2. <text x="10">A<tspan>B</tspan>C</text> - three InlineTextBoxs are created, one SVGTextChunk, with three SVGInlineBoxCharacterRanges +// [SVGTextChunk 1] +// [SVGInlineBoxCharacterRange 1, startOffset=0, endOffset=1, box=0x1] +// [SVGInlineBoxCharacterRange 2, startOffset=0, endOffset=1, box=0x2] +// [SVGInlineBoxCharacterRange 3, startOffset=0, endOffset=1, box=0x3] +// +// High level overview of the SVG text layout code: +// Step #1) - Build Vector of SVGChar objects starting from root <text> diving into children +// Step #2) - Build Vector of SVGTextChunk objects, containing offsets into the InlineTextBoxes and SVGChar vectors +// Step #3) - Apply chunk post processing (text-anchor / textLength support, which operate on text chunks!) +// Step #4) - Propagate information, how many chunk "parts" are associated with each SVGInlineTextBox (see below) +// Step #5) - Layout all InlineBoxes, only by measuring their context rect (x/y/width/height defined through SVGChars and transformations) +// Step #6) - Layout SVGRootInlineBox, it's parent RenderSVGText block and fixup child positions, to be relative to the root box +// +// When painting a range of characters, we have to determine how many can be drawn in a row. Each absolute postioned +// character is drawn individually. After step #2) we know all text chunks, and how they span across the SVGInlineTextBoxes. +// In step #4) we build a list of text chunk "parts" and store it in each SVGInlineTextBox. A chunk "part" is a part of a +// text chunk that lives in a SVGInlineTextBox (consists of a length, width, height and a monotonic offset from the chunk begin) +// The SVGTextChunkPart object describes this information. +// When painting we can follow the regular InlineBox flow, we start painting the SVGRootInlineBox, which just asks its children +// to paint. They can paint on their own because all position information are known. Previously we used to draw _all_ characters +// from the SVGRootInlineBox, which violates the whole concept of the multiple InlineBoxes, and made text selection very hard to +// implement. + +struct SVGTextChunkPart { + SVGTextChunkPart() + : offset(-1) + , length(-1) + , width(0) + , height(0) + { + } + + bool isValid() const + { + return offset != -1 + && length != -1 + && width + && height; + } + + // First character of this text chunk part, defining the origin to be drawn + Vector<SVGChar>::const_iterator firstCharacter; + + // Start offset in textRenderer()->characters() buffer. + int offset; + + // length/width/height of chunk part + int length; + float width; + float height; +}; + struct SVGInlineBoxCharacterRange { SVGInlineBoxCharacterRange() : startOffset(INT_MIN) @@ -57,16 +126,16 @@ struct SVGChar; typedef SVGTextContentElement::SVGLengthAdjustType ELengthAdjust; struct SVGTextChunk { - SVGTextChunk() + SVGTextChunk(Vector<SVGChar>::iterator it) : anchor(TA_START) , textLength(0.0f) , lengthAdjust(SVGTextContentElement::LENGTHADJUST_SPACING) - , ctm() , isVerticalText(false) , isTextPath(false) - , start(0) - , end(0) - { } + , start(it) + , end(it) + { + } // text-anchor support ETextAnchor anchor; @@ -87,91 +156,42 @@ struct SVGTextChunk { Vector<SVGInlineBoxCharacterRange> boxes; }; -struct SVGTextChunkWalkerBase { - virtual ~SVGTextChunkWalkerBase() { } - - 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; -}; - -template<typename CallbackClass> -struct SVGTextChunkWalker : public SVGTextChunkWalkerBase { -public: - typedef void (CallbackClass::*SVGTextChunkWalkerCallback)(SVGInlineTextBox* textBox, - int startOffset, - const AffineTransform& chunkCtm, - const Vector<SVGChar>::iterator& start, - const Vector<SVGChar>::iterator& end); - - // These callbacks are only used for painting! - typedef void (CallbackClass::*SVGTextChunkStartCallback)(InlineBox* box); - typedef void (CallbackClass::*SVGTextChunkEndCallback)(InlineBox* box); - - SVGTextChunkWalker(CallbackClass* object, - SVGTextChunkWalkerCallback walker, - SVGTextChunkStartCallback start = 0, - SVGTextChunkEndCallback end = 0) - : m_object(object) - , m_walkerCallback(walker) - , m_startCallback(start) - , m_endCallback(end) +struct SVGTextChunkLayoutInfo { + SVGTextChunkLayoutInfo() + : m_assignChunkProperties(true) + , m_handlingTextPath(false) + , m_charsIt(0) + , m_charsBegin(0) + , m_charsEnd(0) + , m_chunk(0) { - ASSERT(object); - ASSERT(walker); } - 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); - } + const Vector<SVGTextChunk>& textChunks() const { return m_svgTextChunks; } - // Followings methods are only used for painting text chunks - virtual void start(InlineBox* box) - { - if (m_startCallback) - (*m_object.*m_startCallback)(box); - else - ASSERT_NOT_REACHED(); - } - - virtual void end(InlineBox* box) - { - if (m_endCallback) - (*m_object.*m_endCallback)(box); - else - ASSERT_NOT_REACHED(); - } + void buildTextChunks(Vector<SVGChar>::iterator charsBegin, Vector<SVGChar>::iterator charsEnd, InlineFlowBox* start); + void layoutTextChunks(); private: - CallbackClass* m_object; - SVGTextChunkWalkerCallback m_walkerCallback; - SVGTextChunkStartCallback m_startCallback; - SVGTextChunkEndCallback m_endCallback; -}; + void startTextChunk(); + void closeTextChunk(); + void recursiveBuildTextChunks(InlineFlowBox* start); -struct SVGTextChunkLayoutInfo { - SVGTextChunkLayoutInfo(Vector<SVGTextChunk>& textChunks) - : assignChunkProperties(true) - , handlingTextPath(false) - , svgTextChunks(textChunks) - , it(0) - { - } - - bool assignChunkProperties : 1; - bool handlingTextPath : 1; + bool m_assignChunkProperties : 1; + bool m_handlingTextPath : 1; - Vector<SVGTextChunk>& svgTextChunks; - Vector<SVGChar>::iterator it; + Vector<SVGChar>::iterator m_charsIt; + Vector<SVGChar>::iterator m_charsBegin; + Vector<SVGChar>::iterator m_charsEnd; - SVGTextChunk chunk; + Vector<SVGTextChunk> m_svgTextChunks; + SVGTextChunk m_chunk; }; +// Helper functions +float calculateTextAnchorShiftForTextChunk(SVGTextChunk&, ETextAnchor); +float calculateTextLengthCorrectionForTextChunk(SVGTextChunk&, ELengthAdjust, float& computedLength); + } // namespace WebCore #endif // ENABLE(SVG) diff --git a/WebCore/rendering/SVGTextLayoutUtilities.cpp b/WebCore/rendering/SVGTextLayoutUtilities.cpp index 39705e9..4366498 100644 --- a/WebCore/rendering/SVGTextLayoutUtilities.cpp +++ b/WebCore/rendering/SVGTextLayoutUtilities.cpp @@ -272,40 +272,45 @@ float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range { ASSERT(!range.isOpen()); ASSERT(range.isClosed()); - ASSERT(range.box->isInlineTextBox()); + ASSERT(range.box->isSVGInlineTextBox()); InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); RenderText* text = textBox->textRenderer(); RenderStyle* style = text->style(); - - return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox, 0)); + return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox)); } float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range) { ASSERT(!range.isOpen()); ASSERT(range.isClosed()); - ASSERT(range.box->isInlineTextBox()); + ASSERT(range.box->isSVGInlineTextBox()); InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); - RenderText* text = textBox->textRenderer(); - const Font& font = text->style()->font(); - - return (range.endOffset - range.startOffset) * (font.ascent() + font.descent()); + return (range.endOffset - range.startOffset) * textBox->textRenderer()->style()->font().height(); } -TextRun svgTextRunForInlineTextBox(const UChar* characters, int length, const RenderStyle* style, const InlineTextBox* textBox, float xPosition) +TextRun svgTextRunForInlineTextBox(const UChar* characters, int length, const RenderStyle* style, const InlineTextBox* textBox) { ASSERT(textBox); ASSERT(style); - TextRun run(characters, length, false, static_cast<int>(xPosition), textBox->toAdd(), textBox->direction() == RTL, textBox->m_dirOverride || style->visuallyOrdered()); + TextRun run(characters + , length + , false /* allowTabs */ + , 0 /* xPos, only relevant with allowTabs=true */ + , 0 /* padding, only relevant for justified text, not relevant for SVG */ + , textBox->direction() == RTL + , textBox->m_dirOverride || style->visuallyOrdered() /* directionalOverride */); #if ENABLE(SVG_FONTS) run.setReferencingRenderObject(textBox->textRenderer()->parent()); #endif - // We handle letter & word spacing ourselves + // Disable any word/character rounding. + run.disableRoundingHacks(); + + // We handle letter & word spacing ourselves. run.disableSpacing(); return run; } diff --git a/WebCore/rendering/SVGTextLayoutUtilities.h b/WebCore/rendering/SVGTextLayoutUtilities.h index 196a09c..8c07f62 100644 --- a/WebCore/rendering/SVGTextLayoutUtilities.h +++ b/WebCore/rendering/SVGTextLayoutUtilities.h @@ -32,7 +32,6 @@ class Font; class InlineTextBox; class RenderObject; class RenderStyle; -class RenderSVGResource; class SVGRenderStyle; class TextRun; @@ -40,26 +39,6 @@ struct SVGChar; struct SVGCharacterLayoutInfo; struct SVGInlineBoxCharacterRange; -enum SVGTextPaintSubphase { - SVGTextPaintSubphaseBackground, - SVGTextPaintSubphaseGlyphFill, - SVGTextPaintSubphaseGlyphFillSelection, - SVGTextPaintSubphaseGlyphStroke, - SVGTextPaintSubphaseGlyphStrokeSelection, - SVGTextPaintSubphaseForeground -}; - -struct SVGTextPaintInfo { - SVGTextPaintInfo() - : activePaintingResource(0) - , subphase(SVGTextPaintSubphaseBackground) - { - } - - RenderSVGResource* activePaintingResource; - SVGTextPaintSubphase subphase; -}; - struct SVGLastGlyphInfo { SVGLastGlyphInfo() : isValid(false) @@ -79,7 +58,7 @@ float applyGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator start, Vector<SVGChar>::iterator end); float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange&); float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange&); -TextRun svgTextRunForInlineTextBox(const UChar*, int length, const RenderStyle*, const InlineTextBox*, float xPos); +TextRun svgTextRunForInlineTextBox(const UChar*, int length, const RenderStyle*, const InlineTextBox*); float calculateCSSKerning(const RenderStyle*); bool applySVGKerning(SVGCharacterLayoutInfo&, const RenderStyle*, SVGLastGlyphInfo&, const String& unicodeString, const String& glyphName, bool isVerticalText); diff --git a/WebCore/rendering/SVGTextQuery.cpp b/WebCore/rendering/SVGTextQuery.cpp new file mode 100644 index 0000000..35ca690 --- /dev/null +++ b/WebCore/rendering/SVGTextQuery.cpp @@ -0,0 +1,467 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "SVGTextQuery.h" + +#if ENABLE(SVG) +#include "FloatConversion.h" +#include "InlineFlowBox.h" +#include "RenderBlock.h" +#include "RenderInline.h" +#include "SVGInlineTextBox.h" +#include "VisiblePosition.h" + +namespace WebCore { + +// Base structure for callback user data +struct SVGTextQuery::Data { + Data() + : processedChunkCharacters(0) + { + } + + unsigned processedChunkCharacters; +}; + +static inline InlineFlowBox* flowBoxForRenderer(RenderObject* renderer) +{ + if (!renderer) + return 0; + + if (renderer->isRenderBlock()) { + // If we're given a block element, it has to be a RenderSVGText. + ASSERT(renderer->isSVGText()); + RenderBlock* renderBlock = toRenderBlock(renderer); + + // RenderSVGText only ever contains a single line box. + InlineFlowBox* flowBox = renderBlock->firstLineBox(); + ASSERT(flowBox == renderBlock->lastLineBox()); + return flowBox; + } + + if (renderer->isRenderInline()) { + // We're given a RenderSVGInline or objects that derive from it (RenderSVGTSpan / RenderSVGTextPath) + RenderInline* renderInline = toRenderInline(renderer); + + // RenderSVGInline only ever contains a single line box. + InlineFlowBox* flowBox = renderInline->firstLineBox(); + ASSERT(flowBox == renderInline->lastLineBox()); + return flowBox; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +static inline float mapLengthThroughChunkTransformation(const SVGInlineTextBox* textBox, bool isVerticalText, float length) +{ + const AffineTransform& transform = textBox->chunkTransformation(); + if (transform.isIdentity()) + return length; + + return narrowPrecisionToFloat(static_cast<double>(length) * (isVerticalText ? transform.d() : transform.a())); +} + +SVGTextQuery::SVGTextQuery(RenderObject* renderer) +{ + collectTextBoxesInFlowBox(flowBoxForRenderer(renderer)); +} + +void SVGTextQuery::collectTextBoxesInFlowBox(InlineFlowBox* flowBox) +{ + if (!flowBox) + return; + + for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnLine()) { + if (child->isInlineFlowBox()) { + // Skip generated content. + if (!child->renderer()->node()) + continue; + + collectTextBoxesInFlowBox(static_cast<InlineFlowBox*>(child)); + continue; + } + + ASSERT(child->isSVGInlineTextBox()); + m_textBoxes.append(static_cast<SVGInlineTextBox*>(child)); + } +} + +bool SVGTextQuery::executeQuery(Data* queryData, ProcessTextChunkPartCallback chunkPartCallback) const +{ + ASSERT(!m_textBoxes.isEmpty()); + bool finished = false; + + // Loop over all text boxes + const Vector<SVGInlineTextBox*>::const_iterator end = m_textBoxes.end(); + for (Vector<SVGInlineTextBox*>::const_iterator it = m_textBoxes.begin(); it != end; ++it) { + const SVGInlineTextBox* textBox = *it; + const Vector<SVGTextChunkPart>& parts = textBox->svgTextChunkParts(); + + int processedCharacters = 0; + + // Loop over all text chunk parts in this text box, firing a callback for each chunk part. + const Vector<SVGTextChunkPart>::const_iterator partEnd = parts.end(); + for (Vector<SVGTextChunkPart>::const_iterator partIt = parts.begin(); partIt != partEnd; ++partIt) { + if ((this->*chunkPartCallback)(queryData, textBox, *partIt)) { + finished = true; + break; + } + + processedCharacters += partIt->length; + } + + if (finished) + break; + + queryData->processedChunkCharacters += processedCharacters; + } + + return finished; +} + +bool SVGTextQuery::mapStartAndLengthIntoChunkPartCoordinates(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part, int& startPosition, int& endPosition) const +{ + // Reuse the same logic used for text selection & painting, to map our query start/length into start/endPositions of the current text chunk part. + startPosition -= queryData->processedChunkCharacters; + endPosition -= queryData->processedChunkCharacters; + textBox->mapStartEndPositionsIntoChunkPartCoordinates(startPosition, endPosition, part); + + // If startPosition < endPosition, then the position we're supposed to measure lies in this chunk part. + return startPosition < endPosition; +} + +float SVGTextQuery::measureCharacterRange(const SVGInlineTextBox* textBox, RenderStyle* style, bool isVerticalText, int startPosition, int length) const +{ + // FIXME: Vertical writing mode needs to be handled more accurate. + if (isVerticalText) + return length * style->font().height(); + + const UChar* startCharacter = textBox->textRenderer()->characters() + textBox->start() + startPosition; + return style->font().floatWidth(svgTextRunForInlineTextBox(startCharacter, length, style, textBox)); +} + +// numberOfCharacters() implementation +struct NumberOfCharactersData : SVGTextQuery::Data { + NumberOfCharactersData() + : characters(0) + { + } + + unsigned characters; +}; + +bool SVGTextQuery::numberOfCharactersCallback(Data* queryData, const SVGInlineTextBox*, const SVGTextChunkPart& part) const +{ + NumberOfCharactersData* data = static_cast<NumberOfCharactersData*>(queryData); + data->characters += part.length; + return false; +} + +unsigned SVGTextQuery::numberOfCharacters() const +{ + if (m_textBoxes.isEmpty()) + return 0; + + NumberOfCharactersData data; + executeQuery(&data, &SVGTextQuery::numberOfCharactersCallback); + return data.characters; +} + +// textLength() implementation +struct TextLengthData : SVGTextQuery::Data { + TextLengthData() + : textLength(0.0f) + { + } + + float textLength; +}; + +bool SVGTextQuery::textLengthCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const +{ + TextLengthData* data = static_cast<TextLengthData*>(queryData); + + RenderStyle* style = textBox->textRenderer()->style(); + ASSERT(style); + + bool isVerticalText = isVerticalWritingMode(style->svgStyle()); + float partLength = isVerticalText ? part.height : part.width; + + data->textLength += mapLengthThroughChunkTransformation(textBox, isVerticalText, partLength); + return false; +} + +float SVGTextQuery::textLength() const +{ + if (m_textBoxes.isEmpty()) + return 0.0f; + + TextLengthData data; + executeQuery(&data, &SVGTextQuery::textLengthCallback); + return data.textLength; +} + +// subStringLength() implementation +struct SubStringLengthData : SVGTextQuery::Data { + SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) + : startPosition(queryStartPosition) + , length(queryLength) + , subStringLength(0.0f) + { + } + + unsigned startPosition; + unsigned length; + + float subStringLength; +}; + +bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const +{ + SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData); + + int startPosition = data->startPosition; + int endPosition = startPosition + data->length; + if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) + return false; + + RenderStyle* style = textBox->textRenderer()->style(); + ASSERT(style); + + bool isVerticalText = isVerticalWritingMode(style->svgStyle()); + float partLength = measureCharacterRange(textBox, style, isVerticalWritingMode(style->svgStyle()), part.offset + startPosition, endPosition - startPosition); + + data->subStringLength += mapLengthThroughChunkTransformation(textBox, isVerticalText, partLength); + return false; +} + +float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) const +{ + if (m_textBoxes.isEmpty()) + return 0.0f; + + SubStringLengthData data(startPosition, length); + executeQuery(&data, &SVGTextQuery::subStringLengthCallback); + return data.subStringLength; +} + +// startPositionOfCharacter() implementation +struct StartPositionOfCharacterData : SVGTextQuery::Data { + StartPositionOfCharacterData(unsigned queryPosition) + : position(queryPosition) + { + } + + unsigned position; + FloatPoint startPosition; +}; + +bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const +{ + StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterData*>(queryData); + + int startPosition = data->position; + int endPosition = startPosition + 1; + if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) + return false; + + const SVGChar& character = *(part.firstCharacter + startPosition); + data->startPosition = textBox->chunkTransformation().mapPoint(FloatPoint(character.x, character.y)); + return true; +} + +FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const +{ + if (m_textBoxes.isEmpty()) + return FloatPoint(); + + StartPositionOfCharacterData data(position); + executeQuery(&data, &SVGTextQuery::startPositionOfCharacterCallback); + return data.startPosition; +} + +// endPositionOfCharacter() implementation +struct EndPositionOfCharacterData : SVGTextQuery::Data { + EndPositionOfCharacterData(unsigned queryPosition) + : position(queryPosition) + { + } + + unsigned position; + FloatPoint endPosition; +}; + +bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const +{ + EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(queryData); + + int startPosition = data->position; + int endPosition = startPosition + 1; + if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) + return false; + + const SVGChar& character = *(part.firstCharacter + startPosition); + data->endPosition = FloatPoint(character.x, character.y); + + RenderStyle* style = textBox->textRenderer()->style(); + ASSERT(style); + + bool isVerticalText = isVerticalWritingMode(style->svgStyle()); + float glyphAdvance = measureCharacterRange(textBox, style, isVerticalText, part.offset + startPosition, 1); + + if (isVerticalText) + data->endPosition.move(0.0f, glyphAdvance); + else + data->endPosition.move(glyphAdvance, 0.0f); + + data->endPosition = textBox->chunkTransformation().mapPoint(data->endPosition); + return true; +} + +FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const +{ + if (m_textBoxes.isEmpty()) + return FloatPoint(); + + EndPositionOfCharacterData data(position); + executeQuery(&data, &SVGTextQuery::endPositionOfCharacterCallback); + return data.endPosition; +} + +// rotationOfCharacter() implementation +struct RotationOfCharacterData : SVGTextQuery::Data { + RotationOfCharacterData(unsigned queryPosition) + : position(queryPosition) + , rotation(0.0f) + { + } + + unsigned position; + float rotation; +}; + +bool SVGTextQuery::rotationOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const +{ + RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryData); + + int startPosition = data->position; + int endPosition = startPosition + 1; + if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) + return false; + + const SVGChar& character = *(part.firstCharacter + startPosition); + data->rotation = character.angle; + return true; +} + +float SVGTextQuery::rotationOfCharacter(unsigned position) const +{ + if (m_textBoxes.isEmpty()) + return 0.0f; + + RotationOfCharacterData data(position); + executeQuery(&data, &SVGTextQuery::rotationOfCharacterCallback); + return data.rotation; +} + +// extentOfCharacter() implementation +struct ExtentOfCharacterData : SVGTextQuery::Data { + ExtentOfCharacterData(unsigned queryPosition) + : position(queryPosition) + { + } + + unsigned position; + FloatRect extent; +}; + +bool SVGTextQuery::extentOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const +{ + ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData); + + int startPosition = data->position; + int endPosition = startPosition + 1; + if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition)) + return false; + + RenderStyle* style = textBox->textRenderer()->style(); + ASSERT(style); + + const SVGChar& character = *(part.firstCharacter + startPosition); + data->extent = textBox->calculateGlyphBoundaries(style, part.offset + startPosition, character); + return true; +} + +FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const +{ + if (m_textBoxes.isEmpty()) + return FloatRect(); + + ExtentOfCharacterData data(position); + executeQuery(&data, &SVGTextQuery::extentOfCharacterCallback); + return data.extent; +} + +// characterNumberAtPosition() implementation +struct CharacterNumberAtPositionData : SVGTextQuery::Data { + CharacterNumberAtPositionData(const FloatPoint& queryPosition) + : characterNumber(0) + , position(queryPosition) + { + } + + unsigned characterNumber; + FloatPoint position; +}; + +bool SVGTextQuery::characterNumberAtPositionCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const +{ + CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionData*>(queryData); + + RenderStyle* style = textBox->textRenderer()->style(); + ASSERT(style); + + for (int i = 0; i < part.length; ++i) { + FloatRect extent(textBox->calculateGlyphBoundaries(style, part.offset + i, *(part.firstCharacter + i))); + if (extent.contains(data->position)) + return true; + + ++data->characterNumber; + } + + return false; +} + +int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const +{ + if (m_textBoxes.isEmpty()) + return -1; + + CharacterNumberAtPositionData data(position); + if (!executeQuery(&data, &SVGTextQuery::characterNumberAtPositionCallback)) + return -1; + + return data.characterNumber; +} + +} + +#endif diff --git a/WebCore/rendering/SVGTextQuery.h b/WebCore/rendering/SVGTextQuery.h new file mode 100644 index 0000000..f19c72e --- /dev/null +++ b/WebCore/rendering/SVGTextQuery.h @@ -0,0 +1,76 @@ +/* + Copyright (C) Research In Motion Limited 2010. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SVGTextQuery_h +#define SVGTextQuery_h + +#if ENABLE(SVG) +#include "FloatRect.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class InlineFlowBox; +class RenderObject; +class RenderStyle; +class SVGInlineTextBox; +struct SVGTextChunkPart; + +class SVGTextQuery { +public: + SVGTextQuery(RenderObject*); + + unsigned numberOfCharacters() const; + float textLength() const; + float subStringLength(unsigned startPosition, unsigned length) const; + FloatPoint startPositionOfCharacter(unsigned position) const; + FloatPoint endPositionOfCharacter(unsigned position) const; + float rotationOfCharacter(unsigned position) const; + FloatRect extentOfCharacter(unsigned position) const; + int characterNumberAtPosition(const FloatPoint&) const; + + // Public helper struct. Private classes in SVGTextQuery inherit from it. + struct Data; + +private: + typedef bool (SVGTextQuery::*ProcessTextChunkPartCallback)(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + bool executeQuery(Data*, ProcessTextChunkPartCallback) const; + + void collectTextBoxesInFlowBox(InlineFlowBox*); + float measureCharacterRange(const SVGInlineTextBox*, RenderStyle*, bool isVerticalText, int startPosition, int length) const; + bool mapStartAndLengthIntoChunkPartCoordinates(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&, int& startPosition, int& endPosition) const; + +private: + bool numberOfCharactersCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + bool textLengthCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + bool subStringLengthCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + bool startPositionOfCharacterCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + bool endPositionOfCharacterCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + bool rotationOfCharacterCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + bool extentOfCharacterCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + bool characterNumberAtPositionCallback(Data*, const SVGInlineTextBox*, const SVGTextChunkPart&) const; + +private: + Vector<SVGInlineTextBox*> m_textBoxes; +}; + +} + +#endif +#endif diff --git a/WebCore/rendering/ShadowElement.cpp b/WebCore/rendering/ShadowElement.cpp index dea9233..62b37e1 100644 --- a/WebCore/rendering/ShadowElement.cpp +++ b/WebCore/rendering/ShadowElement.cpp @@ -22,17 +22,85 @@ #include "ShadowElement.h" #include "HTMLNames.h" +#include "RenderTheme.h" +#include "RenderView.h" namespace WebCore { +using namespace HTMLNames; + PassRefPtr<ShadowBlockElement> ShadowBlockElement::create(Node* shadowParent) { return new ShadowBlockElement(shadowParent); } ShadowBlockElement::ShadowBlockElement(Node* shadowParent) - : ShadowElement<HTMLDivElement>(HTMLNames::divTag, shadowParent) + : ShadowElement<HTMLDivElement>(divTag, shadowParent) +{ +} + +void ShadowBlockElement::layoutAsPart(const IntRect& partRect) +{ + RenderBox* parentRenderer = toRenderBox(renderer()->parent()); + RenderBox* selfRenderer = toRenderBox(renderer()); + IntRect oldRect = selfRenderer->frameRect(); + + LayoutStateMaintainer statePusher(parentRenderer->view(), parentRenderer, parentRenderer->size()); + + if (oldRect.size() != partRect.size()) + selfRenderer->setChildNeedsLayout(true, false); + + selfRenderer->layoutIfNeeded(); + selfRenderer->setFrameRect(partRect); + + if (selfRenderer->checkForRepaintDuringLayout()) + selfRenderer->repaintDuringLayoutIfMoved(oldRect); + + statePusher.pop(); + parentRenderer->addOverflowFromChild(selfRenderer); +} + +void ShadowBlockElement::updateStyleForPart(PseudoId pseudoId) +{ + if (renderer()->style()->styleType() != pseudoId) + renderer()->setStyle(createStyleForPart(renderer()->parent(), pseudoId)); +} + +PassRefPtr<ShadowBlockElement> ShadowBlockElement::createForPart(Node* shadowParent, PseudoId pseudoId) +{ + RenderObject* parentRenderer = shadowParent->renderer(); + RefPtr<RenderStyle> styleForPart = createStyleForPart(parentRenderer, pseudoId); + RefPtr<ShadowBlockElement> part = create(shadowParent); + part->setRenderer(part->createRenderer(parentRenderer->renderArena(), styleForPart.get())); + part->renderer()->setStyle(styleForPart.release()); + part->setAttached(); + part->setInDocument(); + return part.release(); +} + +PassRefPtr<RenderStyle> ShadowBlockElement::createStyleForPart(RenderObject* parentRenderer, PseudoId pseudoId) +{ + RefPtr<RenderStyle> styleForPart; + RenderStyle* pseudoStyle = parentRenderer->getCachedPseudoStyle(pseudoId); + if (pseudoStyle) + styleForPart = RenderStyle::clone(pseudoStyle); + else + styleForPart = RenderStyle::create(); + + styleForPart->inheritFrom(parentRenderer->style()); + styleForPart->setDisplay(BLOCK); + styleForPart->setAppearance(NoControlPart); + return styleForPart.release(); +} + +bool ShadowBlockElement::partShouldHaveStyle(const RenderObject* parentRenderer, PseudoId pseudoId) { + // We have some -webkit-appearance values for default styles of parts and + // that appearance get turned off during RenderStyle creation + // if they have background styles specified. + // So !hasAppearance() implies that there are something to be styled. + RenderStyle* pseudoStyle = parentRenderer->getCachedPseudoStyle(pseudoId); + return !(pseudoStyle && pseudoStyle->hasAppearance()); } PassRefPtr<ShadowInputElement> ShadowInputElement::create(Node* shadowParent) @@ -41,7 +109,7 @@ PassRefPtr<ShadowInputElement> ShadowInputElement::create(Node* shadowParent) } ShadowInputElement::ShadowInputElement(Node* shadowParent) - : ShadowElement<HTMLInputElement>(HTMLNames::inputTag, shadowParent) + : ShadowElement<HTMLInputElement>(inputTag, shadowParent) { } diff --git a/WebCore/rendering/ShadowElement.h b/WebCore/rendering/ShadowElement.h index 90030ee..b8aacfd 100644 --- a/WebCore/rendering/ShadowElement.h +++ b/WebCore/rendering/ShadowElement.h @@ -40,20 +40,31 @@ protected: ShadowElement(const QualifiedName& name, Node* shadowParent) : BaseElement(name, shadowParent->document()) , m_shadowParent(shadowParent) - {} + { + } + Node* shadowParent() const { return m_shadowParent; } + +private: virtual bool isShadowNode() const { return true; } virtual Node* shadowParentNode() { return m_shadowParent; } -private: Node* m_shadowParent; }; class ShadowBlockElement : public ShadowElement<HTMLDivElement> { public: static PassRefPtr<ShadowBlockElement> create(Node*); + static PassRefPtr<ShadowBlockElement> createForPart(Node*, PseudoId); + static bool partShouldHaveStyle(const RenderObject* parentRenderer, PseudoId pseudoId); + void layoutAsPart(const IntRect& partRect); + void updateStyleForPart(PseudoId); + protected: ShadowBlockElement(Node*); + +private: + static PassRefPtr<RenderStyle> createStyleForPart(RenderObject*, PseudoId); }; class ShadowInputElement : public ShadowElement<HTMLInputElement> { diff --git a/WebCore/rendering/TextControlInnerElements.cpp b/WebCore/rendering/TextControlInnerElements.cpp index ed12611..61c85e7 100644 --- a/WebCore/rendering/TextControlInnerElements.cpp +++ b/WebCore/rendering/TextControlInnerElements.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -89,7 +90,7 @@ TextControlInnerElement::TextControlInnerElement(Document* document, Node* shado PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(Node* shadowParent) { - return new TextControlInnerElement(shadowParent->document(), shadowParent); + return adoptRef(new TextControlInnerElement(shadowParent->document(), shadowParent)); } void TextControlInnerElement::attachInnerElement(Node* parent, PassRefPtr<RenderStyle> style, RenderArena* arena) @@ -126,7 +127,7 @@ inline TextControlInnerTextElement::TextControlInnerTextElement(Document* docume PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document, Node* shadowParent) { - return new TextControlInnerTextElement(document, shadowParent); + return adoptRef(new TextControlInnerTextElement(document, shadowParent)); } void TextControlInnerTextElement::defaultEventHandler(Event* event) @@ -162,7 +163,7 @@ inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document) { - return new SearchFieldResultsButtonElement(document); + return adoptRef(new SearchFieldResultsButtonElement(document)); } void SearchFieldResultsButtonElement::defaultEventHandler(Event* event) @@ -194,7 +195,7 @@ inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document) { - return new SearchFieldCancelButtonElement(document); + return adoptRef(new SearchFieldCancelButtonElement(document)); } void SearchFieldCancelButtonElement::detach() @@ -251,7 +252,7 @@ inline SpinButtonElement::SpinButtonElement(Node* shadowParent) PassRefPtr<SpinButtonElement> SpinButtonElement::create(Node* shadowParent) { - return new SpinButtonElement(shadowParent); + return adoptRef(new SpinButtonElement(shadowParent)); } void SpinButtonElement::defaultEventHandler(Event* event) @@ -269,20 +270,27 @@ void SpinButtonElement::defaultEventHandler(Event* event) return; } + RenderBox* box = renderBox(); + if (!box) { + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); + return; + } + HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode()); - IntPoint local = roundedIntPoint(renderBox()->absoluteToLocal(mouseEvent->absoluteLocation(), false, true)); + IntPoint local = roundedIntPoint(box->absoluteToLocal(mouseEvent->absoluteLocation(), false, true)); if (event->type() == eventNames().clickEvent) { - if (renderBox()->borderBoxRect().contains(local)) { + if (box->borderBoxRect().contains(local)) { input->focus(); input->select(); - if (local.y() < renderBox()->y() + renderBox()->height() / 2) + if (local.y() < box->y() + box->height() / 2) input->stepUpFromRenderer(1); else input->stepUpFromRenderer(-1); event->setDefaultHandled(); } } else if (event->type() == eventNames().mousemoveEvent) { - if (renderBox()->borderBoxRect().contains(local)) { + if (box->borderBoxRect().contains(local)) { if (!m_capturing) { if (Frame* frame = document()->frame()) { frame->eventHandler()->setCapturingMouseEventsNode(input); @@ -290,7 +298,7 @@ void SpinButtonElement::defaultEventHandler(Event* event) } } bool oldOnUpButton = m_onUpButton; - m_onUpButton = local.y() < renderBox()->y() + renderBox()->height() / 2; + m_onUpButton = local.y() < box->y() + box->height() / 2; if (m_onUpButton != oldOnUpButton) renderer()->repaint(); } else { @@ -307,4 +315,26 @@ void SpinButtonElement::defaultEventHandler(Event* event) HTMLDivElement::defaultEventHandler(event); } +// ---------------------------- + +#if ENABLE(INPUT_SPEECH) + +inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document* document) + : TextControlInnerElement(document) +{ +} + +PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document* document) +{ + return adoptRef(new InputFieldSpeechButtonElement(document)); +} + +void InputFieldSpeechButtonElement::defaultEventHandler(Event* event) +{ + // FIXME: Start speech recognition here. + HTMLDivElement::defaultEventHandler(event); +} + +#endif // ENABLE(INPUT_SPEECH) + } diff --git a/WebCore/rendering/TextControlInnerElements.h b/WebCore/rendering/TextControlInnerElements.h index ed1887e..1884a34 100644 --- a/WebCore/rendering/TextControlInnerElements.h +++ b/WebCore/rendering/TextControlInnerElements.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -104,6 +105,20 @@ private: bool m_onUpButton; }; +#if ENABLE(INPUT_SPEECH) + +class InputFieldSpeechButtonElement : public TextControlInnerElement { +public: + static PassRefPtr<InputFieldSpeechButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + InputFieldSpeechButtonElement(Document*); +}; + +#endif // ENABLE(INPUT_SPEECH) + } // namespace #endif diff --git a/WebCore/rendering/style/RenderStyle.cpp b/WebCore/rendering/style/RenderStyle.cpp index af4e055..40f7a27 100644 --- a/WebCore/rendering/style/RenderStyle.cpp +++ b/WebCore/rendering/style/RenderStyle.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 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 @@ -386,7 +386,9 @@ StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedCon rareInheritedData->wordWrap != other->rareInheritedData->wordWrap || rareInheritedData->nbspMode != other->rareInheritedData->nbspMode || rareInheritedData->khtmlLineBreak != other->rareInheritedData->khtmlLineBreak || - rareInheritedData->textSecurity != other->rareInheritedData->textSecurity) + rareInheritedData->textSecurity != other->rareInheritedData->textSecurity || + rareInheritedData->hyphens != other->rareInheritedData->hyphens || + rareInheritedData->hyphenateCharacter != other->rareInheritedData->hyphenateCharacter) return StyleDifferenceLayout; if (!rareInheritedData->shadowDataEquivalent(*other->rareInheritedData.get())) @@ -802,6 +804,19 @@ CounterDirectiveMap& RenderStyle::accessCounterDirectives() return *map.get(); } +const AtomicString& RenderStyle::hyphenString() const +{ + ASSERT(hyphens() == HyphensAuto); + + const AtomicString& hyphenateCharacter = rareInheritedData.get()->hyphenateCharacter; + if (!hyphenateCharacter.isNull()) + return hyphenateCharacter; + + // FIXME: This should depend on locale. + DEFINE_STATIC_LOCAL(AtomicString, hyphenMinusString, (&hyphen, 1)); + return hyphenMinusString; +} + #if ENABLE(DASHBOARD_SUPPORT) const Vector<StyleDashboardRegion>& RenderStyle::initialDashboardRegions() { diff --git a/WebCore/rendering/style/RenderStyle.h b/WebCore/rendering/style/RenderStyle.h index 16dcae8..2914fcf 100644 --- a/WebCore/rendering/style/RenderStyle.h +++ b/WebCore/rendering/style/RenderStyle.h @@ -2,7 +2,7 @@ * 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, 2010 Apple Inc. All rights reserved. * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) * * This library is free software; you can redistribute it and/or @@ -647,6 +647,8 @@ public: EKHTMLLineBreak khtmlLineBreak() const { return static_cast<EKHTMLLineBreak>(rareInheritedData->khtmlLineBreak); } EMatchNearestMailBlockquoteColor matchNearestMailBlockquoteColor() const { return static_cast<EMatchNearestMailBlockquoteColor>(rareNonInheritedData->matchNearestMailBlockquoteColor); } const AtomicString& highlight() const { return rareInheritedData->highlight; } + Hyphens hyphens() const { return static_cast<Hyphens>(rareInheritedData->hyphens); } + const AtomicString& hyphenateCharacter() const { return rareInheritedData->hyphenateCharacter; } EBorderFit borderFit() const { return static_cast<EBorderFit>(rareNonInheritedData->m_borderFit); } EResize resize() const { return static_cast<EResize>(rareInheritedData->resize); } float columnWidth() const { return rareNonInheritedData->m_multiCol->m_width; } @@ -701,6 +703,7 @@ public: bool hasPerspective() const { return rareNonInheritedData->m_perspective > 0; } Length perspectiveOriginX() const { return rareNonInheritedData->m_perspectiveOriginX; } Length perspectiveOriginY() const { return rareNonInheritedData->m_perspectiveOriginY; } + LengthSize pageSize() const { return rareNonInheritedData->m_pageSize; } #if USE(ACCELERATED_COMPOSITING) // When set, this ensures that styles compare as different. Used during accelerated animations. @@ -826,6 +829,7 @@ public: void setClipTop(Length v) { SET_VAR(visual, clip.m_top, v) } void setClipBottom(Length v) { SET_VAR(visual, clip.m_bottom, v) } void setClip(Length top, Length right, Length bottom, Length left); + void setClip(LengthBox box) { SET_VAR(visual, clip, box) } void setUnicodeBidi(EUnicodeBidi b) { noninherited_flags._unicodeBidi = b; } @@ -982,6 +986,8 @@ public: void setKHTMLLineBreak(EKHTMLLineBreak b) { SET_VAR(rareInheritedData, khtmlLineBreak, b); } void setMatchNearestMailBlockquoteColor(EMatchNearestMailBlockquoteColor c) { SET_VAR(rareNonInheritedData, matchNearestMailBlockquoteColor, c); } void setHighlight(const AtomicString& h) { SET_VAR(rareInheritedData, highlight, h); } + void setHyphens(Hyphens h) { SET_VAR(rareInheritedData, hyphens, h); } + void setHyphenateCharacter(const AtomicString& h) { SET_VAR(rareInheritedData, hyphenateCharacter, h); } void setBorderFit(EBorderFit b) { SET_VAR(rareNonInheritedData, m_borderFit, b); } void setResize(EResize r) { SET_VAR(rareInheritedData, resize, r); } void setColumnWidth(float f) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_autoWidth, false); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_width, f); } @@ -1028,6 +1034,7 @@ public: void setPerspective(float p) { SET_VAR(rareNonInheritedData, m_perspective, p); } void setPerspectiveOriginX(Length l) { SET_VAR(rareNonInheritedData, m_perspectiveOriginX, l); } void setPerspectiveOriginY(Length l) { SET_VAR(rareNonInheritedData, m_perspectiveOriginY, l); } + void setPageSize(LengthSize s) { SET_VAR(rareNonInheritedData, m_pageSize, s); } #if USE(ACCELERATED_COMPOSITING) void setIsRunningAcceleratedAnimation(bool b = true) { SET_VAR(rareNonInheritedData, m_runningAcceleratedAnimation, b); } @@ -1065,6 +1072,8 @@ public: const CounterDirectiveMap* counterDirectives() const; CounterDirectiveMap& accessCounterDirectives(); + const AtomicString& hyphenString() const; + bool inheritedNotEqual(const RenderStyle*) const; StyleDifference diff(const RenderStyle*, unsigned& changedContextSensitiveProperties) const; @@ -1188,6 +1197,8 @@ public: static EKHTMLLineBreak initialKHTMLLineBreak() { return LBNORMAL; } static EMatchNearestMailBlockquoteColor initialMatchNearestMailBlockquoteColor() { return BCNORMAL; } static const AtomicString& initialHighlight() { return nullAtom; } + static Hyphens initialHyphens() { return HyphensManual; } + static const AtomicString& initialHyphenateCharacter() { return nullAtom; } static EBorderFit initialBorderFit() { return BorderFitBorder; } static EResize initialResize() { return RESIZE_NONE; } static ControlPart initialAppearance() { return NoControlPart; } diff --git a/WebCore/rendering/style/RenderStyleConstants.h b/WebCore/rendering/style/RenderStyleConstants.h index b6dce18..dd82433 100644 --- a/WebCore/rendering/style/RenderStyleConstants.h +++ b/WebCore/rendering/style/RenderStyleConstants.h @@ -74,8 +74,9 @@ enum PseudoId { MEDIA_CONTROLS_SEEK_BACK_BUTTON, MEDIA_CONTROLS_SEEK_FORWARD_BUTTON, MEDIA_CONTROLS_FULLSCREEN_BUTTON, MEDIA_CONTROLS_REWIND_BUTTON, MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON, MEDIA_CONTROLS_STATUS_DISPLAY, SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER, RESIZER, - INPUT_LIST_BUTTON, INNER_SPIN_BUTTON, OUTER_SPIN_BUTTON, VISITED_LINK, PROGRESS_BAR_VALUE, - + INPUT_LIST_BUTTON, INPUT_SPEECH_BUTTON, INNER_SPIN_BUTTON, OUTER_SPIN_BUTTON, VISITED_LINK, PROGRESS_BAR_VALUE, + METER_HORIZONTAL_BAR, METER_HORIZONTAL_OPTIMUM, METER_HORIZONTAL_SUBOPTIMAL, METER_HORIZONTAL_EVEN_LESS_GOOD, + METER_VERTICAL_BAR, METER_VERTICAL_OPTIMUM, METER_VERTICAL_SUBOPTIMAL, METER_VERTICAL_EVEN_LESS_GOOD, AFTER_LAST_INTERNAL_PSEUDOID, FIRST_PUBLIC_PSEUDOID = FIRST_LINE, FIRST_INTERNAL_PSEUDOID = FILE_UPLOAD_BUTTON, @@ -405,6 +406,8 @@ enum EBackfaceVisibility { enum ELineClampType { LineClampLineCount, LineClampPercentage }; +enum Hyphens { HyphensNone, HyphensManual, HyphensAuto }; + } // namespace WebCore #endif // RenderStyleConstants_h diff --git a/WebCore/rendering/style/StyleRareInheritedData.cpp b/WebCore/rendering/style/StyleRareInheritedData.cpp index 04923d5..cb6edf3 100644 --- a/WebCore/rendering/style/StyleRareInheritedData.cpp +++ b/WebCore/rendering/style/StyleRareInheritedData.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 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 @@ -47,6 +47,7 @@ StyleRareInheritedData::StyleRareInheritedData() , resize(RenderStyle::initialResize()) , userSelect(RenderStyle::initialUserSelect()) , colorSpace(DeviceColorSpace) + , hyphens(HyphensManual) { } @@ -75,6 +76,8 @@ StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o) , resize(o.resize) , userSelect(o.userSelect) , colorSpace(o.colorSpace) + , hyphens(o.hyphens) + , hyphenateCharacter(o.hyphenateCharacter) { } @@ -116,7 +119,9 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const #endif && resize == o.resize && userSelect == o.userSelect - && colorSpace == o.colorSpace; + && colorSpace == o.colorSpace + && hyphens == o.hyphens + && hyphenateCharacter == o.hyphenateCharacter; } bool StyleRareInheritedData::shadowDataEquivalent(const StyleRareInheritedData& o) const diff --git a/WebCore/rendering/style/StyleRareInheritedData.h b/WebCore/rendering/style/StyleRareInheritedData.h index 3ad8b0b..f6e3bf4 100644 --- a/WebCore/rendering/style/StyleRareInheritedData.h +++ b/WebCore/rendering/style/StyleRareInheritedData.h @@ -80,7 +80,10 @@ public: unsigned resize : 2; // EResize unsigned userSelect : 1; // EUserSelect unsigned colorSpace : 1; // ColorSpace - + unsigned hyphens : 2; // Hyphens + + AtomicString hyphenateCharacter; + private: StyleRareInheritedData(); StyleRareInheritedData(const StyleRareInheritedData&); diff --git a/WebCore/rendering/style/StyleRareNonInheritedData.h b/WebCore/rendering/style/StyleRareNonInheritedData.h index d6fe96e..74e736c 100644 --- a/WebCore/rendering/style/StyleRareNonInheritedData.h +++ b/WebCore/rendering/style/StyleRareNonInheritedData.h @@ -40,6 +40,7 @@ namespace WebCore { class AnimationList; class CSSStyleSelector; +class LengthSize; class ShadowData; class StyleFlexibleBoxData; class StyleMarqueeData; @@ -50,7 +51,7 @@ class StyleTransformData; struct ContentData; #if ENABLE(DASHBOARD_SUPPORT) -class StyleDashboardRegion; +struct StyleDashboardRegion; #endif #if ENABLE(XBL) @@ -123,6 +124,8 @@ public: Length m_perspectiveOriginX; Length m_perspectiveOriginY; + LengthSize m_pageSize; + #if ENABLE(XBL) OwnPtr<BindingURI> bindingURI; // The XBL binding URI list. #endif |