Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / svg / base / src / nsSVGTextContainerFrame.cpp
blobc3dc3f2f41b07c1f2b48b33e9267daeb259c8818
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is the Mozilla SVG project.
17 * The Initial Developer of the Original Code is IBM Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2006
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "nsSVGContainerFrame.h"
38 #include "nsSVGTextFrame.h"
39 #include "nsSVGUtils.h"
40 #include "nsSVGMatrix.h"
41 #include "nsSVGOuterSVGFrame.h"
42 #include "nsIDOMSVGTextElement.h"
43 #include "nsIDOMSVGAnimatedLengthList.h"
44 #include "nsISVGGlyphFragmentLeaf.h"
45 #include "nsDOMError.h"
47 //----------------------------------------------------------------------
48 // nsISupports methods
50 NS_INTERFACE_MAP_BEGIN(nsSVGTextContainerFrame)
51 NS_INTERFACE_MAP_ENTRY(nsISVGTextContentMetrics)
52 NS_INTERFACE_MAP_END_INHERITING(nsSVGDisplayContainerFrame)
54 void
55 nsSVGTextContainerFrame::NotifyGlyphMetricsChange()
57 nsSVGTextFrame *textFrame = GetTextFrame();
58 if (textFrame)
59 textFrame->NotifyGlyphMetricsChange();
62 NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
63 nsSVGTextContainerFrame::GetX()
65 nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
66 do_QueryInterface(mContent);
68 if (!tpElement)
69 return nsnull;
71 if (!mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::x))
72 return nsnull;
74 nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
75 tpElement->GetX(getter_AddRefs(animLengthList));
76 nsIDOMSVGLengthList *retval;
77 animLengthList->GetAnimVal(&retval);
78 return retval;
81 NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
82 nsSVGTextContainerFrame::GetY()
84 nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
85 do_QueryInterface(mContent);
87 if (!tpElement)
88 return nsnull;
90 if (!mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::y))
91 return nsnull;
93 nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
94 tpElement->GetY(getter_AddRefs(animLengthList));
95 nsIDOMSVGLengthList *retval;
96 animLengthList->GetAnimVal(&retval);
97 return retval;
100 NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
101 nsSVGTextContainerFrame::GetDx()
103 nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
104 do_QueryInterface(mContent);
106 if (!tpElement)
107 return nsnull;
109 nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
110 tpElement->GetDx(getter_AddRefs(animLengthList));
111 nsIDOMSVGLengthList *retval;
112 animLengthList->GetAnimVal(&retval);
113 return retval;
116 NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
117 nsSVGTextContainerFrame::GetDy()
119 nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
120 do_QueryInterface(mContent);
122 if (!tpElement)
123 return nsnull;
125 nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
126 tpElement->GetDy(getter_AddRefs(animLengthList));
127 nsIDOMSVGLengthList *retval;
128 animLengthList->GetAnimVal(&retval);
129 return retval;
132 //----------------------------------------------------------------------
133 // nsIFrame methods
135 NS_IMETHODIMP
136 nsSVGTextContainerFrame::InsertFrames(nsIAtom* aListName,
137 nsIFrame* aPrevFrame,
138 nsIFrame* aFrameList)
140 nsresult rv = nsSVGDisplayContainerFrame::InsertFrames(aListName,
141 aPrevFrame,
142 aFrameList);
144 NotifyGlyphMetricsChange();
145 return rv;
148 NS_IMETHODIMP
149 nsSVGTextContainerFrame::RemoveFrame(nsIAtom *aListName, nsIFrame *aOldFrame)
151 nsSVGTextFrame *textFrame = GetTextFrame();
153 nsresult rv = nsSVGDisplayContainerFrame::RemoveFrame(aListName, aOldFrame);
155 if (textFrame)
156 textFrame->NotifyGlyphMetricsChange();
158 return rv;
161 //----------------------------------------------------------------------
162 // nsISVGTextContentMetrics methods
164 NS_IMETHODIMP
165 nsSVGTextContainerFrame::GetNumberOfChars(PRInt32 *_retval)
167 *_retval = GetNumberOfChars();
169 return NS_OK;
172 NS_IMETHODIMP
173 nsSVGTextContainerFrame::GetComputedTextLength(float *_retval)
175 *_retval = GetComputedTextLength();
177 return NS_OK;
180 NS_IMETHODIMP
181 nsSVGTextContainerFrame::GetSubStringLength(PRUint32 charnum,
182 PRUint32 nchars,
183 float *_retval)
185 PRUint32 charcount = GetNumberOfChars();
186 if (charcount <= charnum || nchars > charcount - charnum) {
187 *_retval = 0.0f;
188 return NS_ERROR_DOM_INDEX_SIZE_ERR;
191 if (nchars == 0) {
192 *_retval = 0.0f;
193 return NS_OK;
196 *_retval = GetSubStringLengthNoValidation(charnum, nchars);
198 return NS_OK;
201 NS_IMETHODIMP
202 nsSVGTextContainerFrame::GetStartPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
204 *_retval = nsnull;
206 if (charnum >= GetNumberOfChars()) {
207 return NS_ERROR_DOM_INDEX_SIZE_ERR;
210 nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
211 if (!node) {
212 return NS_ERROR_FAILURE;
215 PRUint32 offset;
216 nsISVGGlyphFragmentLeaf *fragment = GetGlyphFragmentAtCharNum(node, charnum, &offset);
217 if (!fragment) {
218 return NS_ERROR_FAILURE;
221 return fragment->GetStartPositionOfChar(charnum - offset, _retval);
224 NS_IMETHODIMP
225 nsSVGTextContainerFrame::GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
227 *_retval = nsnull;
229 if (charnum >= GetNumberOfChars()) {
230 return NS_ERROR_DOM_INDEX_SIZE_ERR;
233 nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
234 if (!node) {
235 return NS_ERROR_FAILURE;
238 PRUint32 offset;
239 nsISVGGlyphFragmentLeaf *fragment = GetGlyphFragmentAtCharNum(node, charnum, &offset);
240 if (!fragment) {
241 return NS_ERROR_FAILURE;
244 return fragment->GetEndPositionOfChar(charnum - offset, _retval);
247 NS_IMETHODIMP
248 nsSVGTextContainerFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
250 *_retval = nsnull;
252 if (charnum >= GetNumberOfChars()) {
253 return NS_ERROR_DOM_INDEX_SIZE_ERR;
256 nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
257 if (!node) {
258 return NS_ERROR_FAILURE;
261 PRUint32 offset;
262 nsISVGGlyphFragmentLeaf *fragment = GetGlyphFragmentAtCharNum(node, charnum, &offset);
263 if (!fragment) {
264 return NS_ERROR_FAILURE;
267 return fragment->GetExtentOfChar(charnum - offset, _retval);
270 NS_IMETHODIMP
271 nsSVGTextContainerFrame::GetRotationOfChar(PRUint32 charnum, float *_retval)
273 *_retval = 0.0f;
275 if (charnum >= GetNumberOfChars()) {
276 return NS_ERROR_DOM_INDEX_SIZE_ERR;
279 nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
280 if (!node) {
281 return NS_ERROR_FAILURE;
284 PRUint32 offset;
285 nsISVGGlyphFragmentLeaf *fragment = GetGlyphFragmentAtCharNum(node, charnum, &offset);
286 if (!fragment) {
287 return NS_ERROR_FAILURE;
290 return fragment->GetRotationOfChar(charnum - offset, _retval);
293 NS_IMETHODIMP
294 nsSVGTextContainerFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point, PRInt32 *_retval)
296 *_retval = GetCharNumAtPosition(point);
298 return NS_OK;
301 // -------------------------------------------------------------------------
302 // Protected functions
303 // -------------------------------------------------------------------------
305 nsISVGGlyphFragmentNode *
306 nsSVGTextContainerFrame::GetFirstGlyphFragmentChildNode()
308 nsISVGGlyphFragmentNode *retval = nsnull;
309 nsIFrame* kid = mFrames.FirstChild();
310 while (kid) {
311 CallQueryInterface(kid, &retval);
312 if (retval) break;
313 kid = kid->GetNextSibling();
315 return retval;
318 nsISVGGlyphFragmentNode *
319 nsSVGTextContainerFrame::GetNextGlyphFragmentChildNode(nsISVGGlyphFragmentNode *node)
321 nsISVGGlyphFragmentNode *retval = nsnull;
322 nsIFrame *frame = nsnull;
323 CallQueryInterface(node, &frame);
324 NS_ASSERTION(frame, "interface not implemented");
325 frame = frame->GetNextSibling();
326 while (frame) {
327 CallQueryInterface(frame, &retval);
328 if (retval) break;
329 frame = frame->GetNextSibling();
331 return retval;
334 void
335 nsSVGTextContainerFrame::SetWhitespaceHandling()
337 // init children:
338 nsISVGGlyphFragmentNode* node = GetFirstGlyphFragmentChildNode();
339 nsISVGGlyphFragmentNode* next;
341 PRUint8 whitespaceHandling = COMPRESS_WHITESPACE | TRIM_LEADING_WHITESPACE;
343 for (nsIFrame *frame = this; frame != nsnull; frame = frame->GetParent()) {
344 nsIContent *content = frame->GetContent();
345 static nsIContent::AttrValuesArray strings[] =
346 {&nsGkAtoms::preserve, &nsGkAtoms::_default, nsnull};
348 PRInt32 index = content->FindAttrValueIn(kNameSpaceID_XML,
349 nsGkAtoms::space,
350 strings, eCaseMatters);
351 if (index == 0) {
352 whitespaceHandling = PRESERVE_WHITESPACE;
353 break;
355 if (index != nsIContent::ATTR_MISSING ||
356 (frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))
357 break;
360 while (node) {
361 next = GetNextGlyphFragmentChildNode(node);
362 if (!next && (whitespaceHandling & COMPRESS_WHITESPACE)) {
363 whitespaceHandling |= TRIM_TRAILING_WHITESPACE;
365 node->SetWhitespaceHandling(whitespaceHandling);
366 node = next;
367 whitespaceHandling &= ~TRIM_LEADING_WHITESPACE;
371 PRUint32
372 nsSVGTextContainerFrame::GetNumberOfChars()
374 PRUint32 nchars = 0;
375 nsISVGGlyphFragmentNode* node;
376 node = GetFirstGlyphFragmentChildNode();
378 while (node) {
379 nchars += node->GetNumberOfChars();
380 node = GetNextGlyphFragmentChildNode(node);
383 return nchars;
386 float
387 nsSVGTextContainerFrame::GetComputedTextLength()
389 float length = 0.0f;
390 nsISVGGlyphFragmentNode* node = GetFirstGlyphFragmentChildNode();
392 while (node) {
393 length += node->GetComputedTextLength();
394 node = GetNextGlyphFragmentChildNode(node);
397 return length;
400 float
401 nsSVGTextContainerFrame::GetSubStringLengthNoValidation(PRUint32 charnum,
402 PRUint32 nchars)
404 float length = 0.0f;
405 nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
407 while (node) {
408 PRUint32 count = node->GetNumberOfChars();
409 if (count > charnum) {
410 PRUint32 fragmentChars = PR_MIN(nchars, count);
411 float fragmentLength = node->GetSubStringLength(charnum, fragmentChars);
412 length += fragmentLength;
413 nchars -= fragmentChars;
414 if (nchars == 0) break;
416 charnum -= PR_MIN(charnum, count);
417 node = GetNextGlyphFragmentChildNode(node);
420 return length;
423 PRInt32
424 nsSVGTextContainerFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point)
426 PRInt32 index = -1;
427 PRInt32 offset = 0;
428 nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
430 while (node) {
431 PRUint32 count = node->GetNumberOfChars();
432 if (count > 0) {
433 PRInt32 charnum = node->GetCharNumAtPosition(point);
434 if (charnum >= 0) {
435 index = charnum + offset;
437 offset += count;
438 // Keep going, multiple characters may match
439 // and we must return the last one
441 node = GetNextGlyphFragmentChildNode(node);
444 return index;
447 // -------------------------------------------------------------------------
448 // Private functions
449 // -------------------------------------------------------------------------
451 nsISVGGlyphFragmentLeaf *
452 nsSVGTextContainerFrame::GetGlyphFragmentAtCharNum(nsISVGGlyphFragmentNode* node,
453 PRUint32 charnum,
454 PRUint32 *offset)
456 nsISVGGlyphFragmentLeaf *fragment = node->GetFirstGlyphFragment();
457 *offset = 0;
459 while (fragment) {
460 PRUint32 count = fragment->GetNumberOfChars();
461 if (count > charnum)
462 return fragment;
463 charnum -= count;
464 *offset += count;
465 fragment = fragment->GetNextGlyphFragment();
468 // not found
469 return nsnull;
472 nsSVGTextFrame *
473 nsSVGTextContainerFrame::GetTextFrame()
475 for (nsIFrame *frame = this; frame != nsnull; frame = frame->GetParent()) {
476 if (frame->GetType() == nsGkAtoms::svgTextFrame) {
477 return static_cast<nsSVGTextFrame*>(frame);
480 return nsnull;