summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering
diff options
context:
space:
mode:
authorKristian Monsen <kristianm@google.com>2010-06-28 16:42:48 +0100
committerKristian Monsen <kristianm@google.com>2010-07-02 10:29:56 +0100
commit06ea8e899e48f1f2f396b70e63fae369f2f23232 (patch)
tree20c1428cd05c76f32394ab354ea35ed99acd86d8 /WebCore/rendering
parent72aad67af14193199e29cdd5c4ddc095a8b9a8a8 (diff)
downloadexternal_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')
-rw-r--r--WebCore/rendering/BidiRun.h2
-rw-r--r--WebCore/rendering/InlineBox.h28
-rw-r--r--WebCore/rendering/InlineFlowBox.h2
-rw-r--r--WebCore/rendering/InlineTextBox.cpp46
-rw-r--r--WebCore/rendering/InlineTextBox.h11
-rw-r--r--WebCore/rendering/MediaControlElements.cpp30
-rw-r--r--WebCore/rendering/RenderApplet.cpp2
-rw-r--r--WebCore/rendering/RenderBlock.h18
-rw-r--r--WebCore/rendering/RenderBlockLineLayout.cpp112
-rw-r--r--WebCore/rendering/RenderBoxModelObject.cpp178
-rw-r--r--WebCore/rendering/RenderBoxModelObject.h3
-rw-r--r--WebCore/rendering/RenderEmbeddedObject.cpp17
-rw-r--r--WebCore/rendering/RenderFileUploadControl.cpp1
-rw-r--r--WebCore/rendering/RenderImage.cpp131
-rw-r--r--WebCore/rendering/RenderIndicator.cpp81
-rw-r--r--WebCore/rendering/RenderIndicator.h53
-rw-r--r--WebCore/rendering/RenderLayer.cpp8
-rw-r--r--WebCore/rendering/RenderLayer.h1
-rw-r--r--WebCore/rendering/RenderLayerBacking.cpp25
-rw-r--r--WebCore/rendering/RenderLayerCompositor.cpp5
-rw-r--r--WebCore/rendering/RenderMenuList.cpp2
-rw-r--r--WebCore/rendering/RenderMeter.cpp131
-rw-r--r--WebCore/rendering/RenderMeter.h22
-rw-r--r--WebCore/rendering/RenderObject.cpp10
-rw-r--r--WebCore/rendering/RenderObject.h1
-rw-r--r--WebCore/rendering/RenderProgress.cpp128
-rw-r--r--WebCore/rendering/RenderProgress.h13
-rw-r--r--WebCore/rendering/RenderSVGBlock.cpp5
-rw-r--r--WebCore/rendering/RenderSVGBlock.h5
-rw-r--r--WebCore/rendering/RenderSVGHiddenContainer.cpp5
-rw-r--r--WebCore/rendering/RenderSVGHiddenContainer.h1
-rw-r--r--WebCore/rendering/RenderSVGImage.cpp12
-rw-r--r--WebCore/rendering/RenderSVGImage.h7
-rw-r--r--WebCore/rendering/RenderSVGInline.cpp53
-rw-r--r--WebCore/rendering/RenderSVGInline.h16
-rw-r--r--WebCore/rendering/RenderSVGInlineText.cpp132
-rw-r--r--WebCore/rendering/RenderSVGInlineText.h13
-rw-r--r--WebCore/rendering/RenderSVGModelObject.cpp7
-rw-r--r--WebCore/rendering/RenderSVGModelObject.h2
-rw-r--r--WebCore/rendering/RenderSVGResourceFilter.cpp4
-rw-r--r--WebCore/rendering/RenderSVGResourceFilter.h4
-rw-r--r--WebCore/rendering/RenderSVGRoot.cpp8
-rw-r--r--WebCore/rendering/RenderSVGText.cpp112
-rw-r--r--WebCore/rendering/RenderSVGText.h12
-rw-r--r--WebCore/rendering/RenderSelectionInfo.h6
-rw-r--r--WebCore/rendering/RenderSlider.cpp9
-rw-r--r--WebCore/rendering/RenderTable.h2
-rw-r--r--WebCore/rendering/RenderTableSection.cpp2
-rw-r--r--WebCore/rendering/RenderText.cpp6
-rw-r--r--WebCore/rendering/RenderTextControlSingleLine.cpp126
-rw-r--r--WebCore/rendering/RenderTextControlSingleLine.h9
-rw-r--r--WebCore/rendering/RenderTheme.cpp73
-rw-r--r--WebCore/rendering/RenderTheme.h1
-rw-r--r--WebCore/rendering/RenderThemeChromiumMac.mm2
-rw-r--r--WebCore/rendering/RenderThemeMac.h1
-rw-r--r--WebCore/rendering/RenderThemeMac.mm14
-rw-r--r--WebCore/rendering/RenderTreeAsText.cpp9
-rw-r--r--WebCore/rendering/RenderView.cpp23
-rw-r--r--WebCore/rendering/RootInlineBox.cpp24
-rw-r--r--WebCore/rendering/RootInlineBox.h11
-rw-r--r--WebCore/rendering/SVGCharacterData.h6
-rw-r--r--WebCore/rendering/SVGCharacterLayoutInfo.cpp3
-rw-r--r--WebCore/rendering/SVGCharacterLayoutInfo.h4
-rw-r--r--WebCore/rendering/SVGInlineFlowBox.cpp40
-rw-r--r--WebCore/rendering/SVGInlineFlowBox.h6
-rw-r--r--WebCore/rendering/SVGInlineTextBox.cpp866
-rw-r--r--WebCore/rendering/SVGInlineTextBox.h68
-rw-r--r--WebCore/rendering/SVGRenderSupport.h9
-rw-r--r--WebCore/rendering/SVGRenderTreeAsText.cpp2
-rw-r--r--WebCore/rendering/SVGRootInlineBox.cpp1218
-rw-r--r--WebCore/rendering/SVGRootInlineBox.h40
-rw-r--r--WebCore/rendering/SVGTextChunkLayoutInfo.cpp493
-rw-r--r--WebCore/rendering/SVGTextChunkLayoutInfo.h178
-rw-r--r--WebCore/rendering/SVGTextLayoutUtilities.cpp27
-rw-r--r--WebCore/rendering/SVGTextLayoutUtilities.h23
-rw-r--r--WebCore/rendering/SVGTextQuery.cpp467
-rw-r--r--WebCore/rendering/SVGTextQuery.h76
-rw-r--r--WebCore/rendering/ShadowElement.cpp72
-rw-r--r--WebCore/rendering/ShadowElement.h15
-rw-r--r--WebCore/rendering/TextControlInnerElements.cpp50
-rw-r--r--WebCore/rendering/TextControlInnerElements.h15
-rw-r--r--WebCore/rendering/style/RenderStyle.cpp19
-rw-r--r--WebCore/rendering/style/RenderStyle.h13
-rw-r--r--WebCore/rendering/style/RenderStyleConstants.h7
-rw-r--r--WebCore/rendering/style/StyleRareInheritedData.cpp9
-rw-r--r--WebCore/rendering/style/StyleRareInheritedData.h5
-rw-r--r--WebCore/rendering/style/StyleRareNonInheritedData.h5
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