5b8cfe34cd84a179dadb17549bbc261e8262918c
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / svg / SVGTextContentElement.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22 #include "core/svg/SVGTextContentElement.h"
23
24 #include "CSSPropertyNames.h"
25 #include "CSSValueKeywords.h"
26 #include "SVGNames.h"
27 #include "XMLNames.h"
28 #include "bindings/v8/ExceptionMessages.h"
29 #include "bindings/v8/ExceptionState.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "core/editing/FrameSelection.h"
32 #include "core/frame/LocalFrame.h"
33 #include "core/rendering/RenderObject.h"
34 #include "core/rendering/svg/RenderSVGResource.h"
35 #include "core/rendering/svg/SVGTextQuery.h"
36 #include "core/svg/SVGElementInstance.h"
37
38 namespace WebCore {
39
40 template<> const SVGEnumerationStringEntries& getStaticStringEntries<SVGLengthAdjustType>()
41 {
42     DEFINE_STATIC_LOCAL(SVGEnumerationStringEntries, entries, ());
43     if (entries.isEmpty()) {
44         entries.append(std::make_pair(SVGLengthAdjustUnknown, emptyString()));
45         entries.append(std::make_pair(SVGLengthAdjustSpacing, "spacing"));
46         entries.append(std::make_pair(SVGLengthAdjustSpacingAndGlyphs, "spacingAndGlyphs"));
47     }
48     return entries;
49 }
50
51 // SVGTextContentElement's 'textLength' attribute needs special handling.
52 // It should return getComputedTextLength() when textLength is not specified manually.
53 class SVGAnimatedTextLength FINAL : public SVGAnimatedLength {
54 public:
55     static PassRefPtr<SVGAnimatedTextLength> create(SVGTextContentElement* contextElement)
56     {
57         return adoptRef(new SVGAnimatedTextLength(contextElement));
58     }
59
60     virtual SVGLengthTearOff* baseVal() OVERRIDE
61     {
62         SVGTextContentElement* textContentElement = toSVGTextContentElement(contextElement());
63         if (!textContentElement->textLengthIsSpecifiedByUser())
64             baseValue()->newValueSpecifiedUnits(LengthTypeNumber, textContentElement->getComputedTextLength());
65
66         return SVGAnimatedLength::baseVal();
67     }
68
69 private:
70     SVGAnimatedTextLength(SVGTextContentElement* contextElement)
71         : SVGAnimatedLength(contextElement, SVGNames::textLengthAttr, SVGLength::create(LengthModeOther), ForbidNegativeLengths)
72     {
73     }
74 };
75
76
77 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document& document)
78     : SVGGraphicsElement(tagName, document)
79     , m_textLength(SVGAnimatedTextLength::create(this))
80     , m_textLengthIsSpecifiedByUser(false)
81     , m_lengthAdjust(SVGAnimatedEnumeration<SVGLengthAdjustType>::create(this, SVGNames::lengthAdjustAttr, SVGLengthAdjustSpacing))
82 {
83     ScriptWrappable::init(this);
84
85     addToPropertyMap(m_textLength);
86     addToPropertyMap(m_lengthAdjust);
87 }
88
89 unsigned SVGTextContentElement::getNumberOfChars()
90 {
91     document().updateLayoutIgnorePendingStylesheets();
92     return SVGTextQuery(renderer()).numberOfCharacters();
93 }
94
95 float SVGTextContentElement::getComputedTextLength()
96 {
97     document().updateLayoutIgnorePendingStylesheets();
98     return SVGTextQuery(renderer()).textLength();
99 }
100
101 float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
102 {
103     document().updateLayoutIgnorePendingStylesheets();
104
105     unsigned numberOfChars = getNumberOfChars();
106     if (charnum >= numberOfChars) {
107         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
108         return 0.0f;
109     }
110
111     if (nchars > numberOfChars - charnum)
112         nchars = numberOfChars - charnum;
113
114     return SVGTextQuery(renderer()).subStringLength(charnum, nchars);
115 }
116
117 PassRefPtr<SVGPointTearOff> SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
118 {
119     document().updateLayoutIgnorePendingStylesheets();
120
121     if (charnum > getNumberOfChars()) {
122         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
123         return nullptr;
124     }
125
126     FloatPoint point = SVGTextQuery(renderer()).startPositionOfCharacter(charnum);
127     return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal);
128 }
129
130 PassRefPtr<SVGPointTearOff> SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
131 {
132     document().updateLayoutIgnorePendingStylesheets();
133
134     if (charnum > getNumberOfChars()) {
135         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
136         return nullptr;
137     }
138
139     FloatPoint point = SVGTextQuery(renderer()).endPositionOfCharacter(charnum);
140     return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal);
141 }
142
143 PassRefPtr<SVGRectTearOff> SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionState& exceptionState)
144 {
145     document().updateLayoutIgnorePendingStylesheets();
146
147     if (charnum > getNumberOfChars()) {
148         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
149         return nullptr;
150     }
151
152     FloatRect rect = SVGTextQuery(renderer()).extentOfCharacter(charnum);
153     return SVGRectTearOff::create(SVGRect::create(rect), 0, PropertyIsNotAnimVal);
154 }
155
156 float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionState& exceptionState)
157 {
158     document().updateLayoutIgnorePendingStylesheets();
159
160     if (charnum > getNumberOfChars()) {
161         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
162         return 0.0f;
163     }
164
165     return SVGTextQuery(renderer()).rotationOfCharacter(charnum);
166 }
167
168 int SVGTextContentElement::getCharNumAtPosition(PassRefPtr<SVGPointTearOff> point, ExceptionState& exceptionState)
169 {
170     document().updateLayoutIgnorePendingStylesheets();
171     return SVGTextQuery(renderer()).characterNumberAtPosition(point->target()->value());
172 }
173
174 void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
175 {
176     unsigned numberOfChars = getNumberOfChars();
177     if (charnum >= numberOfChars) {
178         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
179         return;
180     }
181
182     if (nchars > numberOfChars - charnum)
183         nchars = numberOfChars - charnum;
184
185     ASSERT(document().frame());
186
187     // Find selection start
188     VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this)));
189     for (unsigned i = 0; i < charnum; ++i)
190         start = start.next();
191
192     // Find selection end
193     VisiblePosition end(start);
194     for (unsigned i = 0; i < nchars; ++i)
195         end = end.next();
196
197     document().frame()->selection().setSelection(VisibleSelection(start, end));
198 }
199
200 bool SVGTextContentElement::isSupportedAttribute(const QualifiedName& attrName)
201 {
202     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
203     if (supportedAttributes.isEmpty()) {
204         supportedAttributes.add(SVGNames::lengthAdjustAttr);
205         supportedAttributes.add(SVGNames::textLengthAttr);
206         supportedAttributes.add(XMLNames::spaceAttr);
207     }
208     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
209 }
210
211 bool SVGTextContentElement::isPresentationAttribute(const QualifiedName& name) const
212 {
213     if (name.matches(XMLNames::spaceAttr))
214         return true;
215     return SVGGraphicsElement::isPresentationAttribute(name);
216 }
217
218 void SVGTextContentElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
219 {
220     if (!isSupportedAttribute(name))
221         SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
222     else if (name.matches(XMLNames::spaceAttr)) {
223         DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve", AtomicString::ConstructFromLiteral));
224
225         if (value == preserveString)
226             addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValuePre);
227         else
228             addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValueNowrap);
229     }
230 }
231
232 void SVGTextContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
233 {
234     SVGParsingError parseError = NoError;
235
236     if (!isSupportedAttribute(name))
237         SVGGraphicsElement::parseAttribute(name, value);
238     else if (name == SVGNames::lengthAdjustAttr) {
239         m_lengthAdjust->setBaseValueAsString(value, parseError);
240     } else if (name == SVGNames::textLengthAttr) {
241         m_textLength->setBaseValueAsString(value, parseError);
242     } else if (name.matches(XMLNames::spaceAttr)) {
243     } else
244         ASSERT_NOT_REACHED();
245
246     reportAttributeParsingError(parseError, name, value);
247 }
248
249 void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName)
250 {
251     if (!isSupportedAttribute(attrName)) {
252         SVGGraphicsElement::svgAttributeChanged(attrName);
253         return;
254     }
255
256     if (attrName == SVGNames::textLengthAttr)
257         m_textLengthIsSpecifiedByUser = true;
258
259     SVGElementInstance::InvalidationGuard invalidationGuard(this);
260
261     if (RenderObject* renderer = this->renderer())
262         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
263 }
264
265 bool SVGTextContentElement::selfHasRelativeLengths() const
266 {
267     // Any element of the <text> subtree is advertized as using relative lengths.
268     // On any window size change, we have to relayout the text subtree, as the
269     // effective 'on-screen' font size may change.
270     return true;
271 }
272
273 SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer)
274 {
275     if (!renderer)
276         return 0;
277
278     if (!renderer->isSVGText() && !renderer->isSVGInline())
279         return 0;
280
281     SVGElement* element = toSVGElement(renderer->node());
282     ASSERT(element);
283     return isSVGTextContentElement(*element) ? toSVGTextContentElement(element) : 0;
284 }
285
286 }