Bug 570258: Some more atom usage cleanup. r=jst
[mozilla-central.git] / content / html / document / src / nsHTMLFragmentContentSink.cpp
blobf0e54ddb3ecbeb5c5bc008ad96f046b87f4cd32a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 et tw=80: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Robert Sayre <sayrer@gmail.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
39 #include "nsCOMPtr.h"
40 #include "nsIServiceManager.h"
41 #include "nsIFragmentContentSink.h"
42 #include "nsIDTD.h"
43 #include "nsIHTMLContentSink.h"
44 #include "nsIParser.h"
45 #include "nsIParserService.h"
46 #include "nsGkAtoms.h"
47 #include "nsHTMLTokens.h"
48 #include "nsGenericHTMLElement.h"
49 #include "nsIDOMText.h"
50 #include "nsIDOMComment.h"
51 #include "nsIDOMHTMLFormElement.h"
52 #include "nsIDOMDocumentFragment.h"
53 #include "nsTArray.h"
54 #include "nsINameSpaceManager.h"
55 #include "nsIDocument.h"
56 #include "nsINodeInfo.h"
57 #include "prmem.h"
58 #include "nsReadableUtils.h"
59 #include "nsUnicharUtils.h"
60 #include "nsContentUtils.h"
61 #include "nsEscape.h"
62 #include "nsNodeInfoManager.h"
63 #include "nsContentCreatorFunctions.h"
64 #include "nsNetUtil.h"
65 #include "nsIScriptSecurityManager.h"
66 #include "nsContentSink.h"
67 #include "nsTHashtable.h"
68 #include "nsCycleCollectionParticipant.h"
71 // XXX THIS IS TEMPORARY CODE
72 // There's a considerable amount of copied code from the
73 // regular nsHTMLContentSink. All of it will be factored
74 // at some pointe really soon!
77 class nsHTMLFragmentContentSink : public nsIFragmentContentSink,
78 public nsIHTMLContentSink {
79 public:
80 nsHTMLFragmentContentSink(PRBool aAllContent = PR_FALSE);
81 virtual ~nsHTMLFragmentContentSink();
83 // nsISupports
84 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
85 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHTMLFragmentContentSink,
86 nsIContentSink)
88 NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
90 // nsIContentSink
91 NS_IMETHOD WillParse(void) { return NS_OK; }
92 NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode);
93 NS_IMETHOD DidBuildModel(PRBool aTerminated);
94 NS_IMETHOD WillInterrupt(void);
95 NS_IMETHOD WillResume(void);
96 NS_IMETHOD SetParser(nsIParser* aParser);
97 virtual void FlushPendingNotifications(mozFlushType aType) { }
98 NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
99 virtual nsISupports *GetTarget() { return mTargetDocument; }
101 // nsIHTMLContentSink
102 NS_IMETHOD BeginContext(PRInt32 aID);
103 NS_IMETHOD EndContext(PRInt32 aID);
104 NS_IMETHOD OpenHead();
105 NS_IMETHOD IsEnabled(PRInt32 aTag, PRBool* aReturn) {
106 *aReturn = PR_TRUE;
107 return NS_OK;
109 NS_IMETHOD_(PRBool) IsFormOnStack() { return PR_FALSE; }
110 NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
111 NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
112 NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
113 NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
114 NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
115 NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
116 NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
117 NS_IMETHOD AddComment(const nsIParserNode& aNode);
118 NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode);
119 NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode);
121 // nsIFragmentContentSink
122 NS_IMETHOD GetFragment(PRBool aWillOwnFragment,
123 nsIDOMDocumentFragment** aFragment);
124 NS_IMETHOD SetTargetDocument(nsIDocument* aDocument);
125 NS_IMETHOD WillBuildContent();
126 NS_IMETHOD DidBuildContent();
127 NS_IMETHOD IgnoreFirstContainer();
129 nsIContent* GetCurrentContent();
130 PRInt32 PushContent(nsIContent *aContent);
131 nsIContent* PopContent();
133 virtual nsresult AddAttributes(const nsIParserNode& aNode,
134 nsIContent* aContent);
136 nsresult AddText(const nsAString& aString);
137 nsresult FlushText();
139 nsresult Init();
141 PRPackedBool mAllContent;
142 PRPackedBool mProcessing;
143 PRPackedBool mSeenBody;
144 PRPackedBool mIgnoreContainer;
145 PRPackedBool mIgnoreNextCloseHead;
147 nsCOMPtr<nsIContent> mRoot;
148 nsCOMPtr<nsIParser> mParser;
150 nsTArray<nsIContent*>* mContentStack;
152 PRUnichar* mText;
153 PRInt32 mTextLength;
154 PRInt32 mTextSize;
156 nsCOMPtr<nsIDocument> mTargetDocument;
157 nsRefPtr<nsNodeInfoManager> mNodeInfoManager;
159 nsINodeInfo* mNodeInfoCache[NS_HTML_TAG_MAX + 1];
162 static nsresult
163 NewHTMLFragmentContentSinkHelper(PRBool aAllContent, nsIFragmentContentSink** aResult)
165 NS_PRECONDITION(aResult, "Null out ptr");
166 if (nsnull == aResult) {
167 return NS_ERROR_NULL_POINTER;
170 nsHTMLFragmentContentSink* it = new nsHTMLFragmentContentSink(aAllContent);
171 if (nsnull == it) {
172 return NS_ERROR_OUT_OF_MEMORY;
175 NS_ADDREF(*aResult = it);
177 return NS_OK;
180 nsresult
181 NS_NewHTMLFragmentContentSink2(nsIFragmentContentSink** aResult)
183 return NewHTMLFragmentContentSinkHelper(PR_TRUE,aResult);
186 nsresult
187 NS_NewHTMLFragmentContentSink(nsIFragmentContentSink** aResult)
189 return NewHTMLFragmentContentSinkHelper(PR_FALSE,aResult);
192 nsHTMLFragmentContentSink::nsHTMLFragmentContentSink(PRBool aAllContent)
193 : mAllContent(aAllContent),
194 mProcessing(aAllContent),
195 mSeenBody(!aAllContent)
197 // Note: operator new zeros our memory
200 nsHTMLFragmentContentSink::~nsHTMLFragmentContentSink()
202 // Should probably flush the text buffer here, just to make sure:
203 //FlushText();
205 if (nsnull != mContentStack) {
206 // there shouldn't be anything here except in an error condition
207 PRInt32 indx = mContentStack->Length();
208 while (0 < indx--) {
209 nsIContent* content = mContentStack->ElementAt(indx);
210 NS_RELEASE(content);
212 delete mContentStack;
215 PR_FREEIF(mText);
217 PRUint32 i;
218 for (i = 0; i < NS_ARRAY_LENGTH(mNodeInfoCache); ++i) {
219 NS_IF_RELEASE(mNodeInfoCache[i]);
223 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsHTMLFragmentContentSink,
224 nsIContentSink)
225 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsHTMLFragmentContentSink,
226 nsIContentSink)
228 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHTMLFragmentContentSink)
229 NS_INTERFACE_MAP_ENTRY(nsIFragmentContentSink)
230 NS_INTERFACE_MAP_ENTRY(nsIHTMLContentSink)
231 NS_INTERFACE_MAP_ENTRY(nsIContentSink)
232 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentSink)
233 NS_INTERFACE_MAP_END
235 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLFragmentContentSink)
236 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHTMLFragmentContentSink)
237 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParser)
238 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTargetDocument)
239 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
240 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNodeInfoManager)
241 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
242 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHTMLFragmentContentSink)
243 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParser)
244 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTargetDocument)
245 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot)
246 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNodeInfoManager,
247 nsNodeInfoManager)
249 PRUint32 i;
250 for (i = 0; i < NS_ARRAY_LENGTH(tmp->mNodeInfoCache); ++i) {
251 cb.NoteXPCOMChild(tmp->mNodeInfoCache[i]);
254 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
256 NS_IMETHODIMP
257 nsHTMLFragmentContentSink::WillBuildModel(nsDTDMode)
259 if (mRoot) {
260 return NS_OK;
263 NS_ASSERTION(mNodeInfoManager, "Need a nodeinfo manager!");
265 nsCOMPtr<nsIDOMDocumentFragment> frag;
266 nsresult rv = NS_NewDocumentFragment(getter_AddRefs(frag), mNodeInfoManager);
267 NS_ENSURE_SUCCESS(rv, rv);
269 mRoot = do_QueryInterface(frag, &rv);
271 return rv;
274 NS_IMETHODIMP
275 nsHTMLFragmentContentSink::DidBuildModel(PRBool aTerminated)
277 FlushText();
279 // Drop our reference to the parser to get rid of a circular
280 // reference.
281 mParser = nsnull;
283 return NS_OK;
286 NS_IMETHODIMP
287 nsHTMLFragmentContentSink::WillInterrupt(void)
289 return NS_OK;
292 NS_IMETHODIMP
293 nsHTMLFragmentContentSink::WillResume(void)
295 return NS_OK;
298 NS_IMETHODIMP
299 nsHTMLFragmentContentSink::SetParser(nsIParser* aParser)
301 mParser = aParser;
303 return NS_OK;
306 NS_IMETHODIMP
307 nsHTMLFragmentContentSink::BeginContext(PRInt32 aID)
309 return NS_OK;
312 NS_IMETHODIMP
313 nsHTMLFragmentContentSink::EndContext(PRInt32 aID)
315 return NS_OK;
318 NS_IMETHODIMP
319 nsHTMLFragmentContentSink::OpenHead()
321 mIgnoreNextCloseHead = PR_TRUE;
322 return NS_OK;
325 NS_IMETHODIMP
326 nsHTMLFragmentContentSink::OpenContainer(const nsIParserNode& aNode)
328 NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_NOT_INITIALIZED);
330 nsresult result = NS_OK;
332 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
334 if (nodeType == eHTMLTag_html) {
335 return NS_OK;
338 // Ignore repeated BODY elements. The DTD is just sending them
339 // to us for compatibility reasons that don't apply here.
340 if (nodeType == eHTMLTag_body) {
341 if (mSeenBody) {
342 return NS_OK;
344 mSeenBody = PR_TRUE;
347 if (mProcessing && !mIgnoreContainer) {
348 FlushText();
350 nsIContent *content = nsnull;
352 nsCOMPtr<nsINodeInfo> nodeInfo;
354 if (nodeType == eHTMLTag_userdefined) {
355 nsAutoString lower;
356 nsContentUtils::ASCIIToLower(aNode.GetText(), lower);
357 nsCOMPtr<nsIAtom> name = do_GetAtom(lower);
358 nodeInfo = mNodeInfoManager->GetNodeInfo(name,
359 nsnull,
360 kNameSpaceID_XHTML);
361 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
363 else if (mNodeInfoCache[nodeType]) {
364 nodeInfo = mNodeInfoCache[nodeType];
366 else {
367 nsIParserService* parserService = nsContentUtils::GetParserService();
368 if (!parserService)
369 return NS_ERROR_OUT_OF_MEMORY;
371 nsIAtom *name = parserService->HTMLIdToAtomTag(nodeType);
372 NS_ASSERTION(name, "This should not happen!");
374 nodeInfo = mNodeInfoManager->GetNodeInfo(name,
375 nsnull,
376 kNameSpaceID_XHTML);
377 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
379 NS_ADDREF(mNodeInfoCache[nodeType] = nodeInfo);
382 content = CreateHTMLElement(nodeType, nodeInfo, PR_FALSE).get();
383 NS_ENSURE_TRUE(content, NS_ERROR_OUT_OF_MEMORY);
385 result = AddAttributes(aNode, content);
386 if (NS_FAILED(result)) {
387 NS_RELEASE(content);
388 return result;
391 nsIContent *parent = GetCurrentContent();
392 if (!parent) {
393 parent = mRoot;
396 parent->AppendChildTo(content, PR_FALSE);
397 PushContent(content);
399 else if (mProcessing && mIgnoreContainer) {
400 mIgnoreContainer = PR_FALSE;
403 return result;
406 NS_IMETHODIMP
407 nsHTMLFragmentContentSink::CloseContainer(const nsHTMLTag aTag)
409 if (aTag == eHTMLTag_html) {
410 return NS_OK;
412 if (mIgnoreNextCloseHead && aTag == eHTMLTag_head) {
413 mIgnoreNextCloseHead = PR_FALSE;
414 return NS_OK;
417 if (mProcessing && (nsnull != GetCurrentContent())) {
418 nsIContent* content;
419 FlushText();
420 content = PopContent();
421 NS_RELEASE(content);
423 return NS_OK;
426 NS_IMETHODIMP
427 nsHTMLFragmentContentSink::AddLeaf(const nsIParserNode& aNode)
429 NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_NOT_INITIALIZED);
431 nsresult result = NS_OK;
433 switch (aNode.GetTokenType()) {
434 case eToken_start:
436 FlushText();
438 // Create new leaf content object
439 nsRefPtr<nsGenericHTMLElement> content;
440 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
442 nsIParserService* parserService = nsContentUtils::GetParserService();
443 if (!parserService)
444 return NS_ERROR_OUT_OF_MEMORY;
446 nsCOMPtr<nsINodeInfo> nodeInfo;
448 if (nodeType == eHTMLTag_userdefined) {
449 nsAutoString lower;
450 nsContentUtils::ASCIIToLower(aNode.GetText(), lower);
451 nsCOMPtr<nsIAtom> name = do_GetAtom(lower);
452 nodeInfo = mNodeInfoManager->GetNodeInfo(name, nsnull,
453 kNameSpaceID_XHTML);
454 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
456 else if (mNodeInfoCache[nodeType]) {
457 nodeInfo = mNodeInfoCache[nodeType];
459 else {
460 nsIAtom *name = parserService->HTMLIdToAtomTag(nodeType);
461 NS_ASSERTION(name, "This should not happen!");
463 nodeInfo = mNodeInfoManager->GetNodeInfo(name, nsnull,
464 kNameSpaceID_XHTML);
465 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
466 NS_ADDREF(mNodeInfoCache[nodeType] = nodeInfo);
469 content = CreateHTMLElement(nodeType, nodeInfo, PR_FALSE);
470 NS_ENSURE_TRUE(content, NS_ERROR_OUT_OF_MEMORY);
472 result = AddAttributes(aNode, content);
473 NS_ENSURE_SUCCESS(result, result);
475 nsIContent *parent = GetCurrentContent();
476 if (!parent) {
477 parent = mRoot;
480 parent->AppendChildTo(content, PR_FALSE);
482 break;
483 case eToken_text:
484 case eToken_whitespace:
485 case eToken_newline:
486 result = AddText(aNode.GetText());
487 break;
489 case eToken_entity:
491 nsAutoString tmp;
492 PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp);
493 if (unicode < 0) {
494 result = AddText(aNode.GetText());
496 else {
497 result = AddText(tmp);
500 break;
503 return result;
506 NS_IMETHODIMP
507 nsHTMLFragmentContentSink::AddComment(const nsIParserNode& aNode)
509 nsCOMPtr<nsIContent> comment;
510 nsresult result = NS_OK;
512 FlushText();
514 result = NS_NewCommentNode(getter_AddRefs(comment), mNodeInfoManager);
515 if (NS_SUCCEEDED(result)) {
516 comment->SetText(aNode.GetText(), PR_FALSE);
518 nsIContent *parent = GetCurrentContent();
520 if (nsnull == parent) {
521 parent = mRoot;
524 parent->AppendChildTo(comment, PR_FALSE);
527 return NS_OK;
530 NS_IMETHODIMP
531 nsHTMLFragmentContentSink::AddProcessingInstruction(const nsIParserNode& aNode)
533 return NS_OK;
537 * This gets called by the parser when it encounters
538 * a DOCTYPE declaration in the HTML document.
541 NS_IMETHODIMP
542 nsHTMLFragmentContentSink::AddDocTypeDecl(const nsIParserNode& aNode)
544 return NS_OK;
547 NS_IMETHODIMP
548 nsHTMLFragmentContentSink::GetFragment(PRBool aWillOwnFragment,
549 nsIDOMDocumentFragment** aFragment)
551 if (mRoot) {
552 nsresult rv = CallQueryInterface(mRoot, aFragment);
553 if (NS_SUCCEEDED(rv) && aWillOwnFragment) {
554 mRoot = nsnull;
556 return rv;
559 *aFragment = nsnull;
561 return NS_OK;
564 NS_IMETHODIMP
565 nsHTMLFragmentContentSink::SetTargetDocument(nsIDocument* aTargetDocument)
567 NS_ENSURE_ARG_POINTER(aTargetDocument);
569 mTargetDocument = aTargetDocument;
570 mNodeInfoManager = aTargetDocument->NodeInfoManager();
572 return NS_OK;
575 NS_IMETHODIMP
576 nsHTMLFragmentContentSink::WillBuildContent()
578 mProcessing = PR_TRUE;
580 return NS_OK;
583 NS_IMETHODIMP
584 nsHTMLFragmentContentSink::DidBuildContent()
586 if (!mAllContent) {
587 FlushText();
588 DidBuildModel(PR_FALSE); // Release our ref to the parser now.
589 mProcessing = PR_FALSE;
592 return NS_OK;
595 NS_IMETHODIMP
596 nsHTMLFragmentContentSink::IgnoreFirstContainer()
598 mIgnoreContainer = PR_TRUE;
599 return NS_OK;
602 nsIContent*
603 nsHTMLFragmentContentSink::GetCurrentContent()
605 if (nsnull != mContentStack) {
606 PRInt32 indx = mContentStack->Length() - 1;
607 if (indx >= 0)
608 return mContentStack->ElementAt(indx);
610 return nsnull;
613 PRInt32
614 nsHTMLFragmentContentSink::PushContent(nsIContent *aContent)
616 if (nsnull == mContentStack) {
617 mContentStack = new nsTArray<nsIContent*>();
620 mContentStack->AppendElement(aContent);
621 return mContentStack->Length();
624 nsIContent*
625 nsHTMLFragmentContentSink::PopContent()
627 nsIContent* content = nsnull;
628 if (nsnull != mContentStack) {
629 PRInt32 indx = mContentStack->Length() - 1;
630 if (indx >= 0) {
631 content = mContentStack->ElementAt(indx);
632 mContentStack->RemoveElementAt(indx);
635 return content;
638 #define NS_ACCUMULATION_BUFFER_SIZE 4096
640 nsresult
641 nsHTMLFragmentContentSink::AddText(const nsAString& aString)
643 PRInt32 addLen = aString.Length();
644 if (0 == addLen) {
645 return NS_OK;
648 // Create buffer when we first need it
649 if (0 == mTextSize) {
650 mText = (PRUnichar *) PR_MALLOC(sizeof(PRUnichar) * NS_ACCUMULATION_BUFFER_SIZE);
651 if (nsnull == mText) {
652 return NS_ERROR_OUT_OF_MEMORY;
654 mTextSize = NS_ACCUMULATION_BUFFER_SIZE;
657 // Copy data from string into our buffer; flush buffer when it fills up
658 PRInt32 offset = 0;
659 PRBool isLastCharCR = PR_FALSE;
660 while (0 != addLen) {
661 PRInt32 amount = mTextSize - mTextLength;
662 if (amount > addLen) {
663 amount = addLen;
665 if (0 == amount) {
666 nsresult rv = FlushText();
667 if (NS_OK != rv) {
668 return rv;
671 mTextLength +=
672 nsContentUtils::CopyNewlineNormalizedUnicodeTo(aString,
673 offset,
674 &mText[mTextLength],
675 amount,
676 isLastCharCR);
677 offset += amount;
678 addLen -= amount;
681 return NS_OK;
684 nsresult
685 nsHTMLFragmentContentSink::FlushText()
687 if (0 == mTextLength) {
688 return NS_OK;
691 nsCOMPtr<nsIContent> content;
692 nsresult rv = NS_NewTextNode(getter_AddRefs(content), mNodeInfoManager);
693 NS_ENSURE_SUCCESS(rv, rv);
695 // Set the text in the text node
696 content->SetText(mText, mTextLength, PR_FALSE);
698 // Add text to its parent
699 nsIContent *parent = GetCurrentContent();
701 if (!parent) {
702 parent = mRoot;
705 rv = parent->AppendChildTo(content, PR_FALSE);
707 mTextLength = 0;
709 return rv;
712 // XXX Code copied from nsHTMLContentSink. It should be shared.
713 nsresult
714 nsHTMLFragmentContentSink::AddAttributes(const nsIParserNode& aNode,
715 nsIContent* aContent)
717 // Add tag attributes to the content attributes
719 PRInt32 ac = aNode.GetAttributeCount();
721 if (ac == 0) {
722 // No attributes, nothing to do. Do an early return to avoid
723 // constructing the nsAutoString object for nothing.
725 return NS_OK;
728 nsAutoString k;
729 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
731 // The attributes are on the parser node in the order they came in in the
732 // source. What we want to happen if a single attribute is set multiple
733 // times on an element is that the first time should "win". That is, <input
734 // value="foo" value="bar"> should show "foo". So we loop over the
735 // attributes backwards; this ensures that the first attribute in the set
736 // wins. This does mean that we do some extra work in the case when the same
737 // attribute is set multiple times, but we save a HasAttr call in the much
738 // more common case of reasonable HTML.
740 for (PRInt32 i = ac - 1; i >= 0; i--) {
741 // Get lower-cased key
742 nsContentUtils::ASCIIToLower(aNode.GetKeyAt(i), k);
743 nsCOMPtr<nsIAtom> keyAtom = do_GetAtom(k);
745 // Get value and remove mandatory quotes
746 static const char* kWhitespace = "\n\r\t\b";
747 const nsAString& v =
748 nsContentUtils::TrimCharsInSet(kWhitespace, aNode.GetValueAt(i));
750 if (nodeType == eHTMLTag_a && keyAtom == nsGkAtoms::name) {
751 NS_ConvertUTF16toUTF8 cname(v);
752 NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
754 // Add attribute to content
755 aContent->SetAttr(kNameSpaceID_None, keyAtom, uv, PR_FALSE);
756 } else {
757 // Add attribute to content
758 aContent->SetAttr(kNameSpaceID_None, keyAtom, v, PR_FALSE);
762 return NS_OK;
765 // nsHTMLParanoidFragmentSink
767 // Find the whitelist of allowed elements and attributes in
768 // nsContentSink.h We share it with nsHTMLParanoidFragmentSink
770 class nsHTMLParanoidFragmentSink : public nsHTMLFragmentContentSink
772 public:
773 nsHTMLParanoidFragmentSink();
775 static nsresult Init();
776 static void Cleanup();
778 // nsISupports
779 NS_DECL_ISUPPORTS_INHERITED
781 NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
782 NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
783 NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
784 NS_IMETHOD AddComment(const nsIParserNode& aNode);
785 NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode);
787 nsresult AddAttributes(const nsIParserNode& aNode,
788 nsIContent* aContent);
789 protected:
790 nsresult NameFromType(const nsHTMLTag aTag,
791 nsIAtom **aResult);
793 nsresult NameFromNode(const nsIParserNode& aNode,
794 nsIAtom **aResult);
796 PRBool mSkip; // used when we descend into <style> or <script>
798 // Use nsTHashTable as a hash set for our whitelists
799 static nsTHashtable<nsISupportsHashKey>* sAllowedTags;
800 static nsTHashtable<nsISupportsHashKey>* sAllowedAttributes;
803 nsTHashtable<nsISupportsHashKey>* nsHTMLParanoidFragmentSink::sAllowedTags;
804 nsTHashtable<nsISupportsHashKey>* nsHTMLParanoidFragmentSink::sAllowedAttributes;
806 nsHTMLParanoidFragmentSink::nsHTMLParanoidFragmentSink():
807 nsHTMLFragmentContentSink(PR_FALSE), mSkip(PR_FALSE)
811 nsresult
812 nsHTMLParanoidFragmentSink::Init()
814 nsresult rv = NS_ERROR_FAILURE;
816 if (sAllowedTags) {
817 return NS_OK;
820 sAllowedTags = new nsTHashtable<nsISupportsHashKey>();
821 if (sAllowedTags) {
822 rv = sAllowedTags->Init(80);
823 for (PRUint32 i = 0; kDefaultAllowedTags[i] && NS_SUCCEEDED(rv); i++) {
824 if (!sAllowedTags->PutEntry(*kDefaultAllowedTags[i])) {
825 rv = NS_ERROR_OUT_OF_MEMORY;
830 sAllowedAttributes = new nsTHashtable<nsISupportsHashKey>();
831 if (sAllowedAttributes && NS_SUCCEEDED(rv)) {
832 rv = sAllowedAttributes->Init(80);
833 for (PRUint32 i = 0;
834 kDefaultAllowedAttributes[i] && NS_SUCCEEDED(rv); i++) {
835 if (!sAllowedAttributes->PutEntry(*kDefaultAllowedAttributes[i])) {
836 rv = NS_ERROR_OUT_OF_MEMORY;
841 if (NS_FAILED(rv)) {
842 NS_WARNING("Failed to populate whitelist hash sets");
843 Cleanup();
844 return rv;
847 return rv;
850 void
851 nsHTMLParanoidFragmentSink::Cleanup()
853 if (sAllowedTags) {
854 delete sAllowedTags;
855 sAllowedTags = nsnull;
858 if (sAllowedAttributes) {
859 delete sAllowedAttributes;
860 sAllowedAttributes = nsnull;
864 nsresult
865 NS_NewHTMLParanoidFragmentSink(nsIFragmentContentSink** aResult)
867 nsHTMLParanoidFragmentSink* it = new nsHTMLParanoidFragmentSink();
868 if (!it) {
869 return NS_ERROR_OUT_OF_MEMORY;
871 nsresult rv = nsHTMLParanoidFragmentSink::Init();
872 NS_ENSURE_SUCCESS(rv, rv);
873 NS_ADDREF(*aResult = it);
875 return NS_OK;
878 void
879 NS_HTMLParanoidFragmentSinkShutdown()
881 nsHTMLParanoidFragmentSink::Cleanup();
884 NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLParanoidFragmentSink,
885 nsHTMLFragmentContentSink)
887 nsresult
888 nsHTMLParanoidFragmentSink::NameFromType(const nsHTMLTag aTag,
889 nsIAtom **aResult)
891 nsIParserService* parserService = nsContentUtils::GetParserService();
892 if (!parserService) {
893 return NS_ERROR_OUT_OF_MEMORY;
895 NS_IF_ADDREF(*aResult = parserService->HTMLIdToAtomTag(aTag));
897 return NS_OK;
900 nsresult
901 nsHTMLParanoidFragmentSink::NameFromNode(const nsIParserNode& aNode,
902 nsIAtom **aResult)
904 nsresult rv;
905 eHTMLTags type = (eHTMLTags)aNode.GetNodeType();
907 *aResult = nsnull;
908 if (type == eHTMLTag_userdefined) {
909 nsCOMPtr<nsINodeInfo> nodeInfo;
910 rv =
911 mNodeInfoManager->GetNodeInfo(aNode.GetText(), nsnull,
912 kNameSpaceID_XHTML,
913 getter_AddRefs(nodeInfo));
914 NS_ENSURE_SUCCESS(rv, rv);
915 NS_IF_ADDREF(*aResult = nodeInfo->NameAtom());
916 } else {
917 rv = NameFromType(type, aResult);
919 return rv;
922 // nsHTMLFragmentContentSink
924 nsresult
925 nsHTMLParanoidFragmentSink::AddAttributes(const nsIParserNode& aNode,
926 nsIContent* aContent)
928 PRInt32 ac = aNode.GetAttributeCount();
930 if (ac == 0) {
931 return NS_OK;
934 nsAutoString k;
935 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
937 nsresult rv;
938 // use this to check for safe URIs in the few attributes that allow them
939 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
940 nsCOMPtr<nsIURI> baseURI;
942 for (PRInt32 i = ac - 1; i >= 0; i--) {
943 rv = NS_OK;
944 nsContentUtils::ASCIIToLower(aNode.GetKeyAt(i), k);
945 nsCOMPtr<nsIAtom> keyAtom = do_GetAtom(k);
947 // not an allowed attribute
948 if (!sAllowedAttributes || !sAllowedAttributes->GetEntry(keyAtom)) {
949 continue;
952 // Get value and remove mandatory quotes
953 static const char* kWhitespace = "\n\r\t\b";
954 const nsAString& v =
955 nsContentUtils::TrimCharsInSet(kWhitespace, aNode.GetValueAt(i));
957 // check the attributes we allow that contain URIs
958 if (IsAttrURI(keyAtom)) {
959 if (!baseURI) {
960 baseURI = aContent->GetBaseURI();
962 nsCOMPtr<nsIURI> attrURI;
963 rv = NS_NewURI(getter_AddRefs(attrURI), v, nsnull, baseURI);
964 if (NS_SUCCEEDED(rv)) {
965 rv = secMan->
966 CheckLoadURIWithPrincipal(mTargetDocument->NodePrincipal(),
967 attrURI,
968 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL);
972 // skip to the next attribute if we encountered issues with the
973 // current value
974 if (NS_FAILED(rv)) {
975 continue;
978 if (nodeType == eHTMLTag_a && keyAtom == nsGkAtoms::name) {
979 NS_ConvertUTF16toUTF8 cname(v);
980 NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
981 // Add attribute to content
982 aContent->SetAttr(kNameSpaceID_None, keyAtom, uv, PR_FALSE);
983 } else {
984 // Add attribute to content
985 aContent->SetAttr(kNameSpaceID_None, keyAtom, v, PR_FALSE);
989 return NS_OK;
992 NS_IMETHODIMP
993 nsHTMLParanoidFragmentSink::OpenContainer(const nsIParserNode& aNode)
995 nsresult rv = NS_OK;
997 // bail if it's a script or style, or we're already inside one of those
998 eHTMLTags type = (eHTMLTags)aNode.GetNodeType();
999 if (type == eHTMLTag_script || type == eHTMLTag_style) {
1000 mSkip = PR_TRUE;
1001 return rv;
1004 nsCOMPtr<nsIAtom> name;
1005 rv = NameFromNode(aNode, getter_AddRefs(name));
1006 NS_ENSURE_SUCCESS(rv, rv);
1008 // not on whitelist
1009 if (!sAllowedTags || !sAllowedTags->GetEntry(name)) {
1010 return NS_OK;
1013 return nsHTMLFragmentContentSink::OpenContainer(aNode);
1016 NS_IMETHODIMP
1017 nsHTMLParanoidFragmentSink::CloseContainer(const nsHTMLTag aTag)
1019 nsresult rv = NS_OK;
1021 if (mSkip) {
1022 mSkip = PR_FALSE;
1023 return rv;
1026 nsCOMPtr<nsIAtom> name;
1027 rv = NameFromType(aTag, getter_AddRefs(name));
1028 NS_ENSURE_SUCCESS(rv, rv);
1030 // not on whitelist
1031 if (!sAllowedTags || !sAllowedTags->GetEntry(name)) {
1032 return NS_OK;
1035 return nsHTMLFragmentContentSink::CloseContainer(aTag);
1038 NS_IMETHODIMP
1039 nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode)
1041 NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_NOT_INITIALIZED);
1043 nsresult rv = NS_OK;
1045 if (mSkip) {
1046 return rv;
1049 if (aNode.GetTokenType() == eToken_start) {
1050 nsCOMPtr<nsIAtom> name;
1051 rv = NameFromNode(aNode, getter_AddRefs(name));
1052 NS_ENSURE_SUCCESS(rv, rv);
1054 // Don't include base tags in output.
1055 if (name == nsGkAtoms::base) {
1056 return NS_OK;
1059 if (!sAllowedTags || !sAllowedTags->GetEntry(name)) {
1060 return NS_OK;
1064 return nsHTMLFragmentContentSink::AddLeaf(aNode);
1067 NS_IMETHODIMP
1068 nsHTMLParanoidFragmentSink::AddComment(const nsIParserNode& aNode)
1070 // no comments
1071 return NS_OK;
1074 NS_IMETHODIMP
1075 nsHTMLParanoidFragmentSink::AddProcessingInstruction(const nsIParserNode& aNode)
1077 // no PIs
1078 return NS_OK;