1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Chris Waterson <waterson@netscape.com>
24 : * David Hyatt <hyatt@netscape.com>
25 : * Brendan Eich <brendan@mozilla.org>
26 : * Mark Hammond <mhammond@skippinet.com.au>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : /*
43 : * An implementation for a Gecko-style content sink that knows how
44 : * to build a content model (the "prototype" document) from XUL.
45 : *
46 : * For more information on XUL,
47 : * see http://developer.mozilla.org/en/docs/XUL
48 : */
49 :
50 : #include "jsapi.h"
51 : #include "jsfriendapi.h"
52 : #include "nsXULContentSink.h"
53 : #include "nsCOMPtr.h"
54 : #include "nsForwardReference.h"
55 : #include "nsIContentSink.h"
56 : #include "nsIDOMDocument.h"
57 : #include "nsIDOMEventListener.h"
58 : #include "nsIDOMHTMLFormElement.h"
59 : #include "nsIDOMXULDocument.h"
60 : #include "nsIDocument.h"
61 : #include "nsIFormControl.h"
62 : #include "nsHTMLStyleSheet.h"
63 : #include "nsINameSpaceManager.h"
64 : #include "nsINodeInfo.h"
65 : #include "nsIParser.h"
66 : #include "nsIScriptContext.h"
67 : #include "nsIScriptRuntime.h"
68 : #include "nsIScriptGlobalObject.h"
69 : #include "nsIServiceManager.h"
70 : #include "nsIURL.h"
71 : #include "nsIViewManager.h"
72 : #include "nsIXULDocument.h"
73 : #include "nsIScriptSecurityManager.h"
74 : #include "nsLayoutCID.h"
75 : #include "nsNetUtil.h"
76 : #include "nsRDFCID.h"
77 : #include "nsXPIDLString.h"
78 : #include "nsReadableUtils.h"
79 : #include "nsXULElement.h"
80 : #include "prlog.h"
81 : #include "prmem.h"
82 : #include "nsCRT.h"
83 :
84 : #include "nsXULPrototypeDocument.h" // XXXbe temporary
85 : #include "mozilla/css/Loader.h"
86 :
87 : #include "nsUnicharUtils.h"
88 : #include "nsGkAtoms.h"
89 : #include "nsContentUtils.h"
90 : #include "nsAttrName.h"
91 : #include "nsXMLContentSink.h"
92 : #include "nsIConsoleService.h"
93 : #include "nsIScriptError.h"
94 :
95 : #ifdef PR_LOGGING
96 : static PRLogModuleInfo* gLog;
97 : #endif
98 :
99 : //----------------------------------------------------------------------
100 :
101 0 : XULContentSinkImpl::ContextStack::ContextStack()
102 0 : : mTop(nsnull), mDepth(0)
103 : {
104 0 : }
105 :
106 0 : XULContentSinkImpl::ContextStack::~ContextStack()
107 : {
108 0 : while (mTop) {
109 0 : Entry* doomed = mTop;
110 0 : mTop = mTop->mNext;
111 0 : delete doomed;
112 : }
113 0 : }
114 :
115 : nsresult
116 0 : XULContentSinkImpl::ContextStack::Push(nsXULPrototypeNode* aNode, State aState)
117 : {
118 0 : Entry* entry = new Entry;
119 0 : if (! entry)
120 0 : return NS_ERROR_OUT_OF_MEMORY;
121 :
122 0 : entry->mNode = aNode;
123 0 : entry->mState = aState;
124 0 : entry->mNext = mTop;
125 0 : mTop = entry;
126 :
127 0 : ++mDepth;
128 0 : return NS_OK;
129 : }
130 :
131 : nsresult
132 0 : XULContentSinkImpl::ContextStack::Pop(State* aState)
133 : {
134 0 : if (mDepth == 0)
135 0 : return NS_ERROR_UNEXPECTED;
136 :
137 0 : Entry* entry = mTop;
138 0 : mTop = mTop->mNext;
139 0 : --mDepth;
140 :
141 0 : *aState = entry->mState;
142 0 : delete entry;
143 :
144 0 : return NS_OK;
145 : }
146 :
147 :
148 : nsresult
149 0 : XULContentSinkImpl::ContextStack::GetTopNode(nsRefPtr<nsXULPrototypeNode>& aNode)
150 : {
151 0 : if (mDepth == 0)
152 0 : return NS_ERROR_UNEXPECTED;
153 :
154 0 : aNode = mTop->mNode;
155 0 : return NS_OK;
156 : }
157 :
158 :
159 : nsresult
160 0 : XULContentSinkImpl::ContextStack::GetTopChildren(nsPrototypeArray** aChildren)
161 : {
162 0 : if (mDepth == 0)
163 0 : return NS_ERROR_UNEXPECTED;
164 :
165 0 : *aChildren = &(mTop->mChildren);
166 0 : return NS_OK;
167 : }
168 :
169 : nsresult
170 0 : XULContentSinkImpl::ContextStack::GetTopNodeScriptType(PRUint32 *aScriptType)
171 : {
172 0 : if (mDepth == 0)
173 0 : return NS_ERROR_UNEXPECTED;
174 :
175 : // This would be much simpler if nsXULPrototypeNode itself
176 : // stored the language ID - but text elements don't need it!
177 0 : nsresult rv = NS_OK;
178 0 : nsRefPtr<nsXULPrototypeNode> node;
179 0 : rv = GetTopNode(node);
180 0 : if (NS_FAILED(rv)) return rv;
181 0 : switch (node->mType) {
182 : case nsXULPrototypeNode::eType_Element: {
183 : nsXULPrototypeElement *parent =
184 0 : reinterpret_cast<nsXULPrototypeElement*>(node.get());
185 0 : *aScriptType = parent->mScriptTypeID;
186 0 : break;
187 : }
188 : case nsXULPrototypeNode::eType_Script: {
189 : nsXULPrototypeScript *parent =
190 0 : reinterpret_cast<nsXULPrototypeScript*>(node.get());
191 0 : *aScriptType = parent->mScriptObject.mLangID;
192 0 : break;
193 : }
194 : default: {
195 0 : NS_WARNING("Unexpected parent node type");
196 0 : rv = NS_ERROR_UNEXPECTED;
197 : }
198 : }
199 0 : return rv;
200 : }
201 :
202 : void
203 0 : XULContentSinkImpl::ContextStack::Clear()
204 : {
205 0 : Entry *cur = mTop;
206 0 : while (cur) {
207 : // Release the root element (and its descendants).
208 0 : Entry *next = cur->mNext;
209 0 : delete cur;
210 0 : cur = next;
211 : }
212 :
213 0 : mTop = nsnull;
214 0 : mDepth = 0;
215 0 : }
216 :
217 : //----------------------------------------------------------------------
218 :
219 :
220 0 : XULContentSinkImpl::XULContentSinkImpl()
221 : : mText(nsnull),
222 : mTextLength(0),
223 : mTextSize(0),
224 : mConstrainSize(true),
225 : mState(eInProlog),
226 0 : mParser(nsnull)
227 : {
228 :
229 : #ifdef PR_LOGGING
230 0 : if (! gLog)
231 0 : gLog = PR_NewLogModule("nsXULContentSink");
232 : #endif
233 0 : }
234 :
235 :
236 0 : XULContentSinkImpl::~XULContentSinkImpl()
237 : {
238 0 : NS_IF_RELEASE(mParser); // XXX should've been released by now, unless error.
239 :
240 : // The context stack _should_ be empty, unless something has gone wrong.
241 0 : NS_ASSERTION(mContextStack.Depth() == 0, "Context stack not empty?");
242 0 : mContextStack.Clear();
243 :
244 0 : PR_FREEIF(mText);
245 0 : }
246 :
247 : //----------------------------------------------------------------------
248 : // nsISupports interface
249 :
250 0 : NS_IMPL_ISUPPORTS3(XULContentSinkImpl,
251 : nsIXMLContentSink,
252 : nsIContentSink,
253 : nsIExpatSink)
254 :
255 : //----------------------------------------------------------------------
256 : // nsIContentSink interface
257 :
258 : NS_IMETHODIMP
259 0 : XULContentSinkImpl::WillBuildModel(nsDTDMode aDTDMode)
260 : {
261 : #if FIXME
262 : if (! mParentContentSink) {
263 : // If we're _not_ an overlay, then notify the document that
264 : // the load is beginning.
265 : mDocument->BeginLoad();
266 : }
267 : #endif
268 :
269 0 : return NS_OK;
270 : }
271 :
272 : NS_IMETHODIMP
273 0 : XULContentSinkImpl::DidBuildModel(bool aTerminated)
274 : {
275 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
276 0 : if (doc) {
277 0 : doc->EndLoad();
278 0 : mDocument = nsnull;
279 : }
280 :
281 : // Drop our reference to the parser to get rid of a circular
282 : // reference.
283 0 : NS_IF_RELEASE(mParser);
284 0 : return NS_OK;
285 : }
286 :
287 : NS_IMETHODIMP
288 0 : XULContentSinkImpl::WillInterrupt(void)
289 : {
290 : // XXX Notify the docshell, if necessary
291 0 : return NS_OK;
292 : }
293 :
294 : NS_IMETHODIMP
295 0 : XULContentSinkImpl::WillResume(void)
296 : {
297 : // XXX Notify the docshell, if necessary
298 0 : return NS_OK;
299 : }
300 :
301 : NS_IMETHODIMP
302 0 : XULContentSinkImpl::SetParser(nsParserBase* aParser)
303 : {
304 0 : NS_IF_RELEASE(mParser);
305 0 : mParser = aParser;
306 0 : NS_IF_ADDREF(mParser);
307 0 : return NS_OK;
308 : }
309 :
310 : NS_IMETHODIMP
311 0 : XULContentSinkImpl::SetDocumentCharset(nsACString& aCharset)
312 : {
313 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
314 0 : if (doc) {
315 0 : doc->SetDocumentCharacterSet(aCharset);
316 : }
317 :
318 0 : return NS_OK;
319 : }
320 :
321 : nsISupports *
322 0 : XULContentSinkImpl::GetTarget()
323 : {
324 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
325 0 : return doc;
326 : }
327 :
328 : //----------------------------------------------------------------------
329 :
330 : nsresult
331 0 : XULContentSinkImpl::Init(nsIDocument* aDocument,
332 : nsXULPrototypeDocument* aPrototype)
333 : {
334 0 : NS_PRECONDITION(aDocument != nsnull, "null ptr");
335 0 : if (! aDocument)
336 0 : return NS_ERROR_NULL_POINTER;
337 :
338 : nsresult rv;
339 :
340 0 : mDocument = do_GetWeakReference(aDocument);
341 0 : mPrototype = aPrototype;
342 :
343 0 : mDocumentURL = mPrototype->GetURI();
344 :
345 : // XXX this presumes HTTP header info is already set in document
346 : // XXX if it isn't we need to set it here...
347 : // XXXbz not like GetHeaderData on the proto doc _does_ anything....
348 0 : nsAutoString preferredStyle;
349 0 : rv = mPrototype->GetHeaderData(nsGkAtoms::headerDefaultStyle,
350 0 : preferredStyle);
351 0 : if (NS_FAILED(rv)) return rv;
352 :
353 0 : if (!preferredStyle.IsEmpty()) {
354 : aDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle,
355 0 : preferredStyle);
356 : }
357 :
358 : // Set the right preferred style on the document's CSSLoader.
359 0 : aDocument->CSSLoader()->SetPreferredSheet(preferredStyle);
360 :
361 0 : mNodeInfoManager = aPrototype->GetNodeInfoManager();
362 0 : if (! mNodeInfoManager)
363 0 : return NS_ERROR_UNEXPECTED;
364 :
365 0 : mState = eInProlog;
366 0 : return NS_OK;
367 : }
368 :
369 :
370 : //----------------------------------------------------------------------
371 : //
372 : // Text buffering
373 : //
374 :
375 : bool
376 0 : XULContentSinkImpl::IsDataInBuffer(PRUnichar* buffer, PRInt32 length)
377 : {
378 0 : for (PRInt32 i = 0; i < length; ++i) {
379 0 : if (buffer[i] == ' ' ||
380 0 : buffer[i] == '\t' ||
381 0 : buffer[i] == '\n' ||
382 0 : buffer[i] == '\r')
383 0 : continue;
384 :
385 0 : return true;
386 : }
387 0 : return false;
388 : }
389 :
390 :
391 : nsresult
392 0 : XULContentSinkImpl::FlushText(bool aCreateTextNode)
393 : {
394 : nsresult rv;
395 :
396 : do {
397 : // Don't do anything if there's no text to create a node from, or
398 : // if they've told us not to create a text node
399 0 : if (! mTextLength)
400 0 : break;
401 :
402 0 : if (! aCreateTextNode)
403 0 : break;
404 :
405 0 : nsRefPtr<nsXULPrototypeNode> node;
406 0 : rv = mContextStack.GetTopNode(node);
407 0 : if (NS_FAILED(rv)) return rv;
408 :
409 0 : bool stripWhitespace = false;
410 0 : if (node->mType == nsXULPrototypeNode::eType_Element) {
411 : nsINodeInfo *nodeInfo =
412 0 : static_cast<nsXULPrototypeElement*>(node.get())->mNodeInfo;
413 :
414 0 : if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL))
415 0 : stripWhitespace = !nodeInfo->Equals(nsGkAtoms::label) &&
416 0 : !nodeInfo->Equals(nsGkAtoms::description);
417 : }
418 :
419 : // Don't bother if there's nothing but whitespace.
420 0 : if (stripWhitespace && ! IsDataInBuffer(mText, mTextLength))
421 : break;
422 :
423 : // Don't bother if we're not in XUL document body
424 0 : if (mState != eInDocumentElement || mContextStack.Depth() == 0)
425 : break;
426 :
427 0 : nsXULPrototypeText* text = new nsXULPrototypeText();
428 0 : if (! text)
429 0 : return NS_ERROR_OUT_OF_MEMORY;
430 :
431 0 : text->mValue.Assign(mText, mTextLength);
432 0 : if (stripWhitespace)
433 0 : text->mValue.Trim(" \t\n\r");
434 :
435 : // hook it up
436 0 : nsPrototypeArray* children = nsnull;
437 0 : rv = mContextStack.GetTopChildren(&children);
438 0 : if (NS_FAILED(rv)) return rv;
439 :
440 : // transfer ownership of 'text' to the children array
441 0 : children->AppendElement(text);
442 : } while (0);
443 :
444 : // Reset our text buffer
445 0 : mTextLength = 0;
446 0 : return NS_OK;
447 : }
448 :
449 : //----------------------------------------------------------------------
450 :
451 : nsresult
452 0 : XULContentSinkImpl::NormalizeAttributeString(const PRUnichar *aExpatName,
453 : nsAttrName &aName)
454 : {
455 : PRInt32 nameSpaceID;
456 0 : nsCOMPtr<nsIAtom> prefix, localName;
457 0 : nsContentUtils::SplitExpatName(aExpatName, getter_AddRefs(prefix),
458 0 : getter_AddRefs(localName), &nameSpaceID);
459 :
460 0 : if (nameSpaceID == kNameSpaceID_None) {
461 0 : aName.SetTo(localName);
462 :
463 0 : return NS_OK;
464 : }
465 :
466 0 : nsCOMPtr<nsINodeInfo> ni;
467 : ni = mNodeInfoManager->GetNodeInfo(localName, prefix,
468 : nameSpaceID,
469 0 : nsIDOMNode::ATTRIBUTE_NODE);
470 0 : NS_ENSURE_TRUE(ni, NS_ERROR_OUT_OF_MEMORY);
471 :
472 0 : aName.SetTo(ni);
473 :
474 0 : return NS_OK;
475 : }
476 :
477 : nsresult
478 0 : XULContentSinkImpl::CreateElement(nsINodeInfo *aNodeInfo,
479 : nsXULPrototypeElement** aResult)
480 : {
481 0 : nsXULPrototypeElement* element = new nsXULPrototypeElement();
482 0 : if (! element)
483 0 : return NS_ERROR_OUT_OF_MEMORY;
484 :
485 0 : element->mNodeInfo = aNodeInfo;
486 :
487 0 : *aResult = element;
488 0 : return NS_OK;
489 : }
490 :
491 : /**** BEGIN NEW APIs ****/
492 :
493 :
494 : NS_IMETHODIMP
495 0 : XULContentSinkImpl::HandleStartElement(const PRUnichar *aName,
496 : const PRUnichar **aAtts,
497 : PRUint32 aAttsCount,
498 : PRInt32 aIndex,
499 : PRUint32 aLineNumber)
500 : {
501 : // XXX Hopefully the parser will flag this before we get here. If
502 : // we're in the epilog, there should be no new elements
503 0 : NS_PRECONDITION(mState != eInEpilog, "tag in XUL doc epilog");
504 0 : NS_PRECONDITION(aIndex >= -1, "Bogus aIndex");
505 0 : NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount");
506 : // Adjust aAttsCount so it's the actual number of attributes
507 0 : aAttsCount /= 2;
508 :
509 0 : if (mState == eInEpilog)
510 0 : return NS_ERROR_UNEXPECTED;
511 :
512 0 : if (mState != eInScript) {
513 0 : FlushText();
514 : }
515 :
516 : PRInt32 nameSpaceID;
517 0 : nsCOMPtr<nsIAtom> prefix, localName;
518 0 : nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
519 0 : getter_AddRefs(localName), &nameSpaceID);
520 :
521 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
522 : nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
523 0 : nsIDOMNode::ELEMENT_NODE);
524 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
525 :
526 0 : nsresult rv = NS_OK;
527 0 : switch (mState) {
528 : case eInProlog:
529 : // We're the root document element
530 0 : rv = OpenRoot(aAtts, aAttsCount, nodeInfo);
531 0 : break;
532 :
533 : case eInDocumentElement:
534 0 : rv = OpenTag(aAtts, aAttsCount, aLineNumber, nodeInfo);
535 0 : break;
536 :
537 : case eInEpilog:
538 : case eInScript:
539 0 : PR_LOG(gLog, PR_LOG_WARNING,
540 : ("xul: warning: unexpected tags in epilog at line %d",
541 : aLineNumber));
542 0 : rv = NS_ERROR_UNEXPECTED; // XXX
543 0 : break;
544 : }
545 :
546 : // Set the ID attribute atom on the node info object for this node
547 0 : if (aIndex != -1 && NS_SUCCEEDED(rv)) {
548 0 : nsCOMPtr<nsIAtom> IDAttr = do_GetAtom(aAtts[aIndex]);
549 :
550 0 : if (IDAttr) {
551 0 : nodeInfo->SetIDAttributeAtom(IDAttr);
552 : }
553 : }
554 :
555 0 : return rv;
556 : }
557 :
558 : NS_IMETHODIMP
559 0 : XULContentSinkImpl::HandleEndElement(const PRUnichar *aName)
560 : {
561 : // Never EVER return anything but NS_OK or
562 : // NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow
563 : // the parser's little mind all over the planet.
564 : nsresult rv;
565 :
566 0 : nsRefPtr<nsXULPrototypeNode> node;
567 0 : rv = mContextStack.GetTopNode(node);
568 :
569 0 : if (NS_FAILED(rv)) {
570 0 : return NS_OK;
571 : }
572 :
573 0 : switch (node->mType) {
574 : case nsXULPrototypeNode::eType_Element: {
575 : // Flush any text _now_, so that we'll get text nodes created
576 : // before popping the stack.
577 0 : FlushText();
578 :
579 : // Pop the context stack and do prototype hookup.
580 0 : nsPrototypeArray* children = nsnull;
581 0 : rv = mContextStack.GetTopChildren(&children);
582 0 : if (NS_FAILED(rv)) return rv;
583 :
584 : nsXULPrototypeElement* element =
585 0 : static_cast<nsXULPrototypeElement*>(node.get());
586 :
587 0 : PRInt32 count = children->Length();
588 0 : if (count) {
589 0 : if (!element->mChildren.SetCapacity(count))
590 0 : return NS_ERROR_OUT_OF_MEMORY;
591 :
592 0 : for (PRInt32 i = 0; i < count; ++i)
593 0 : element->mChildren.AppendElement(children->ElementAt(i));
594 :
595 : }
596 : }
597 0 : break;
598 :
599 : case nsXULPrototypeNode::eType_Script: {
600 : nsXULPrototypeScript* script =
601 0 : static_cast<nsXULPrototypeScript*>(node.get());
602 :
603 : // If given a src= attribute, we must ignore script tag content.
604 0 : if (! script->mSrcURI && ! script->mScriptObject.mObject) {
605 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
606 :
607 0 : script->mOutOfLine = false;
608 0 : if (doc)
609 : script->Compile(mText, mTextLength, mDocumentURL,
610 0 : script->mLineNo, doc, mPrototype);
611 : }
612 :
613 0 : FlushText(false);
614 : }
615 0 : break;
616 :
617 : default:
618 0 : NS_ERROR("didn't expect that");
619 0 : break;
620 : }
621 :
622 0 : rv = mContextStack.Pop(&mState);
623 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "context stack corrupted");
624 0 : if (NS_FAILED(rv)) return rv;
625 :
626 0 : if (mContextStack.Depth() == 0) {
627 : // The root element should -always- be an element, because
628 : // it'll have been created via XULContentSinkImpl::OpenRoot().
629 0 : NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element, "root is not an element");
630 0 : if (node->mType != nsXULPrototypeNode::eType_Element)
631 0 : return NS_ERROR_UNEXPECTED;
632 :
633 : // Now that we're done parsing, set the prototype document's
634 : // root element. This transfers ownership of the prototype
635 : // element tree to the prototype document.
636 : nsXULPrototypeElement* element =
637 0 : static_cast<nsXULPrototypeElement*>(node.get());
638 :
639 0 : mPrototype->SetRootElement(element);
640 0 : mState = eInEpilog;
641 : }
642 :
643 0 : return NS_OK;
644 : }
645 :
646 : NS_IMETHODIMP
647 0 : XULContentSinkImpl::HandleComment(const PRUnichar *aName)
648 : {
649 0 : FlushText();
650 0 : return NS_OK;
651 : }
652 :
653 : NS_IMETHODIMP
654 0 : XULContentSinkImpl::HandleCDataSection(const PRUnichar *aData, PRUint32 aLength)
655 : {
656 0 : FlushText();
657 0 : return AddText(aData, aLength);
658 : }
659 :
660 : NS_IMETHODIMP
661 0 : XULContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset,
662 : const nsAString & aName,
663 : const nsAString & aSystemId,
664 : const nsAString & aPublicId,
665 : nsISupports* aCatalogData)
666 : {
667 0 : return NS_OK;
668 : }
669 :
670 : NS_IMETHODIMP
671 0 : XULContentSinkImpl::HandleCharacterData(const PRUnichar *aData,
672 : PRUint32 aLength)
673 : {
674 0 : if (aData && mState != eInProlog && mState != eInEpilog) {
675 0 : return AddText(aData, aLength);
676 : }
677 0 : return NS_OK;
678 : }
679 :
680 : NS_IMETHODIMP
681 0 : XULContentSinkImpl::HandleProcessingInstruction(const PRUnichar *aTarget,
682 : const PRUnichar *aData)
683 : {
684 0 : FlushText();
685 :
686 0 : const nsDependentString target(aTarget);
687 0 : const nsDependentString data(aData);
688 :
689 : // Note: the created nsXULPrototypePI has mRefCnt == 1
690 0 : nsRefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
691 0 : if (!pi)
692 0 : return NS_ERROR_OUT_OF_MEMORY;
693 :
694 0 : pi->mTarget = target;
695 0 : pi->mData = data;
696 :
697 0 : if (mState == eInProlog) {
698 : // Note: passing in already addrefed pi
699 0 : return mPrototype->AddProcessingInstruction(pi);
700 : }
701 :
702 : nsresult rv;
703 0 : nsPrototypeArray* children = nsnull;
704 0 : rv = mContextStack.GetTopChildren(&children);
705 0 : if (NS_FAILED(rv)) {
706 0 : return rv;
707 : }
708 :
709 0 : if (!children->AppendElement(pi)) {
710 0 : return NS_ERROR_OUT_OF_MEMORY;
711 : }
712 :
713 0 : return NS_OK;
714 : }
715 :
716 :
717 : NS_IMETHODIMP
718 0 : XULContentSinkImpl::HandleXMLDeclaration(const PRUnichar *aVersion,
719 : const PRUnichar *aEncoding,
720 : PRInt32 aStandalone)
721 : {
722 0 : return NS_OK;
723 : }
724 :
725 :
726 : NS_IMETHODIMP
727 0 : XULContentSinkImpl::ReportError(const PRUnichar* aErrorText,
728 : const PRUnichar* aSourceText,
729 : nsIScriptError *aError,
730 : bool *_retval)
731 : {
732 0 : NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
733 :
734 : // The expat driver should report the error.
735 0 : *_retval = true;
736 :
737 0 : nsresult rv = NS_OK;
738 :
739 : // make sure to empty the context stack so that
740 : // <parsererror> could become the root element.
741 0 : mContextStack.Clear();
742 :
743 0 : mState = eInProlog;
744 :
745 : // Clear any buffered-up text we have. It's enough to set the length to 0.
746 : // The buffer itself is allocated when we're created and deleted in our
747 : // destructor, so don't mess with it.
748 0 : mTextLength = 0;
749 :
750 0 : nsCOMPtr<nsIXULDocument> doc = do_QueryReferent(mDocument);
751 0 : if (doc && !doc->OnDocumentParserError()) {
752 : // The overlay was broken. Don't add a messy element to the master doc.
753 0 : return NS_OK;
754 : }
755 :
756 0 : const PRUnichar* noAtts[] = { 0, 0 };
757 :
758 0 : NS_NAMED_LITERAL_STRING(errorNs,
759 : "http://www.mozilla.org/newlayout/xml/parsererror.xml");
760 :
761 0 : nsAutoString parsererror(errorNs);
762 0 : parsererror.Append((PRUnichar)0xFFFF);
763 0 : parsererror.AppendLiteral("parsererror");
764 :
765 0 : rv = HandleStartElement(parsererror.get(), noAtts, 0, -1, 0);
766 0 : NS_ENSURE_SUCCESS(rv,rv);
767 :
768 0 : rv = HandleCharacterData(aErrorText, nsCRT::strlen(aErrorText));
769 0 : NS_ENSURE_SUCCESS(rv,rv);
770 :
771 0 : nsAutoString sourcetext(errorNs);
772 0 : sourcetext.Append((PRUnichar)0xFFFF);
773 0 : sourcetext.AppendLiteral("sourcetext");
774 :
775 0 : rv = HandleStartElement(sourcetext.get(), noAtts, 0, -1, 0);
776 0 : NS_ENSURE_SUCCESS(rv,rv);
777 :
778 0 : rv = HandleCharacterData(aSourceText, nsCRT::strlen(aSourceText));
779 0 : NS_ENSURE_SUCCESS(rv,rv);
780 :
781 0 : rv = HandleEndElement(sourcetext.get());
782 0 : NS_ENSURE_SUCCESS(rv,rv);
783 :
784 0 : rv = HandleEndElement(parsererror.get());
785 0 : NS_ENSURE_SUCCESS(rv,rv);
786 :
787 0 : return rv;
788 : }
789 :
790 : nsresult
791 0 : XULContentSinkImpl::SetElementScriptType(nsXULPrototypeElement* element,
792 : const PRUnichar** aAttributes,
793 : const PRUint32 aAttrLen)
794 : {
795 : // First check if the attributes specify an explicit script type.
796 0 : nsresult rv = NS_OK;
797 : PRUint32 i;
798 0 : bool found = false;
799 0 : for (i=0;i<aAttrLen;i++) {
800 0 : const nsDependentString key(aAttributes[i*2]);
801 0 : if (key.EqualsLiteral("script-type")) {
802 0 : const nsDependentString value(aAttributes[i*2+1]);
803 0 : if (!value.IsEmpty()) {
804 0 : nsCOMPtr<nsIScriptRuntime> runtime;
805 0 : rv = NS_GetScriptRuntime(value, getter_AddRefs(runtime));
806 0 : if (NS_SUCCEEDED(rv))
807 0 : element->mScriptTypeID = runtime->GetScriptTypeID();
808 : else {
809 : // probably just a bad language name (typo, etc)
810 0 : NS_WARNING("Failed to load the node's script language!");
811 : // Leave the default language as unknown - we don't want js
812 : // trying to execute this stuff.
813 0 : NS_ASSERTION(element->mScriptTypeID == nsIProgrammingLanguage::UNKNOWN,
814 : "Default script type should be unknown");
815 : }
816 0 : found = true;
817 : break;
818 : }
819 : }
820 : }
821 : // If not specified, look at the context stack and use the element
822 : // there.
823 0 : if (!found) {
824 0 : if (mContextStack.Depth() == 0) {
825 : // This is the root element - default to JS
826 0 : element->mScriptTypeID = nsIProgrammingLanguage::JAVASCRIPT;
827 : } else {
828 : // Ask the top-node for its script type (which has already
829 : // had this function called for it - so no need to recurse
830 : // until we find it)
831 0 : PRUint32 scriptId = 0;
832 0 : rv = mContextStack.GetTopNodeScriptType(&scriptId);
833 0 : element->mScriptTypeID = scriptId;
834 : }
835 : }
836 0 : return rv;
837 : }
838 :
839 : nsresult
840 0 : XULContentSinkImpl::OpenRoot(const PRUnichar** aAttributes,
841 : const PRUint32 aAttrLen,
842 : nsINodeInfo *aNodeInfo)
843 : {
844 0 : NS_ASSERTION(mState == eInProlog, "how'd we get here?");
845 0 : if (mState != eInProlog)
846 0 : return NS_ERROR_UNEXPECTED;
847 :
848 : nsresult rv;
849 :
850 0 : if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
851 0 : aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
852 0 : PR_LOG(gLog, PR_LOG_ERROR,
853 : ("xul: script tag not allowed as root content element"));
854 :
855 0 : return NS_ERROR_UNEXPECTED;
856 : }
857 :
858 : // Create the element
859 : nsXULPrototypeElement* element;
860 0 : rv = CreateElement(aNodeInfo, &element);
861 :
862 0 : if (NS_FAILED(rv)) {
863 : #ifdef PR_LOGGING
864 0 : if (PR_LOG_TEST(gLog, PR_LOG_ERROR)) {
865 0 : nsAutoString anodeC;
866 0 : aNodeInfo->GetName(anodeC);
867 0 : PR_LOG(gLog, PR_LOG_ERROR,
868 : ("xul: unable to create element '%s' at line %d",
869 : NS_ConvertUTF16toUTF8(anodeC).get(),
870 : -1)); // XXX pass in line number
871 : }
872 : #endif
873 :
874 0 : return rv;
875 : }
876 :
877 : // Set the correct script-type for the element.
878 0 : rv = SetElementScriptType(element, aAttributes, aAttrLen);
879 0 : if (NS_FAILED(rv)) return rv;
880 :
881 : // Push the element onto the context stack, so that child
882 : // containers will hook up to us as their parent.
883 0 : rv = mContextStack.Push(element, mState);
884 0 : if (NS_FAILED(rv)) {
885 0 : element->Release();
886 0 : return rv;
887 : }
888 :
889 : // Add the attributes
890 0 : rv = AddAttributes(aAttributes, aAttrLen, element);
891 0 : if (NS_FAILED(rv)) return rv;
892 :
893 0 : mState = eInDocumentElement;
894 0 : return NS_OK;
895 : }
896 :
897 : nsresult
898 0 : XULContentSinkImpl::OpenTag(const PRUnichar** aAttributes,
899 : const PRUint32 aAttrLen,
900 : const PRUint32 aLineNumber,
901 : nsINodeInfo *aNodeInfo)
902 : {
903 : nsresult rv;
904 :
905 : // Create the element
906 : nsXULPrototypeElement* element;
907 0 : rv = CreateElement(aNodeInfo, &element);
908 :
909 0 : if (NS_FAILED(rv)) {
910 : #ifdef PR_LOGGING
911 0 : if (PR_LOG_TEST(gLog, PR_LOG_ERROR)) {
912 0 : nsAutoString anodeC;
913 0 : aNodeInfo->GetName(anodeC);
914 0 : PR_LOG(gLog, PR_LOG_ERROR,
915 : ("xul: unable to create element '%s' at line %d",
916 : NS_ConvertUTF16toUTF8(anodeC).get(),
917 : aLineNumber));
918 : }
919 : #endif
920 :
921 0 : return rv;
922 : }
923 :
924 : // Link this element to its parent.
925 0 : nsPrototypeArray* children = nsnull;
926 0 : rv = mContextStack.GetTopChildren(&children);
927 0 : if (NS_FAILED(rv)) {
928 0 : delete element;
929 0 : return rv;
930 : }
931 :
932 : // Add the attributes
933 0 : rv = AddAttributes(aAttributes, aAttrLen, element);
934 0 : if (NS_FAILED(rv)) return rv;
935 :
936 0 : children->AppendElement(element);
937 :
938 0 : if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
939 0 : aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
940 : // Do scripty things now. Set a script language for the element,
941 : // even though it is ignored (the nsPrototypeScriptElement
942 : // has its own script-type).
943 0 : element->mScriptTypeID = nsIProgrammingLanguage::JAVASCRIPT;
944 0 : rv = OpenScript(aAttributes, aLineNumber);
945 0 : NS_ENSURE_SUCCESS(rv, rv);
946 :
947 0 : NS_ASSERTION(mState == eInScript || mState == eInDocumentElement,
948 : "Unexpected state");
949 0 : if (mState == eInScript) {
950 : // OpenScript has pushed the nsPrototypeScriptElement onto the
951 : // stack, so we're done.
952 0 : return NS_OK;
953 : }
954 : }
955 :
956 : // Set the correct script-type for the element.
957 0 : rv = SetElementScriptType(element, aAttributes, aAttrLen);
958 0 : if (NS_FAILED(rv)) return rv;
959 :
960 : // Push the element onto the context stack, so that child
961 : // containers will hook up to us as their parent.
962 0 : rv = mContextStack.Push(element, mState);
963 0 : if (NS_FAILED(rv)) return rv;
964 :
965 0 : mState = eInDocumentElement;
966 0 : return NS_OK;
967 : }
968 :
969 : nsresult
970 0 : XULContentSinkImpl::OpenScript(const PRUnichar** aAttributes,
971 : const PRUint32 aLineNumber)
972 : {
973 : PRUint32 langID;
974 0 : nsresult rv = mContextStack.GetTopNodeScriptType(&langID);
975 0 : if (NS_FAILED(rv)) return rv;
976 0 : PRUint32 version = 0;
977 :
978 : // Look for SRC attribute and look for a LANGUAGE attribute
979 0 : nsAutoString src;
980 0 : while (*aAttributes) {
981 0 : const nsDependentString key(aAttributes[0]);
982 0 : if (key.EqualsLiteral("src")) {
983 0 : src.Assign(aAttributes[1]);
984 : }
985 0 : else if (key.EqualsLiteral("type")) {
986 0 : nsDependentString str(aAttributes[1]);
987 0 : nsContentTypeParser parser(str);
988 0 : nsAutoString mimeType;
989 0 : rv = parser.GetType(mimeType);
990 0 : if (NS_FAILED(rv)) {
991 0 : if (rv == NS_ERROR_INVALID_ARG) {
992 : // Might as well bail out now instead of setting langID to
993 : // nsIProgrammingLanguage::UNKNOWN and bailing out later.
994 0 : return NS_OK;
995 : }
996 : // We do want the warning here
997 0 : NS_ENSURE_SUCCESS(rv, rv);
998 : }
999 :
1000 : // Javascript keeps the fast path, optimized for most-likely type
1001 : // Table ordered from most to least likely JS MIME types. For .xul
1002 : // files that we host, the likeliest type is application/x-javascript.
1003 : // See bug 62485, feel free to add <script type="..."> survey data to it,
1004 : // or to a new bug once 62485 is closed.
1005 : static const char *jsTypes[] = {
1006 : "application/x-javascript",
1007 : "text/javascript",
1008 : "text/ecmascript",
1009 : "application/javascript",
1010 : "application/ecmascript",
1011 : nsnull
1012 : };
1013 :
1014 0 : bool isJavaScript = false;
1015 0 : for (PRInt32 i = 0; jsTypes[i]; i++) {
1016 0 : if (mimeType.LowerCaseEqualsASCII(jsTypes[i])) {
1017 0 : isJavaScript = true;
1018 0 : break;
1019 : }
1020 : }
1021 :
1022 0 : if (isJavaScript) {
1023 0 : langID = nsIProgrammingLanguage::JAVASCRIPT;
1024 0 : version = JSVERSION_LATEST;
1025 : } else {
1026 : // Use the script object factory to locate the language.
1027 0 : nsCOMPtr<nsIScriptRuntime> runtime;
1028 0 : rv = NS_GetScriptRuntime(mimeType, getter_AddRefs(runtime));
1029 0 : if (NS_FAILED(rv) || runtime == nsnull) {
1030 : // Failed to get the explicitly specified language
1031 0 : NS_WARNING("Failed to find a scripting language");
1032 0 : langID = nsIProgrammingLanguage::UNKNOWN;
1033 : } else
1034 0 : langID = runtime->GetScriptTypeID();
1035 : }
1036 :
1037 0 : if (langID != nsIProgrammingLanguage::UNKNOWN) {
1038 : // Get the version string, and ensure the language supports it.
1039 0 : nsAutoString versionName;
1040 0 : rv = parser.GetParameter("version", versionName);
1041 0 : if (NS_FAILED(rv)) {
1042 0 : if (rv != NS_ERROR_INVALID_ARG)
1043 0 : return rv;
1044 : // no version specified - version remains the default.
1045 : } else {
1046 0 : nsCOMPtr<nsIScriptRuntime> runtime;
1047 0 : rv = NS_GetScriptRuntimeByID(langID, getter_AddRefs(runtime));
1048 0 : if (NS_FAILED(rv))
1049 0 : return rv;
1050 0 : rv = runtime->ParseVersion(versionName, &version);
1051 0 : if (NS_FAILED(rv)) {
1052 0 : NS_WARNING("This script language version is not supported - ignored");
1053 0 : langID = nsIProgrammingLanguage::UNKNOWN;
1054 : }
1055 : }
1056 : }
1057 : // Some js specifics yet to be abstracted.
1058 0 : if (langID == nsIProgrammingLanguage::JAVASCRIPT) {
1059 : // By default scripts in XUL documents have E4X turned on. We use
1060 : // our implementation knowledge to reuse JSVERSION_HAS_XML as a
1061 : // safe version flag. This is still OK if version is
1062 : // JSVERSION_UNKNOWN (-1),
1063 0 : version = js::VersionSetXML(JSVersion(version), true);
1064 :
1065 0 : nsAutoString value;
1066 0 : rv = parser.GetParameter("e4x", value);
1067 0 : if (NS_FAILED(rv)) {
1068 0 : if (rv != NS_ERROR_INVALID_ARG)
1069 0 : return rv;
1070 : } else {
1071 0 : if (value.Length() == 1 && value[0] == '0')
1072 0 : version = js::VersionSetXML(JSVersion(version), false);
1073 : }
1074 : }
1075 : }
1076 0 : else if (key.EqualsLiteral("language")) {
1077 : // Language is deprecated, and the impl in nsScriptLoader ignores the
1078 : // various version strings anyway. So we make no attempt to support
1079 : // languages other than JS for language=
1080 0 : nsAutoString lang(aAttributes[1]);
1081 0 : if (nsContentUtils::IsJavaScriptLanguage(lang, &version)) {
1082 0 : langID = nsIProgrammingLanguage::JAVASCRIPT;
1083 :
1084 : // Even when JS version < 1.6 is specified, E4X is
1085 : // turned on in XUL.
1086 0 : version = js::VersionSetXML(JSVersion(version), true);
1087 : }
1088 : }
1089 0 : aAttributes += 2;
1090 : }
1091 : // Not all script languages have a "sandbox" concept. At time of
1092 : // writing, Python is the only other language, and it does not.
1093 : // For such languages, neither any inline script nor remote script are
1094 : // safe to execute from untrusted sources.
1095 : // So for such languages, we only allow script when the document
1096 : // itself is from chrome. We then don't bother to check the
1097 : // "src=" tag - we trust chrome to do the right thing.
1098 : // (See also similar code in nsScriptLoader.cpp)
1099 0 : nsCOMPtr<nsIDocument> doc(do_QueryReferent(mDocument));
1100 0 : if (langID != nsIProgrammingLanguage::UNKNOWN &&
1101 : langID != nsIProgrammingLanguage::JAVASCRIPT &&
1102 0 : doc && !nsContentUtils::IsChromeDoc(doc)) {
1103 0 : langID = nsIProgrammingLanguage::UNKNOWN;
1104 0 : NS_WARNING("Non JS language called from non chrome - ignored");
1105 : }
1106 : // Don't process scripts that aren't known
1107 0 : if (langID != nsIProgrammingLanguage::UNKNOWN) {
1108 0 : nsIScriptGlobalObject* globalObject = nsnull; // borrowed reference
1109 0 : if (doc)
1110 0 : globalObject = doc->GetScriptGlobalObject();
1111 : nsRefPtr<nsXULPrototypeScript> script =
1112 0 : new nsXULPrototypeScript(langID, aLineNumber, version);
1113 0 : if (! script)
1114 0 : return NS_ERROR_OUT_OF_MEMORY;
1115 :
1116 : // If there is a SRC attribute...
1117 0 : if (! src.IsEmpty()) {
1118 : // Use the SRC attribute value to load the URL
1119 0 : rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nsnull, mDocumentURL);
1120 :
1121 : // Check if this document is allowed to load a script from this source
1122 : // NOTE: if we ever allow scripts added via the DOM to run, we need to
1123 : // add a CheckLoadURI call for that as well.
1124 0 : if (NS_SUCCEEDED(rv)) {
1125 0 : if (!mSecMan)
1126 0 : mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
1127 0 : if (NS_SUCCEEDED(rv)) {
1128 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument, &rv);
1129 :
1130 0 : if (NS_SUCCEEDED(rv)) {
1131 0 : rv = mSecMan->
1132 0 : CheckLoadURIWithPrincipal(doc->NodePrincipal(),
1133 0 : script->mSrcURI,
1134 0 : nsIScriptSecurityManager::ALLOW_CHROME);
1135 : }
1136 : }
1137 : }
1138 :
1139 0 : if (NS_FAILED(rv)) {
1140 0 : return rv;
1141 : }
1142 :
1143 : // Attempt to deserialize an out-of-line script from the FastLoad
1144 : // file right away. Otherwise we'll end up reloading the script and
1145 : // corrupting the FastLoad file trying to serialize it, in the case
1146 : // where it's already there.
1147 0 : if (globalObject)
1148 0 : script->DeserializeOutOfLine(nsnull, globalObject);
1149 : }
1150 :
1151 0 : nsPrototypeArray* children = nsnull;
1152 0 : rv = mContextStack.GetTopChildren(&children);
1153 0 : if (NS_FAILED(rv)) {
1154 0 : return rv;
1155 : }
1156 :
1157 0 : children->AppendElement(script);
1158 :
1159 0 : mConstrainSize = false;
1160 :
1161 0 : mContextStack.Push(script, mState);
1162 0 : mState = eInScript;
1163 : }
1164 :
1165 0 : return NS_OK;
1166 : }
1167 :
1168 : nsresult
1169 0 : XULContentSinkImpl::AddAttributes(const PRUnichar** aAttributes,
1170 : const PRUint32 aAttrLen,
1171 : nsXULPrototypeElement* aElement)
1172 : {
1173 : // Add tag attributes to the element
1174 : nsresult rv;
1175 :
1176 : // Create storage for the attributes
1177 0 : nsXULPrototypeAttribute* attrs = nsnull;
1178 0 : if (aAttrLen > 0) {
1179 0 : attrs = new nsXULPrototypeAttribute[aAttrLen];
1180 0 : if (! attrs)
1181 0 : return NS_ERROR_OUT_OF_MEMORY;
1182 : }
1183 :
1184 0 : aElement->mAttributes = attrs;
1185 0 : aElement->mNumAttributes = aAttrLen;
1186 :
1187 : // Copy the attributes into the prototype
1188 : PRUint32 i;
1189 0 : for (i = 0; i < aAttrLen; ++i) {
1190 0 : rv = NormalizeAttributeString(aAttributes[i * 2], attrs[i].mName);
1191 0 : NS_ENSURE_SUCCESS(rv, rv);
1192 :
1193 0 : rv = aElement->SetAttrAt(i, nsDependentString(aAttributes[i * 2 + 1]),
1194 0 : mDocumentURL);
1195 0 : NS_ENSURE_SUCCESS(rv, rv);
1196 :
1197 : #ifdef PR_LOGGING
1198 0 : if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) {
1199 0 : nsAutoString extraWhiteSpace;
1200 0 : PRInt32 cnt = mContextStack.Depth();
1201 0 : while (--cnt >= 0)
1202 0 : extraWhiteSpace.AppendLiteral(" ");
1203 0 : nsAutoString qnameC,valueC;
1204 0 : qnameC.Assign(aAttributes[0]);
1205 0 : valueC.Assign(aAttributes[1]);
1206 0 : PR_LOG(gLog, PR_LOG_DEBUG,
1207 : ("xul: %.5d. %s %s=%s",
1208 : -1, // XXX pass in line number
1209 : NS_ConvertUTF16toUTF8(extraWhiteSpace).get(),
1210 : NS_ConvertUTF16toUTF8(qnameC).get(),
1211 : NS_ConvertUTF16toUTF8(valueC).get()));
1212 : }
1213 : #endif
1214 : }
1215 :
1216 0 : return NS_OK;
1217 : }
1218 :
1219 : nsresult
1220 0 : XULContentSinkImpl::AddText(const PRUnichar* aText,
1221 : PRInt32 aLength)
1222 : {
1223 : // Create buffer when we first need it
1224 0 : if (0 == mTextSize) {
1225 0 : mText = (PRUnichar *) PR_MALLOC(sizeof(PRUnichar) * 4096);
1226 0 : if (nsnull == mText) {
1227 0 : return NS_ERROR_OUT_OF_MEMORY;
1228 : }
1229 0 : mTextSize = 4096;
1230 : }
1231 :
1232 : // Copy data from string into our buffer; flush buffer when it fills up
1233 0 : PRInt32 offset = 0;
1234 0 : while (0 != aLength) {
1235 0 : PRInt32 amount = mTextSize - mTextLength;
1236 0 : if (amount > aLength) {
1237 0 : amount = aLength;
1238 : }
1239 0 : if (0 == amount) {
1240 0 : if (mConstrainSize) {
1241 0 : nsresult rv = FlushText();
1242 0 : if (NS_OK != rv) {
1243 0 : return rv;
1244 : }
1245 : }
1246 : else {
1247 0 : mTextSize += aLength;
1248 0 : mText = (PRUnichar *) PR_REALLOC(mText, sizeof(PRUnichar) * mTextSize);
1249 0 : if (nsnull == mText) {
1250 0 : return NS_ERROR_OUT_OF_MEMORY;
1251 : }
1252 : }
1253 : }
1254 0 : memcpy(&mText[mTextLength],aText + offset, sizeof(PRUnichar) * amount);
1255 :
1256 0 : mTextLength += amount;
1257 0 : offset += amount;
1258 0 : aLength -= amount;
1259 : }
1260 :
1261 0 : return NS_OK;
1262 : }
|