1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 et tw=78: */
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
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.
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Peter Annema <disttsc@bart.nl>
26 * Daniel Glazman <glazman@netscape.com>
27 * Henri Sivonen <hsivonen@iki.fi>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either of the GNU General Public License Version 2 or later (the "GPL"),
31 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
43 #include "nsContentSink.h"
45 #include "nsReadableUtils.h"
46 #include "nsUnicharUtils.h"
47 #include "nsIHTMLContentSink.h"
48 #include "nsIInterfaceRequestor.h"
49 #include "nsIInterfaceRequestorUtils.h"
50 #include "nsIParser.h"
51 #include "nsParserUtils.h"
52 #include "nsScriptLoader.h"
54 #include "nsNetUtil.h"
55 #include "nsIContentViewer.h"
56 #include "nsIMarkupDocumentViewer.h"
57 #include "nsINodeInfo.h"
58 #include "nsHTMLTokens.h"
59 #include "nsIAppShell.h"
64 #include "nsNodeUtils.h"
65 #include "nsIContent.h"
66 #include "mozilla/dom/Element.h"
68 #include "nsGenericHTMLElement.h"
70 #include "nsIDOMText.h"
71 #include "nsIDOMComment.h"
72 #include "nsIDOMDocument.h"
73 #include "nsIDOMNSDocument.h"
74 #include "nsIDOMDOMImplementation.h"
75 #include "nsIDOMDocumentType.h"
76 #include "nsIDOMHTMLScriptElement.h"
77 #include "nsIScriptElement.h"
79 #include "nsIDOMHTMLFormElement.h"
80 #include "nsIDOMHTMLTextAreaElement.h"
81 #include "nsIFormControl.h"
84 #include "nsIComponentManager.h"
85 #include "nsIServiceManager.h"
87 #include "nsGkAtoms.h"
88 #include "nsContentUtils.h"
89 #include "nsIChannel.h"
90 #include "nsIHttpChannel.h"
91 #include "nsIDocShell.h"
92 #include "nsIDocument.h"
93 #include "nsStubDocumentObserver.h"
94 #include "nsIHTMLDocument.h"
95 #include "nsINameSpaceManager.h"
96 #include "nsIDOMHTMLMapElement.h"
97 #include "nsICookieService.h"
99 #include "nsIScriptSecurityManager.h"
100 #include "nsIPrincipal.h"
101 #include "nsTextFragment.h"
102 #include "nsIScriptGlobalObject.h"
103 #include "nsIScriptGlobalObjectOwner.h"
105 #include "nsIParserService.h"
106 #include "nsISelectElement.h"
108 #include "nsIStyleSheetLinkingElement.h"
109 #include "nsITimer.h"
110 #include "nsDOMError.h"
111 #include "nsContentPolicyUtils.h"
112 #include "nsIScriptContext.h"
113 #include "nsStyleLinkElement.h"
115 #include "nsReadableUtils.h"
116 #include "nsWeakReference.h" // nsHTMLElementFactory supports weak references
117 #include "nsIPrompt.h"
118 #include "nsLayoutCID.h"
119 #include "nsIDocShellTreeItem.h"
121 #include "nsEscape.h"
122 #include "nsIElementObserver.h"
123 #include "nsNodeInfoManager.h"
124 #include "nsContentCreatorFunctions.h"
125 #include "mozAutoDocUpdate.h"
127 using namespace mozilla::dom
;
130 static PRLogModuleInfo
* gSinkLogModuleInfo
;
132 #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj) \
133 _obj->SinkTraceNode(_bit, _msg, _tag, _sp, this)
136 #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj)
139 //----------------------------------------------------------------------
141 typedef nsGenericHTMLElement
* (*contentCreatorCallback
)(nsINodeInfo
*, PRBool aFromParser
);
143 nsGenericHTMLElement
*
144 NS_NewHTMLNOTUSEDElement(nsINodeInfo
*aNodeInfo
, PRBool aFromParser
)
146 NS_NOTREACHED("The element ctor should never be called");
150 #define HTML_TAG(_tag, _classname) NS_NewHTML##_classname##Element,
151 #define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement,
152 static const contentCreatorCallback sContentCreatorCallbacks
[] = {
153 NS_NewHTMLUnknownElement
,
154 #include "nsHTMLTagList.h"
157 NS_NewHTMLUnknownElement
161 class HTMLContentSink
;
163 static void MaybeSetForm(nsGenericHTMLElement
*, nsHTMLTag
, HTMLContentSink
*);
165 class HTMLContentSink
: public nsContentSink
,
167 public nsIDebugDumpContent
,
169 public nsIHTMLContentSink
172 friend class SinkContext
;
173 friend void MaybeSetForm(nsGenericHTMLElement
*, nsHTMLTag
, HTMLContentSink
*);
176 virtual ~HTMLContentSink();
178 NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
180 nsresult
Init(nsIDocument
* aDoc
, nsIURI
* aURI
, nsISupports
* aContainer
,
181 nsIChannel
* aChannel
);
184 NS_DECL_ISUPPORTS_INHERITED
185 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentSink
, nsContentSink
)
188 NS_IMETHOD
WillParse(void);
189 NS_IMETHOD
WillBuildModel(nsDTDMode aDTDMode
);
190 NS_IMETHOD
DidBuildModel(PRBool aTerminated
);
191 NS_IMETHOD
WillInterrupt(void);
192 NS_IMETHOD
WillResume(void);
193 NS_IMETHOD
SetParser(nsIParser
* aParser
);
194 virtual void FlushPendingNotifications(mozFlushType aType
);
195 NS_IMETHOD
SetDocumentCharset(nsACString
& aCharset
);
196 virtual nsISupports
*GetTarget();
197 virtual PRBool
IsScriptExecuting();
199 // nsIHTMLContentSink
200 NS_IMETHOD
OpenContainer(const nsIParserNode
& aNode
);
201 NS_IMETHOD
CloseContainer(const nsHTMLTag aTag
);
202 NS_IMETHOD
CloseMalformedContainer(const nsHTMLTag aTag
);
203 NS_IMETHOD
AddLeaf(const nsIParserNode
& aNode
);
204 NS_IMETHOD
AddComment(const nsIParserNode
& aNode
);
205 NS_IMETHOD
AddProcessingInstruction(const nsIParserNode
& aNode
);
206 NS_IMETHOD
AddDocTypeDecl(const nsIParserNode
& aNode
);
207 NS_IMETHOD
DidProcessTokens(void);
208 NS_IMETHOD
WillProcessAToken(void);
209 NS_IMETHOD
DidProcessAToken(void);
210 NS_IMETHOD
NotifyTagObservers(nsIParserNode
* aNode
);
211 NS_IMETHOD
BeginContext(PRInt32 aID
);
212 NS_IMETHOD
EndContext(PRInt32 aID
);
213 NS_IMETHOD
OpenHead();
214 NS_IMETHOD
IsEnabled(PRInt32 aTag
, PRBool
* aReturn
);
215 NS_IMETHOD_(PRBool
) IsFormOnStack();
218 // nsIDebugDumpContent
219 NS_IMETHOD
DumpContentModel();
223 // If aCheckIfPresent is true, will only set an attribute in cases
224 // when it's not already set.
225 nsresult
AddAttributes(const nsIParserNode
& aNode
, nsIContent
* aContent
,
226 PRBool aNotify
= PR_FALSE
,
227 PRBool aCheckIfPresent
= PR_FALSE
);
228 already_AddRefed
<nsGenericHTMLElement
>
229 CreateContentObject(const nsIParserNode
& aNode
, nsHTMLTag aNodeType
);
232 void SinkTraceNode(PRUint32 aBit
,
234 const nsHTMLTag aTag
,
239 nsCOMPtr
<nsIHTMLDocument
> mHTMLDocument
;
241 // The maximum length of a text run
244 nsRefPtr
<nsGenericHTMLElement
> mRoot
;
245 nsRefPtr
<nsGenericHTMLElement
> mBody
;
246 nsRefPtr
<nsGenericHTMLElement
> mFrameset
;
247 nsRefPtr
<nsGenericHTMLElement
> mHead
;
249 nsRefPtr
<nsGenericHTMLElement
> mCurrentForm
;
251 nsAutoTArray
<SinkContext
*, 8> mContextStack
;
252 SinkContext
* mCurrentContext
;
253 SinkContext
* mHeadContext
;
254 PRInt32 mNumOpenIFRAMES
;
256 // depth of containment within <noembed>, <noframes> etc
257 PRInt32 mInsideNoXXXTag
;
259 // Boolean indicating whether we've seen a <head> tag that might have had
260 // attributes once already.
261 PRPackedBool mHaveSeenHead
;
263 // Boolean indicating whether we've notified insertion of our root content
264 // yet. We want to make sure to only do this once.
265 PRPackedBool mNotifiedRootInsertion
;
267 PRUint8 mScriptEnabled
: 1;
268 PRUint8 mFramesEnabled
: 1;
269 PRUint8 mFormOnStack
: 1;
270 PRUint8 unused
: 5; // bits available if someone needs one
272 nsCOMPtr
<nsIObserverEntry
> mObservers
;
274 nsINodeInfo
* mNodeInfoCache
[NS_HTML_TAG_MAX
+ 1];
276 nsresult
FlushTags();
278 void StartLayout(PRBool aIgnorePendingSheets
);
280 // Routines for tags that require special handling
281 nsresult
CloseHTML();
282 nsresult
OpenFrameset(const nsIParserNode
& aNode
);
283 nsresult
CloseFrameset();
284 nsresult
OpenBody(const nsIParserNode
& aNode
);
285 nsresult
CloseBody();
286 nsresult
OpenForm(const nsIParserNode
& aNode
);
287 nsresult
CloseForm();
288 nsresult
ProcessLINKTag(const nsIParserNode
& aNode
);
290 // Routines for tags that require special handling when we reach their end
292 nsresult
ProcessSCRIPTEndTag(nsGenericHTMLElement
* content
,
294 nsresult
ProcessSTYLEEndTag(nsGenericHTMLElement
* content
);
296 nsresult
OpenHeadContext();
297 void CloseHeadContext();
299 // nsContentSink overrides
300 virtual void PreEvaluateScript();
301 virtual void PostEvaluateScript(nsIScriptElement
*aElement
);
303 void UpdateChildCounts();
305 void NotifyInsert(nsIContent
* aContent
,
306 nsIContent
* aChildContent
,
307 PRInt32 aIndexInContainer
);
308 void NotifyRootInsertion();
310 PRBool
IsMonolithicContainer(nsHTMLTag aTag
);
320 SinkContext(HTMLContentSink
* aSink
);
323 nsresult
Begin(nsHTMLTag aNodeType
, nsGenericHTMLElement
* aRoot
,
324 PRUint32 aNumFlushed
, PRInt32 aInsertionPoint
);
325 nsresult
OpenContainer(const nsIParserNode
& aNode
);
326 nsresult
CloseContainer(const nsHTMLTag aTag
, PRBool aMalformed
);
327 nsresult
AddLeaf(const nsIParserNode
& aNode
);
328 nsresult
AddLeaf(nsIContent
* aContent
);
329 nsresult
AddComment(const nsIParserNode
& aNode
);
332 nsresult
GrowStack();
333 nsresult
AddText(const nsAString
& aText
);
334 nsresult
FlushText(PRBool
* aDidFlush
= nsnull
,
335 PRBool aReleaseLast
= PR_FALSE
);
336 nsresult
FlushTextAndRelease(PRBool
* aDidFlush
= nsnull
)
338 return FlushText(aDidFlush
, PR_TRUE
);
341 nsresult
FlushTags();
343 PRBool
IsCurrentContainer(nsHTMLTag mType
);
344 PRBool
IsAncestorContainer(nsHTMLTag mType
);
346 void DidAddContent(nsIContent
* aContent
);
347 void UpdateChildCounts();
350 // Function to check whether we've notified for the current content.
351 // What this actually does is check whether we've notified for all
352 // of the parent's kids.
353 PRBool
HaveNotifiedForCurrentContent() const;
356 HTMLContentSink
* mSink
;
357 PRInt32 mNotifyLevel
;
358 nsCOMPtr
<nsIContent
> mLastTextNode
;
359 PRInt32 mLastTextNodeSize
;
363 nsGenericHTMLElement
* mContent
;
364 PRUint32 mNumFlushed
;
365 PRInt32 mInsertionPoint
;
367 nsIContent
*Add(nsIContent
*child
);
379 PRBool mLastTextCharWasCR
;
382 //----------------------------------------------------------------------
386 HTMLContentSink::SinkTraceNode(PRUint32 aBit
,
388 const nsHTMLTag aTag
,
392 if (SINK_LOG_TEST(gSinkLogModuleInfo
, aBit
)) {
393 nsIParserService
*parserService
= nsContentUtils::GetParserService();
397 NS_ConvertUTF16toUTF8
tag(parserService
->HTMLIdToStringTag(aTag
));
398 PR_LogPrint("%s: this=%p node='%s' stackPos=%d",
399 aMsg
, aThis
, tag
.get(), aStackPos
);
405 HTMLContentSink::AddAttributes(const nsIParserNode
& aNode
,
406 nsIContent
* aContent
, PRBool aNotify
,
407 PRBool aCheckIfPresent
)
409 // Add tag attributes to the content attributes
411 PRInt32 ac
= aNode
.GetAttributeCount();
414 // No attributes, nothing to do. Do an early return to avoid
415 // constructing the nsAutoString object for nothing.
420 nsHTMLTag nodeType
= nsHTMLTag(aNode
.GetNodeType());
422 // The attributes are on the parser node in the order they came in in the
423 // source. What we want to happen if a single attribute is set multiple
424 // times on an element is that the first time should "win". That is, <input
425 // value="foo" value="bar"> should show "foo". So we loop over the
426 // attributes backwards; this ensures that the first attribute in the set
427 // wins. This does mean that we do some extra work in the case when the same
428 // attribute is set multiple times, but we save a HasAttr call in the much
429 // more common case of reasonable HTML. Note that if aCheckIfPresent is set
430 // then we actually want to loop _forwards_ to preserve the "first attribute
431 // wins" behavior. That does mean that when aCheckIfPresent is set the order
432 // of attributes will get "reversed" from the point of view of the
433 // serializer. But aCheckIfPresent is only true for malformed documents with
434 // multiple <html>, <head>, or <body> tags, so we're doing fixup anyway at
437 PRInt32 i
, limit
, step
;
438 if (aCheckIfPresent
) {
449 for (; i
!= limit
; i
+= step
) {
450 // Get lower-cased key
451 nsContentUtils::ASCIIToLower(aNode
.GetKeyAt(i
), key
);
453 nsCOMPtr
<nsIAtom
> keyAtom
= do_GetAtom(key
);
455 if (aCheckIfPresent
&& aContent
->HasAttr(kNameSpaceID_None
, keyAtom
)) {
459 // Get value and remove mandatory quotes
460 static const char* kWhitespace
= "\n\r\t\b";
462 // Bug 114997: Don't trim whitespace on <input value="...">:
463 // Using ?: outside the function call would be more efficient, but
464 // we don't trust ?: with references.
466 nsContentUtils::TrimCharsInSet(
467 (nodeType
== eHTMLTag_input
&&
468 keyAtom
== nsGkAtoms::value
) ?
469 "" : kWhitespace
, aNode
.GetValueAt(i
));
471 if (nodeType
== eHTMLTag_a
&& keyAtom
== nsGkAtoms::name
) {
472 NS_ConvertUTF16toUTF8
cname(v
);
473 NS_ConvertUTF8toUTF16
uv(nsUnescape(cname
.BeginWriting()));
475 // Add attribute to content
476 aContent
->SetAttr(kNameSpaceID_None
, keyAtom
, uv
, aNotify
);
478 // Add attribute to content
479 aContent
->SetAttr(kNameSpaceID_None
, keyAtom
, v
, aNotify
);
487 MaybeSetForm(nsGenericHTMLElement
* aContent
, nsHTMLTag aNodeType
,
488 HTMLContentSink
* aSink
)
490 nsGenericHTMLElement
* form
= aSink
->mCurrentForm
;
492 if (!form
|| aSink
->mInsideNoXXXTag
) {
497 case eHTMLTag_button
:
498 case eHTMLTag_fieldset
:
500 case eHTMLTag_legend
:
501 case eHTMLTag_object
:
503 case eHTMLTag_select
:
504 case eHTMLTag_textarea
:
510 nsCOMPtr
<nsIFormControl
> formControl(do_QueryInterface(aContent
));
511 NS_ASSERTION(formControl
,
512 "nsGenericHTMLElement didn't implement nsIFormControl");
513 nsCOMPtr
<nsIDOMHTMLFormElement
> formElement(do_QueryInterface(form
));
514 NS_ASSERTION(formElement
,
515 "nsGenericHTMLElement didn't implement nsIDOMHTMLFormElement");
517 formControl
->SetForm(formElement
);
521 * Factory subroutine to create all of the html content objects.
523 already_AddRefed
<nsGenericHTMLElement
>
524 HTMLContentSink::CreateContentObject(const nsIParserNode
& aNode
,
527 // Find/create atom for the tag name
529 nsCOMPtr
<nsINodeInfo
> nodeInfo
;
531 if (aNodeType
== eHTMLTag_userdefined
) {
533 nsContentUtils::ASCIIToLower(aNode
.GetText(), lower
);
534 nsCOMPtr
<nsIAtom
> name
= do_GetAtom(lower
);
535 nodeInfo
= mNodeInfoManager
->GetNodeInfo(name
, nsnull
, kNameSpaceID_XHTML
);
537 else if (mNodeInfoCache
[aNodeType
]) {
538 nodeInfo
= mNodeInfoCache
[aNodeType
];
541 nsIParserService
*parserService
= nsContentUtils::GetParserService();
545 nsIAtom
*name
= parserService
->HTMLIdToAtomTag(aNodeType
);
546 NS_ASSERTION(name
, "What? Reverse mapping of id to string broken!!!");
548 nodeInfo
= mNodeInfoManager
->GetNodeInfo(name
, nsnull
, kNameSpaceID_XHTML
);
549 NS_IF_ADDREF(mNodeInfoCache
[aNodeType
] = nodeInfo
);
552 NS_ENSURE_TRUE(nodeInfo
, nsnull
);
554 // Make the content object
555 return CreateHTMLElement(aNodeType
, nodeInfo
, PR_TRUE
);
559 NS_NewHTMLElement(nsIContent
** aResult
, nsINodeInfo
*aNodeInfo
,
564 nsIParserService
* parserService
= nsContentUtils::GetParserService();
566 return NS_ERROR_OUT_OF_MEMORY
;
568 nsIAtom
*name
= aNodeInfo
->NameAtom();
570 NS_ASSERTION(aNodeInfo
->NamespaceEquals(kNameSpaceID_XHTML
),
571 "Trying to HTML elements that don't have the XHTML namespace");
573 *aResult
= CreateHTMLElement(parserService
->
574 HTMLCaseSensitiveAtomTagToId(name
),
575 aNodeInfo
, aFromParser
).get();
576 return *aResult
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
579 already_AddRefed
<nsGenericHTMLElement
>
580 CreateHTMLElement(PRUint32 aNodeType
, nsINodeInfo
*aNodeInfo
,
583 NS_ASSERTION(aNodeType
<= NS_HTML_TAG_MAX
||
584 aNodeType
== eHTMLTag_userdefined
,
585 "aNodeType is out of bounds");
587 contentCreatorCallback cb
= sContentCreatorCallbacks
[aNodeType
];
589 NS_ASSERTION(cb
!= NS_NewHTMLNOTUSEDElement
,
590 "Don't know how to construct tag element!");
592 nsGenericHTMLElement
* result
= cb(aNodeInfo
, aFromParser
);
593 NS_IF_ADDREF(result
);
598 //----------------------------------------------------------------------
600 SinkContext::SinkContext(HTMLContentSink
* aSink
)
603 mLastTextNodeSize(0),
610 mLastTextCharWasCR(PR_FALSE
)
612 MOZ_COUNT_CTOR(SinkContext
);
615 SinkContext::~SinkContext()
617 MOZ_COUNT_DTOR(SinkContext
);
620 for (PRInt32 i
= 0; i
< mStackPos
; i
++) {
621 NS_RELEASE(mStack
[i
].mContent
);
630 SinkContext::Begin(nsHTMLTag aNodeType
,
631 nsGenericHTMLElement
* aRoot
,
632 PRUint32 aNumFlushed
,
633 PRInt32 aInsertionPoint
)
635 if (mStackSize
< 1) {
636 nsresult rv
= GrowStack();
642 mStack
[0].mType
= aNodeType
;
643 mStack
[0].mContent
= aRoot
;
644 mStack
[0].mNumFlushed
= aNumFlushed
;
645 mStack
[0].mInsertionPoint
= aInsertionPoint
;
654 SinkContext::IsCurrentContainer(nsHTMLTag aTag
)
656 if (aTag
== mStack
[mStackPos
- 1].mType
) {
664 SinkContext::IsAncestorContainer(nsHTMLTag aTag
)
666 PRInt32 stackPos
= mStackPos
- 1;
668 while (stackPos
>= 0) {
669 if (aTag
== mStack
[stackPos
].mType
) {
679 SinkContext::DidAddContent(nsIContent
* aContent
)
681 if ((mStackPos
== 2) && (mSink
->mBody
== mStack
[1].mContent
||
682 mSink
->mFrameset
== mStack
[1].mContent
)) {
683 // We just finished adding something to the body
687 // If we just added content to a node for which
688 // an insertion happen, we need to do an immediate
689 // notification for that insertion.
691 mStack
[mStackPos
- 1].mInsertionPoint
!= -1 &&
692 mStack
[mStackPos
- 1].mNumFlushed
<
693 mStack
[mStackPos
- 1].mContent
->GetChildCount()) {
694 nsIContent
* parent
= mStack
[mStackPos
- 1].mContent
;
698 nsIParserService
*parserService
= nsContentUtils::GetParserService();
700 nsHTMLTag tag
= nsHTMLTag(mStack
[mStackPos
- 1].mType
);
701 NS_ConvertUTF16toUTF8
str(parserService
->HTMLIdToStringTag(tag
));
703 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
704 ("SinkContext::DidAddContent: Insertion notification for "
705 "parent=%s at position=%d and stackPos=%d",
706 str
.get(), mStack
[mStackPos
- 1].mInsertionPoint
- 1,
711 PRInt32 childIndex
= mStack
[mStackPos
- 1].mInsertionPoint
- 1;
712 NS_ASSERTION(parent
->GetChildAt(childIndex
) == aContent
,
713 "Flushing the wrong child.");
714 mSink
->NotifyInsert(parent
, aContent
, childIndex
);
715 mStack
[mStackPos
- 1].mNumFlushed
= parent
->GetChildCount();
716 } else if (mSink
->IsTimeToNotify()) {
717 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
718 ("SinkContext::DidAddContent: Notification as a result of the "
719 "interval expiring; backoff count: %d", mSink
->mBackoffCount
));
725 SinkContext::OpenContainer(const nsIParserNode
& aNode
)
727 FlushTextAndRelease();
729 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
730 "SinkContext::OpenContainer",
731 nsHTMLTag(aNode
.GetNodeType()),
735 if (mStackPos
<= 0) {
736 NS_ERROR("container w/o parent");
738 return NS_ERROR_FAILURE
;
742 if (mStackPos
+ 1 > mStackSize
) {
749 // Create new container content object
750 nsHTMLTag nodeType
= nsHTMLTag(aNode
.GetNodeType());
751 nsGenericHTMLElement
* content
=
752 mSink
->CreateContentObject(aNode
, nodeType
).get();
754 return NS_ERROR_OUT_OF_MEMORY
;
757 mStack
[mStackPos
].mType
= nodeType
;
758 mStack
[mStackPos
].mContent
= content
;
759 mStack
[mStackPos
].mNumFlushed
= 0;
760 mStack
[mStackPos
].mInsertionPoint
= -1;
763 // XXX Need to do this before we start adding attributes.
764 if (nodeType
== eHTMLTag_style
) {
765 nsCOMPtr
<nsIStyleSheetLinkingElement
> ssle
= do_QueryInterface(content
);
766 NS_ASSERTION(ssle
, "Style content isn't a style sheet?");
767 ssle
->SetLineNumber(aNode
.GetSourceLineNumber());
769 // Now disable updates so that every time we add an attribute or child
770 // text token, we don't try to update the style sheet.
771 if (!mSink
->mInsideNoXXXTag
) {
772 ssle
->InitStyleLinkElement(PR_FALSE
);
775 // We're not going to be evaluating this style anyway.
776 ssle
->InitStyleLinkElement(PR_TRUE
);
779 ssle
->SetEnableUpdates(PR_FALSE
);
782 rv
= mSink
->AddAttributes(aNode
, content
);
783 MaybeSetForm(content
, nodeType
, mSink
);
785 mStack
[mStackPos
- 2].Add(content
);
787 NS_ENSURE_SUCCESS(rv
, rv
);
789 if (mSink
->IsMonolithicContainer(nodeType
)) {
790 mSink
->mInMonolithicContainer
++;
793 // Special handling for certain tags
796 mSink
->mCurrentForm
= content
;
799 case eHTMLTag_frameset
:
800 if (!mSink
->mFrameset
&& mSink
->mFramesEnabled
) {
801 mSink
->mFrameset
= content
;
805 case eHTMLTag_noembed
:
806 case eHTMLTag_noframes
:
807 mSink
->mInsideNoXXXTag
++;
810 case eHTMLTag_iframe
:
811 mSink
->mNumOpenIFRAMES
++;
814 case eHTMLTag_script
:
816 nsCOMPtr
<nsIScriptElement
> sele
= do_QueryInterface(content
);
817 NS_ASSERTION(sele
, "Script content isn't a script element?");
818 sele
->SetScriptLineNumber(aNode
.GetSourceLineNumber());
822 case eHTMLTag_button
:
823 content
->DoneCreatingElement();
834 SinkContext::HaveNotifiedForCurrentContent() const
837 nsIContent
* parent
= mStack
[mStackPos
- 1].mContent
;
838 return mStack
[mStackPos
-1].mNumFlushed
== parent
->GetChildCount();
845 SinkContext::Node::Add(nsIContent
*child
)
847 NS_ASSERTION(mContent
, "No parent to insert/append into!");
848 if (mInsertionPoint
!= -1) {
849 NS_ASSERTION(mNumFlushed
== mContent
->GetChildCount(),
850 "Inserting multiple children without flushing.");
851 mContent
->InsertChildAt(child
, mInsertionPoint
++, PR_FALSE
);
853 mContent
->AppendChildTo(child
, PR_FALSE
);
859 SinkContext::CloseContainer(const nsHTMLTag aTag
, PRBool aMalformed
)
861 nsresult result
= NS_OK
;
863 // Flush any collected text content. Release the last text
864 // node to indicate that no more should be added to it.
865 FlushTextAndRelease();
867 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
868 "SinkContext::CloseContainer",
869 aTag
, mStackPos
- 1, mSink
);
871 NS_ASSERTION(mStackPos
> 0,
872 "stack out of bounds. wrong context probably!");
874 if (mStackPos
<= 0) {
875 return NS_OK
; // Fix crash - Ref. bug 45975 or 45007
879 nsHTMLTag nodeType
= mStack
[mStackPos
].mType
;
881 NS_ASSERTION(nodeType
== eHTMLTag_form
|| nodeType
== aTag
,
882 "Tag mismatch. Closing tag on wrong context or something?");
884 nsGenericHTMLElement
* content
= mStack
[mStackPos
].mContent
;
888 // If we're in a state where we do append notifications as
889 // we go up the tree, and we're at the level where the next
890 // notification needs to be done, do the notification.
891 if (mNotifyLevel
>= mStackPos
) {
892 // Check to see if new content has been added after our last
895 if (mStack
[mStackPos
].mNumFlushed
< content
->GetChildCount()) {
899 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
900 ("SinkContext::CloseContainer: reflow on notifyImmediate "
901 "tag=%s newIndex=%d stackPos=%d",
902 nsAtomCString(mStack
[mStackPos
].mContent
->Tag()).get(),
903 mStack
[mStackPos
].mNumFlushed
, mStackPos
));
906 mSink
->NotifyAppend(content
, mStack
[mStackPos
].mNumFlushed
);
907 mStack
[mStackPos
].mNumFlushed
= content
->GetChildCount();
910 // Indicate that notification has now happened at this level
911 mNotifyLevel
= mStackPos
- 1;
914 if (mSink
->IsMonolithicContainer(nodeType
)) {
915 --mSink
->mInMonolithicContainer
;
918 DidAddContent(content
);
920 // Special handling for certain tags
922 case eHTMLTag_noembed
:
923 case eHTMLTag_noframes
:
925 NS_ASSERTION((mSink
->mInsideNoXXXTag
> 0), "mInsideNoXXXTag underflow");
926 if (mSink
->mInsideNoXXXTag
> 0) {
927 mSink
->mInsideNoXXXTag
--;
933 mSink
->mFormOnStack
= PR_FALSE
;
934 // If there's a FORM on the stack, but this close tag doesn't
935 // close the form, then close out the form *and* close out the
936 // next container up. This is since the parser doesn't do fix up
937 // of invalid form nesting. When the end FORM tag comes through,
939 if (aTag
!= nodeType
) {
940 result
= CloseContainer(aTag
, PR_FALSE
);
945 case eHTMLTag_iframe
:
946 mSink
->mNumOpenIFRAMES
--;
954 case eHTMLTag_select
:
955 case eHTMLTag_textarea
:
956 case eHTMLTag_object
:
957 case eHTMLTag_applet
:
959 content
->DoneAddingChildren(HaveNotifiedForCurrentContent());
962 case eHTMLTag_script
:
963 result
= mSink
->ProcessSCRIPTEndTag(content
,
968 result
= mSink
->ProcessSTYLEEndTag(content
);
975 NS_IF_RELEASE(content
);
978 if (SINK_LOG_TEST(gSinkLogModuleInfo
, SINK_ALWAYS_REFLOW
)) {
979 mSink
->ForceReflow();
987 SinkContext::AddLeaf(const nsIParserNode
& aNode
)
989 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
990 "SinkContext::AddLeaf",
991 nsHTMLTag(aNode
.GetNodeType()),
996 switch (aNode
.GetTokenType()) {
999 FlushTextAndRelease();
1001 // Create new leaf content object
1002 nsHTMLTag nodeType
= nsHTMLTag(aNode
.GetNodeType());
1003 nsRefPtr
<nsGenericHTMLElement
> content
=
1004 mSink
->CreateContentObject(aNode
, nodeType
);
1005 NS_ENSURE_TRUE(content
, NS_ERROR_OUT_OF_MEMORY
);
1007 if (nodeType
== eHTMLTag_form
) {
1008 mSink
->mCurrentForm
= content
;
1011 rv
= mSink
->AddAttributes(aNode
, content
);
1013 NS_ENSURE_SUCCESS(rv
, rv
);
1015 MaybeSetForm(content
, nodeType
, mSink
);
1017 // Add new leaf to its parent
1020 // Additional processing needed once the element is in the tree
1023 // XXX It's just not sufficient to check if the parent is head. Also
1024 // check for the preference.
1025 // Bug 40072: Don't evaluate METAs after FRAMESET.
1026 if (!mSink
->mInsideNoXXXTag
&& !mSink
->mFrameset
) {
1027 rv
= mSink
->ProcessMETATag(content
);
1031 case eHTMLTag_input
:
1032 content
->DoneCreatingElement();
1043 case eToken_whitespace
:
1044 case eToken_newline
:
1045 rv
= AddText(aNode
.GetText());
1051 PRInt32 unicode
= aNode
.TranslateToUnicodeStr(tmp
);
1053 rv
= AddText(aNode
.GetText());
1055 // Map carriage returns to newlines
1056 if (!tmp
.IsEmpty()) {
1057 if (tmp
.CharAt(0) == '\r') {
1058 tmp
.Assign((PRUnichar
)'\n');
1074 SinkContext::AddLeaf(nsIContent
* aContent
)
1076 NS_ASSERTION(mStackPos
> 0, "leaf w/o container");
1077 if (mStackPos
<= 0) {
1078 return NS_ERROR_FAILURE
;
1081 DidAddContent(mStack
[mStackPos
- 1].Add(aContent
));
1084 if (SINK_LOG_TEST(gSinkLogModuleInfo
, SINK_ALWAYS_REFLOW
)) {
1085 mSink
->ForceReflow();
1093 SinkContext::AddComment(const nsIParserNode
& aNode
)
1095 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
1096 "SinkContext::AddLeaf",
1097 nsHTMLTag(aNode
.GetNodeType()),
1099 FlushTextAndRelease();
1102 return NS_ERROR_UNEXPECTED
;
1105 nsCOMPtr
<nsIContent
> comment
;
1106 nsresult rv
= NS_NewCommentNode(getter_AddRefs(comment
),
1107 mSink
->mNodeInfoManager
);
1108 NS_ENSURE_SUCCESS(rv
, rv
);
1110 comment
->SetText(aNode
.GetText(), PR_FALSE
);
1112 NS_ASSERTION(mStackPos
> 0, "stack out of bounds");
1113 if (mStackPos
<= 0) {
1114 return NS_ERROR_FAILURE
;
1118 Node
&parentNode
= mStack
[mStackPos
- 1];
1119 nsGenericHTMLElement
*parent
= parentNode
.mContent
;
1120 if (!mSink
->mBody
&& !mSink
->mFrameset
&& mSink
->mHead
)
1121 // XXXbz but this will make DidAddContent use the wrong parent for
1122 // the notification! That seems so bogus it's not even funny.
1123 parentNode
.mContent
= mSink
->mHead
;
1124 DidAddContent(parentNode
.Add(comment
));
1125 parentNode
.mContent
= parent
;
1129 if (SINK_LOG_TEST(gSinkLogModuleInfo
, SINK_ALWAYS_REFLOW
)) {
1130 mSink
->ForceReflow();
1140 for (PRInt32 i
= 0; i
< mStackPos
; i
++) {
1141 NS_RELEASE(mStack
[i
].mContent
);
1151 SinkContext::GrowStack()
1153 PRInt32 newSize
= mStackSize
* 2;
1158 Node
* stack
= new Node
[newSize
];
1160 return NS_ERROR_OUT_OF_MEMORY
;
1163 if (mStackPos
!= 0) {
1164 memcpy(stack
, mStack
, sizeof(Node
) * mStackPos
);
1169 mStackSize
= newSize
;
1175 * Add textual content to the current running text buffer. If the text buffer
1176 * overflows, flush out the text by creating a text content object and adding
1177 * it to the content tree.
1180 // XXX If we get a giant string grow the buffer instead of chopping it
1183 SinkContext::AddText(const nsAString
& aText
)
1185 PRInt32 addLen
= aText
.Length();
1190 // Create buffer when we first need it
1191 if (mTextSize
== 0) {
1192 mText
= new PRUnichar
[4096];
1194 return NS_ERROR_OUT_OF_MEMORY
;
1199 // Copy data from string into our buffer; flush buffer when it fills up
1202 while (addLen
!= 0) {
1203 PRInt32 amount
= mTextSize
- mTextLength
;
1205 if (amount
> addLen
) {
1210 // Don't release last text node so we can add to it again
1211 nsresult rv
= FlushText();
1212 if (NS_FAILED(rv
)) {
1216 // Go back to the top of the loop so we re-calculate amount and
1217 // don't fall through to CopyNewlineNormalizedUnicodeTo with a
1218 // zero-length amount (which invalidates mLastTextCharWasCR).
1223 nsContentUtils::CopyNewlineNormalizedUnicodeTo(aText
, offset
,
1224 &mText
[mTextLength
],
1226 mLastTextCharWasCR
);
1235 * NOTE!! Forked into nsXMLContentSink. Please keep in sync.
1237 * Flush all elements that have been seen so far such that
1238 * they are visible in the tree. Specifically, make sure
1239 * that they are all added to their respective parents.
1240 * Also, do notification at the top for all content that
1241 * has been newly added so that the frame tree is complete.
1244 SinkContext::FlushTags()
1246 mSink
->mDeferredFlushTags
= PR_FALSE
;
1247 PRBool oldBeganUpdate
= mSink
->mBeganUpdate
;
1248 PRUint32 oldUpdates
= mSink
->mUpdatesInNotification
;
1250 ++(mSink
->mInNotification
);
1251 mSink
->mUpdatesInNotification
= 0;
1253 // Scope so we call EndUpdate before we decrease mInNotification
1254 mozAutoDocUpdate
updateBatch(mSink
->mDocument
, UPDATE_CONTENT_MODEL
,
1256 mSink
->mBeganUpdate
= PR_TRUE
;
1258 // Don't release last text node in case we need to add to it again
1261 // Start from the base of the stack (growing downward) and do
1262 // a notification from the node that is closest to the root of
1263 // tree for any content that has been added.
1265 // Note that we can start at stackPos == 0 here, because it's the caller's
1266 // responsibility to handle flushing interactions between contexts (see
1267 // HTMLContentSink::BeginContext).
1268 PRInt32 stackPos
= 0;
1269 PRBool flushed
= PR_FALSE
;
1270 PRUint32 childCount
;
1271 nsGenericHTMLElement
* content
;
1273 while (stackPos
< mStackPos
) {
1274 content
= mStack
[stackPos
].mContent
;
1275 childCount
= content
->GetChildCount();
1277 if (!flushed
&& (mStack
[stackPos
].mNumFlushed
< childCount
)) {
1281 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
1282 ("SinkContext::FlushTags: tag=%s from newindex=%d at "
1284 nsAtomCString(mStack
[stackPos
].mContent
->Tag()).get(),
1285 mStack
[stackPos
].mNumFlushed
, stackPos
));
1288 if (mStack
[stackPos
].mInsertionPoint
!= -1) {
1289 // We might have popped the child off our stack already
1290 // but not notified on it yet, which is why we have to get it
1291 // directly from its parent node.
1293 PRInt32 childIndex
= mStack
[stackPos
].mInsertionPoint
- 1;
1294 nsIContent
* child
= content
->GetChildAt(childIndex
);
1295 // Child not on stack anymore; can't assert it's correct
1296 NS_ASSERTION(!(mStackPos
> (stackPos
+ 1)) ||
1297 (child
== mStack
[stackPos
+ 1].mContent
),
1298 "Flushing the wrong child.");
1299 mSink
->NotifyInsert(content
, child
, childIndex
);
1301 mSink
->NotifyAppend(content
, mStack
[stackPos
].mNumFlushed
);
1307 mStack
[stackPos
].mNumFlushed
= childCount
;
1310 mNotifyLevel
= mStackPos
- 1;
1312 --(mSink
->mInNotification
);
1314 if (mSink
->mUpdatesInNotification
> 1) {
1315 UpdateChildCounts();
1318 mSink
->mUpdatesInNotification
= oldUpdates
;
1319 mSink
->mBeganUpdate
= oldBeganUpdate
;
1325 * NOTE!! Forked into nsXMLContentSink. Please keep in sync.
1328 SinkContext::UpdateChildCounts()
1330 // Start from the top of the stack (growing upwards) and see if any
1331 // new content has been appended. If so, we recognize that reflows
1332 // have been generated for it and we should make sure that no
1333 // further reflows occur. Note that we have to include stackPos == 0
1334 // to properly notify on kids of <html>.
1335 PRInt32 stackPos
= mStackPos
- 1;
1336 while (stackPos
>= 0) {
1337 Node
& node
= mStack
[stackPos
];
1338 node
.mNumFlushed
= node
.mContent
->GetChildCount();
1343 mNotifyLevel
= mStackPos
- 1;
1347 * Flush any buffered text out by creating a text content object and
1348 * adding it to the content.
1351 SinkContext::FlushText(PRBool
* aDidFlush
, PRBool aReleaseLast
)
1353 nsresult rv
= NS_OK
;
1354 PRBool didFlush
= PR_FALSE
;
1356 if (mTextLength
!= 0) {
1357 if (mLastTextNode
) {
1358 if ((mLastTextNodeSize
+ mTextLength
) > mSink
->mMaxTextRun
) {
1359 mLastTextNodeSize
= 0;
1360 mLastTextNode
= nsnull
;
1361 FlushText(aDidFlush
, aReleaseLast
);
1363 PRBool notify
= HaveNotifiedForCurrentContent();
1364 // We could probably always increase mInNotification here since
1365 // if AppendText doesn't notify it shouldn't trigger evil code.
1366 // But just in case it does, we don't want to mask any notifications.
1368 ++mSink
->mInNotification
;
1370 rv
= mLastTextNode
->AppendText(mText
, mTextLength
, notify
);
1372 --mSink
->mInNotification
;
1375 mLastTextNodeSize
+= mTextLength
;
1380 nsCOMPtr
<nsIContent
> textContent
;
1381 rv
= NS_NewTextNode(getter_AddRefs(textContent
),
1382 mSink
->mNodeInfoManager
);
1383 NS_ENSURE_SUCCESS(rv
, rv
);
1385 mLastTextNode
= textContent
;
1387 // Set the text in the text node
1388 mLastTextNode
->SetText(mText
, mTextLength
, PR_FALSE
);
1390 // Eat up the rest of the text up in state.
1391 mLastTextNodeSize
+= mTextLength
;
1394 rv
= AddLeaf(mLastTextNode
);
1395 NS_ENSURE_SUCCESS(rv
, rv
);
1402 *aDidFlush
= didFlush
;
1406 mLastTextNodeSize
= 0;
1407 mLastTextNode
= nsnull
;
1408 mLastTextCharWasCR
= PR_FALSE
;
1413 SINK_LOG_TEST(gSinkLogModuleInfo
, SINK_ALWAYS_REFLOW
)) {
1414 mSink
->ForceReflow();
1423 NS_NewHTMLContentSink(nsIHTMLContentSink
** aResult
,
1426 nsISupports
* aContainer
,
1427 nsIChannel
* aChannel
)
1429 NS_ENSURE_ARG_POINTER(aResult
);
1431 nsRefPtr
<HTMLContentSink
> it
= new HTMLContentSink();
1434 return NS_ERROR_OUT_OF_MEMORY
;
1437 nsresult rv
= it
->Init(aDoc
, aURI
, aContainer
, aChannel
);
1439 NS_ENSURE_SUCCESS(rv
, rv
);
1442 NS_ADDREF(*aResult
);
1447 HTMLContentSink::HTMLContentSink()
1449 // Note: operator new zeros our memory
1453 if (!gSinkLogModuleInfo
) {
1454 gSinkLogModuleInfo
= PR_NewLogModule("htmlcontentsink");
1459 HTMLContentSink::~HTMLContentSink()
1461 if (mNotificationTimer
) {
1462 mNotificationTimer
->Cancel();
1465 PRInt32 numContexts
= mContextStack
.Length();
1467 if (mCurrentContext
== mHeadContext
&& numContexts
> 0) {
1468 // Pop off the second html context if it's not done earlier
1469 mContextStack
.RemoveElementAt(--numContexts
);
1473 for (i
= 0; i
< numContexts
; i
++) {
1474 SinkContext
* sc
= mContextStack
.ElementAt(i
);
1477 if (sc
== mCurrentContext
) {
1478 mCurrentContext
= nsnull
;
1485 if (mCurrentContext
== mHeadContext
) {
1486 mCurrentContext
= nsnull
;
1489 delete mCurrentContext
;
1491 delete mHeadContext
;
1493 for (i
= 0; PRUint32(i
) < NS_ARRAY_LENGTH(mNodeInfoCache
); ++i
) {
1494 NS_IF_RELEASE(mNodeInfoCache
[i
]);
1498 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLContentSink
)
1500 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLContentSink
, nsContentSink
)
1501 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mHTMLDocument
)
1502 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot
)
1503 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mBody
)
1504 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFrameset
)
1505 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mHead
)
1506 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCurrentForm
)
1507 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(tmp
->mNodeInfoCache
); ++i
) {
1508 NS_IF_RELEASE(tmp
->mNodeInfoCache
[i
]);
1510 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1511 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLContentSink
,
1513 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHTMLDocument
)
1514 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot
)
1515 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mBody
)
1516 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFrameset
)
1517 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHead
)
1518 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCurrentForm
)
1519 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(tmp
->mNodeInfoCache
); ++i
) {
1520 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mNodeInfoCache[i]");
1521 cb
.NoteXPCOMChild(tmp
->mNodeInfoCache
[i
]);
1523 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1525 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLContentSink
)
1526 NS_INTERFACE_TABLE_BEGIN
1527 NS_INTERFACE_TABLE_ENTRY(HTMLContentSink
, nsIContentSink
)
1528 NS_INTERFACE_TABLE_ENTRY(HTMLContentSink
, nsIHTMLContentSink
)
1530 NS_INTERFACE_TABLE_ENTRY(HTMLContentSink
, nsIDebugDumpContent
)
1532 NS_INTERFACE_TABLE_END
1533 NS_INTERFACE_TABLE_TAIL_INHERITING(nsContentSink
)
1535 NS_IMPL_ADDREF_INHERITED(HTMLContentSink
, nsContentSink
)
1536 NS_IMPL_RELEASE_INHERITED(HTMLContentSink
, nsContentSink
)
1539 IsScriptEnabled(nsIDocument
*aDoc
, nsIDocShell
*aContainer
)
1541 NS_ENSURE_TRUE(aDoc
&& aContainer
, PR_TRUE
);
1543 nsCOMPtr
<nsIScriptGlobalObject
> globalObject
= aDoc
->GetScriptGlobalObject();
1545 // Getting context is tricky if the document hasn't had its
1546 // GlobalObject set yet
1547 if (!globalObject
) {
1548 nsCOMPtr
<nsIScriptGlobalObjectOwner
> owner
= do_GetInterface(aContainer
);
1549 NS_ENSURE_TRUE(owner
, PR_TRUE
);
1551 globalObject
= owner
->GetScriptGlobalObject();
1552 NS_ENSURE_TRUE(globalObject
, PR_TRUE
);
1555 nsIScriptContext
*scriptContext
= globalObject
->GetContext();
1556 NS_ENSURE_TRUE(scriptContext
, PR_TRUE
);
1558 JSContext
* cx
= (JSContext
*) scriptContext
->GetNativeContext();
1559 NS_ENSURE_TRUE(cx
, PR_TRUE
);
1561 PRBool enabled
= PR_TRUE
;
1562 nsContentUtils::GetSecurityManager()->
1563 CanExecuteScripts(cx
, aDoc
->NodePrincipal(), &enabled
);
1568 HTMLContentSink::Init(nsIDocument
* aDoc
,
1570 nsISupports
* aContainer
,
1571 nsIChannel
* aChannel
)
1573 NS_ENSURE_TRUE(aContainer
, NS_ERROR_NULL_POINTER
);
1575 nsresult rv
= nsContentSink::Init(aDoc
, aURI
, aContainer
, aChannel
);
1576 if (NS_FAILED(rv
)) {
1580 aDoc
->AddObserver(this);
1581 mIsDocumentObserver
= PR_TRUE
;
1582 mHTMLDocument
= do_QueryInterface(aDoc
);
1584 mObservers
= nsnull
;
1585 nsIParserService
* service
= nsContentUtils::GetParserService();
1587 return NS_ERROR_OUT_OF_MEMORY
;
1590 service
->GetTopicObservers(NS_LITERAL_STRING("text/html"),
1591 getter_AddRefs(mObservers
));
1593 NS_ASSERTION(mDocShell
, "oops no docshell!");
1595 // Find out if subframes are enabled
1597 PRBool subFramesEnabled
= PR_TRUE
;
1598 mDocShell
->GetAllowSubframes(&subFramesEnabled
);
1599 if (subFramesEnabled
) {
1600 mFramesEnabled
= PR_TRUE
;
1604 // Find out if scripts are enabled, if not, show <noscript> content
1605 if (IsScriptEnabled(aDoc
, mDocShell
)) {
1606 mScriptEnabled
= PR_TRUE
;
1610 // Changed from 8192 to greatly improve page loading performance on
1611 // large pages. See bugzilla bug 77540.
1612 mMaxTextRun
= nsContentUtils::GetIntPref("content.maxtextrun", 8191);
1614 nsCOMPtr
<nsINodeInfo
> nodeInfo
;
1615 nodeInfo
= mNodeInfoManager
->GetNodeInfo(nsGkAtoms::html
, nsnull
,
1616 kNameSpaceID_XHTML
);
1617 NS_ENSURE_TRUE(nodeInfo
, NS_ERROR_OUT_OF_MEMORY
);
1620 mRoot
= NS_NewHTMLHtmlElement(nodeInfo
);
1622 return NS_ERROR_OUT_OF_MEMORY
;
1625 NS_ASSERTION(mDocument
->GetChildCount() == 0,
1626 "Document should have no kids here!");
1627 rv
= mDocument
->AppendChildTo(mRoot
, PR_FALSE
);
1628 NS_ENSURE_SUCCESS(rv
, rv
);
1631 nodeInfo
= mNodeInfoManager
->GetNodeInfo(nsGkAtoms::head
,
1632 nsnull
, kNameSpaceID_XHTML
);
1633 NS_ENSURE_TRUE(nodeInfo
, NS_ERROR_OUT_OF_MEMORY
);
1635 mHead
= NS_NewHTMLHeadElement(nodeInfo
);
1636 if (NS_FAILED(rv
)) {
1637 return NS_ERROR_OUT_OF_MEMORY
;
1640 mRoot
->AppendChildTo(mHead
, PR_FALSE
);
1642 mCurrentContext
= new SinkContext(this);
1643 NS_ENSURE_TRUE(mCurrentContext
, NS_ERROR_OUT_OF_MEMORY
);
1644 mCurrentContext
->Begin(eHTMLTag_html
, mRoot
, 0, -1);
1645 mContextStack
.AppendElement(mCurrentContext
);
1649 (void)aURI
->GetSpec(spec
);
1650 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_CALLS
,
1651 ("HTMLContentSink::Init: this=%p url='%s'",
1658 HTMLContentSink::WillParse(void)
1660 return WillParseImpl();
1664 HTMLContentSink::WillBuildModel(nsDTDMode aDTDMode
)
1666 WillBuildModelImpl();
1668 if (mHTMLDocument
) {
1669 nsCompatibility mode
= eCompatibility_NavQuirks
;
1671 case eDTDMode_full_standards
:
1672 mode
= eCompatibility_FullStandards
;
1674 case eDTDMode_almost_standards
:
1675 mode
= eCompatibility_AlmostStandards
;
1680 mHTMLDocument
->SetCompatibilityMode(mode
);
1683 // Notify document that the load is beginning
1684 mDocument
->BeginLoad();
1690 HTMLContentSink::DidBuildModel(PRBool aTerminated
)
1692 DidBuildModelImpl(aTerminated
);
1694 // Reflow the last batch of content
1695 if (mBody
|| mFrameset
) {
1696 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
1697 ("HTMLContentSink::DidBuildModel: layout final content"));
1698 mCurrentContext
->FlushTags();
1699 } else if (!mLayoutStarted
) {
1700 // We never saw the body, and layout never got started. Force
1701 // layout *now*, to get an initial reflow.
1702 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
1703 ("HTMLContentSink::DidBuildModel: forcing reflow on empty "
1706 // NOTE: only force the layout if we are NOT destroying the
1707 // docshell. If we are destroying it, then starting layout will
1708 // likely cause us to crash, or at best waste a lot of time as we
1709 // are just going to tear it down anyway.
1710 PRBool bDestroying
= PR_TRUE
;
1712 mDocShell
->IsBeingDestroyed(&bDestroying
);
1716 StartLayout(PR_FALSE
);
1722 mDocument
->ScriptLoader()->RemoveObserver(this);
1724 // Make sure we no longer respond to document mutations. We've flushed all
1725 // our notifications out, so there's no need to do anything else here.
1727 // XXXbz I wonder whether we could End() our contexts here too, or something,
1728 // just to make sure we no longer notify... Or is the mIsDocumentObserver
1729 // thing sufficient?
1730 mDocument
->RemoveObserver(this);
1731 mIsDocumentObserver
= PR_FALSE
;
1733 mDocument
->EndLoad();
1735 DropParserAndPerfHint();
1741 HTMLContentSink::SetParser(nsIParser
* aParser
)
1743 NS_PRECONDITION(aParser
, "Should have a parser here!");
1748 NS_IMETHODIMP_(PRBool
)
1749 HTMLContentSink::IsFormOnStack()
1751 return mFormOnStack
;
1755 HTMLContentSink::BeginContext(PRInt32 aPosition
)
1757 NS_PRECONDITION(aPosition
> -1, "out of bounds");
1759 // Create new context
1760 SinkContext
* sc
= new SinkContext(this);
1762 return NS_ERROR_OUT_OF_MEMORY
;
1765 if (!mCurrentContext
) {
1766 NS_ERROR("Nonexistent context");
1768 return NS_ERROR_FAILURE
;
1771 // Flush everything in the current context so that we don't have
1772 // to worry about insertions resulting in inconsistent frame creation.
1773 mCurrentContext
->FlushTags();
1776 if (mCurrentContext
->mStackPos
<= aPosition
) {
1777 NS_ERROR("Out of bounds position");
1778 return NS_ERROR_FAILURE
;
1781 PRInt32 insertionPoint
= -1;
1782 nsHTMLTag nodeType
= mCurrentContext
->mStack
[aPosition
].mType
;
1783 nsGenericHTMLElement
* content
= mCurrentContext
->mStack
[aPosition
].mContent
;
1785 // If the content under which the new context is created
1786 // has a child on the stack, the insertion point is
1787 // before the last child.
1788 if (aPosition
< (mCurrentContext
->mStackPos
- 1)) {
1789 insertionPoint
= content
->GetChildCount() - 1;
1794 mCurrentContext
->mStack
[aPosition
].mNumFlushed
,
1796 NS_ADDREF(sc
->mSink
);
1798 mContextStack
.AppendElement(mCurrentContext
);
1799 mCurrentContext
= sc
;
1804 HTMLContentSink::EndContext(PRInt32 aPosition
)
1806 NS_PRECONDITION(mCurrentContext
&& aPosition
> -1, "nonexistent context");
1808 PRUint32 n
= mContextStack
.Length() - 1;
1809 SinkContext
* sc
= mContextStack
.ElementAt(n
);
1811 const SinkContext::Node
&bottom
= mCurrentContext
->mStack
[0];
1813 NS_ASSERTION(sc
->mStack
[aPosition
].mType
== bottom
.mType
,
1814 "ending a wrong context");
1816 mCurrentContext
->FlushTextAndRelease();
1818 NS_ASSERTION(bottom
.mContent
->GetChildCount() == bottom
.mNumFlushed
,
1819 "Node at base of context stack not fully flushed.");
1821 // Flushing tags before the assertion on the previous line would
1822 // undoubtedly prevent the assertion from failing, but it shouldn't
1823 // be failing anyway, FlushTags or no. Flushing here is nevertheless
1824 // a worthwhile precaution, since we lose some information (e.g.,
1825 // mInsertionPoints) when we end the current context.
1826 mCurrentContext
->FlushTags();
1828 sc
->mStack
[aPosition
].mNumFlushed
= bottom
.mNumFlushed
;
1830 for (PRInt32 i
= 0; i
<mCurrentContext
->mStackPos
; i
++) {
1831 NS_IF_RELEASE(mCurrentContext
->mStack
[i
].mContent
);
1834 delete [] mCurrentContext
->mStack
;
1836 mCurrentContext
->mStack
= nsnull
;
1837 mCurrentContext
->mStackPos
= 0;
1838 mCurrentContext
->mStackSize
= 0;
1840 delete [] mCurrentContext
->mText
;
1842 mCurrentContext
->mText
= nsnull
;
1843 mCurrentContext
->mTextLength
= 0;
1844 mCurrentContext
->mTextSize
= 0;
1846 NS_IF_RELEASE(mCurrentContext
->mSink
);
1848 delete mCurrentContext
;
1850 mCurrentContext
= sc
;
1851 mContextStack
.RemoveElementAt(n
);
1856 HTMLContentSink::CloseHTML()
1858 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
1859 "HTMLContentSink::CloseHTML",
1860 eHTMLTag_html
, 0, this);
1863 if (mCurrentContext
== mHeadContext
) {
1864 PRUint32 numContexts
= mContextStack
.Length();
1866 // Pop off the second html context if it's not done earlier
1867 mCurrentContext
= mContextStack
.ElementAt(--numContexts
);
1868 mContextStack
.RemoveElementAt(numContexts
);
1871 NS_ASSERTION(mHeadContext
->mTextLength
== 0, "Losing text");
1873 mHeadContext
->End();
1875 delete mHeadContext
;
1876 mHeadContext
= nsnull
;
1883 HTMLContentSink::OpenHead()
1885 nsresult rv
= OpenHeadContext();
1890 HTMLContentSink::OpenBody(const nsIParserNode
& aNode
)
1892 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
1893 "HTMLContentSink::OpenBody",
1895 mCurrentContext
->mStackPos
,
1898 CloseHeadContext(); // do this just in case if the HEAD was left open!
1900 // Add attributes, if any, to the current BODY node
1902 AddAttributes(aNode
, mBody
, PR_TRUE
, PR_TRUE
);
1906 nsresult rv
= mCurrentContext
->OpenContainer(aNode
);
1908 if (NS_FAILED(rv
)) {
1912 mBody
= mCurrentContext
->mStack
[mCurrentContext
->mStackPos
- 1].mContent
;
1914 if (mCurrentContext
->mStackPos
> 1) {
1915 PRInt32 parentIndex
= mCurrentContext
->mStackPos
- 2;
1916 nsGenericHTMLElement
*parent
= mCurrentContext
->mStack
[parentIndex
].mContent
;
1917 PRInt32 numFlushed
= mCurrentContext
->mStack
[parentIndex
].mNumFlushed
;
1918 PRInt32 childCount
= parent
->GetChildCount();
1919 NS_ASSERTION(numFlushed
< childCount
, "Already notified on the body?");
1921 PRInt32 insertionPoint
=
1922 mCurrentContext
->mStack
[parentIndex
].mInsertionPoint
;
1924 // XXX: I have yet to see a case where numFlushed is non-zero and
1925 // insertionPoint is not -1, but this code will try to handle
1928 PRUint32 oldUpdates
= mUpdatesInNotification
;
1929 mUpdatesInNotification
= 0;
1930 if (insertionPoint
!= -1) {
1931 NotifyInsert(parent
, mBody
, insertionPoint
- 1);
1933 NotifyAppend(parent
, numFlushed
);
1935 mCurrentContext
->mStack
[parentIndex
].mNumFlushed
= childCount
;
1936 if (mUpdatesInNotification
> 1) {
1937 UpdateChildCounts();
1939 mUpdatesInNotification
= oldUpdates
;
1942 StartLayout(PR_FALSE
);
1948 HTMLContentSink::CloseBody()
1950 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
1951 "HTMLContentSink::CloseBody",
1953 mCurrentContext
->mStackPos
- 1,
1957 nsresult rv
= mCurrentContext
->FlushTextAndRelease(&didFlush
);
1958 if (NS_FAILED(rv
)) {
1962 // Flush out anything that's left
1963 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
1964 ("HTMLContentSink::CloseBody: layout final body content"));
1966 mCurrentContext
->FlushTags();
1967 mCurrentContext
->CloseContainer(eHTMLTag_body
, PR_FALSE
);
1973 HTMLContentSink::OpenForm(const nsIParserNode
& aNode
)
1975 nsresult result
= NS_OK
;
1977 mCurrentContext
->FlushTextAndRelease();
1979 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
1980 "HTMLContentSink::OpenForm",
1982 mCurrentContext
->mStackPos
,
1985 // Close out previous form if it's there. If there is one
1986 // around, it's probably because the last one wasn't well-formed.
1987 mCurrentForm
= nsnull
;
1989 // Check if the parent is a table, tbody, thead, tfoot, tr, col or
1990 // colgroup. If so, we fix up by making the form leaf content.
1991 if (mCurrentContext
->IsCurrentContainer(eHTMLTag_table
) ||
1992 mCurrentContext
->IsCurrentContainer(eHTMLTag_tbody
) ||
1993 mCurrentContext
->IsCurrentContainer(eHTMLTag_thead
) ||
1994 mCurrentContext
->IsCurrentContainer(eHTMLTag_tfoot
) ||
1995 mCurrentContext
->IsCurrentContainer(eHTMLTag_tr
) ||
1996 mCurrentContext
->IsCurrentContainer(eHTMLTag_col
) ||
1997 mCurrentContext
->IsCurrentContainer(eHTMLTag_colgroup
)) {
1998 result
= mCurrentContext
->AddLeaf(aNode
);
2000 mFormOnStack
= PR_TRUE
;
2001 // Otherwise the form can be a content parent.
2002 result
= mCurrentContext
->OpenContainer(aNode
);
2009 HTMLContentSink::CloseForm()
2011 nsresult result
= NS_OK
;
2013 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
2014 "HTMLContentSink::CloseForm",
2016 mCurrentContext
->mStackPos
- 1,
2020 // if this is a well-formed form, close it too
2021 if (mCurrentContext
->IsCurrentContainer(eHTMLTag_form
)) {
2022 result
= mCurrentContext
->CloseContainer(eHTMLTag_form
, PR_FALSE
);
2023 mFormOnStack
= PR_FALSE
;
2026 mCurrentForm
= nsnull
;
2033 HTMLContentSink::OpenFrameset(const nsIParserNode
& aNode
)
2035 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
2036 "HTMLContentSink::OpenFrameset",
2038 mCurrentContext
->mStackPos
,
2041 CloseHeadContext(); // do this just in case if the HEAD was left open!
2043 // Need to keep track of whether OpenContainer changes mFrameset
2044 nsGenericHTMLElement
* oldFrameset
= mFrameset
;
2045 nsresult rv
= mCurrentContext
->OpenContainer(aNode
);
2046 PRBool isFirstFrameset
= NS_SUCCEEDED(rv
) && mFrameset
!= oldFrameset
;
2048 if (isFirstFrameset
&& mCurrentContext
->mStackPos
> 1) {
2049 NS_ASSERTION(mFrameset
, "Must have frameset!");
2050 // Have to notify for the frameset now, since we never actually
2051 // close out <html>, so won't notify for it then.
2052 PRInt32 parentIndex
= mCurrentContext
->mStackPos
- 2;
2053 nsGenericHTMLElement
*parent
= mCurrentContext
->mStack
[parentIndex
].mContent
;
2054 PRInt32 numFlushed
= mCurrentContext
->mStack
[parentIndex
].mNumFlushed
;
2055 PRInt32 childCount
= parent
->GetChildCount();
2056 NS_ASSERTION(numFlushed
< childCount
, "Already notified on the frameset?");
2058 PRInt32 insertionPoint
=
2059 mCurrentContext
->mStack
[parentIndex
].mInsertionPoint
;
2061 // XXX: I have yet to see a case where numFlushed is non-zero and
2062 // insertionPoint is not -1, but this code will try to handle
2065 PRUint32 oldUpdates
= mUpdatesInNotification
;
2066 mUpdatesInNotification
= 0;
2067 if (insertionPoint
!= -1) {
2068 NotifyInsert(parent
, mFrameset
, insertionPoint
- 1);
2070 NotifyAppend(parent
, numFlushed
);
2072 mCurrentContext
->mStack
[parentIndex
].mNumFlushed
= childCount
;
2073 if (mUpdatesInNotification
> 1) {
2074 UpdateChildCounts();
2076 mUpdatesInNotification
= oldUpdates
;
2083 HTMLContentSink::CloseFrameset()
2085 SINK_TRACE_NODE(SINK_TRACE_CALLS
,
2086 "HTMLContentSink::CloseFrameset",
2088 mCurrentContext
->mStackPos
- 1,
2091 SinkContext
* sc
= mCurrentContext
;
2092 nsGenericHTMLElement
* fs
= sc
->mStack
[sc
->mStackPos
- 1].mContent
;
2093 PRBool done
= fs
== mFrameset
;
2098 rv
= sc
->FlushTextAndRelease(&didFlush
);
2099 if (NS_FAILED(rv
)) {
2103 // Flush out anything that's left
2104 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
2105 ("HTMLContentSink::CloseFrameset: layout final content"));
2110 rv
= sc
->CloseContainer(eHTMLTag_frameset
, PR_FALSE
);
2112 if (done
&& mFramesEnabled
) {
2113 StartLayout(PR_FALSE
);
2120 HTMLContentSink::IsEnabled(PRInt32 aTag
, PRBool
* aReturn
)
2122 nsHTMLTag theHTMLTag
= nsHTMLTag(aTag
);
2124 if (theHTMLTag
== eHTMLTag_script
) {
2125 *aReturn
= mScriptEnabled
;
2126 } else if (theHTMLTag
== eHTMLTag_frameset
) {
2127 *aReturn
= mFramesEnabled
;
2129 *aReturn
= PR_FALSE
;
2136 HTMLContentSink::OpenContainer(const nsIParserNode
& aNode
)
2138 nsresult rv
= NS_OK
;
2140 switch (aNode
.GetNodeType()) {
2141 case eHTMLTag_frameset
:
2142 rv
= OpenFrameset(aNode
);
2145 rv
= OpenHeadContext();
2146 if (NS_SUCCEEDED(rv
)) {
2147 rv
= AddAttributes(aNode
, mHead
, PR_TRUE
, mHaveSeenHead
);
2148 mHaveSeenHead
= PR_TRUE
;
2152 rv
= OpenBody(aNode
);
2156 // If we've already hit this code once, need to check for
2157 // already-present attributes on the root.
2158 AddAttributes(aNode
, mRoot
, PR_TRUE
, mNotifiedRootInsertion
);
2159 if (!mNotifiedRootInsertion
) {
2160 NotifyRootInsertion();
2162 ProcessOfflineManifest(mRoot
);
2166 rv
= OpenForm(aNode
);
2169 rv
= mCurrentContext
->OpenContainer(aNode
);
2177 HTMLContentSink::CloseContainer(const eHTMLTags aTag
)
2179 nsresult rv
= NS_OK
;
2182 case eHTMLTag_frameset
:
2183 rv
= CloseFrameset();
2198 rv
= mCurrentContext
->CloseContainer(aTag
, PR_FALSE
);
2206 HTMLContentSink::CloseMalformedContainer(const eHTMLTags aTag
)
2208 return mCurrentContext
->CloseContainer(aTag
, PR_TRUE
);
2212 HTMLContentSink::AddLeaf(const nsIParserNode
& aNode
)
2216 nsHTMLTag nodeType
= nsHTMLTag(aNode
.GetNodeType());
2219 mCurrentContext
->FlushTextAndRelease();
2220 rv
= ProcessLINKTag(aNode
);
2224 rv
= mCurrentContext
->AddLeaf(aNode
);
2233 * This gets called by the parsing system when we find a comment
2234 * @update gess11/9/98
2235 * @param aNode contains a comment token
2236 * @return error code
2239 HTMLContentSink::AddComment(const nsIParserNode
& aNode
)
2241 nsresult rv
= mCurrentContext
->AddComment(aNode
);
2247 * This gets called by the parsing system when we find a PI
2248 * @update gess11/9/98
2249 * @param aNode contains a comment token
2250 * @return error code
2253 HTMLContentSink::AddProcessingInstruction(const nsIParserNode
& aNode
)
2255 nsresult result
= NS_OK
;
2256 // Implementation of AddProcessingInstruction() should start here
2262 * This gets called by the parser when it encounters
2263 * a DOCTYPE declaration in the HTML document.
2267 HTMLContentSink::AddDocTypeDecl(const nsIParserNode
& aNode
)
2269 nsAutoString
docTypeStr(aNode
.GetText());
2270 nsresult rv
= NS_OK
;
2272 PRInt32 publicStart
= docTypeStr
.Find("PUBLIC", PR_TRUE
);
2273 PRInt32 systemStart
= docTypeStr
.Find("SYSTEM", PR_TRUE
);
2274 nsAutoString name
, publicId
, systemId
;
2276 if (publicStart
>= 0 || systemStart
>= 0) {
2278 * If we find the keyword 'PUBLIC' after the keyword 'SYSTEM' we assume
2279 * that we got a system id that happens to contain the string "PUBLIC"
2280 * and we ignore that as the start of the public id.
2282 if (systemStart
>= 0 && (publicStart
> systemStart
)) {
2287 * We found 'PUBLIC' or 'SYSTEM' in the doctype, put everything before
2288 * the first one of those in name.
2290 docTypeStr
.Mid(name
, 0, publicStart
>= 0 ? publicStart
: systemStart
);
2292 if (publicStart
>= 0) {
2293 // We did find 'PUBLIC'
2294 docTypeStr
.Mid(publicId
, publicStart
+ 6,
2295 docTypeStr
.Length() - publicStart
);
2296 publicId
.Trim(" \t\n\r");
2299 PRUnichar ch
= publicId
.IsEmpty() ? '\0' : publicId
.First();
2301 PRBool hasQuote
= PR_FALSE
;
2302 if (ch
== '"' || ch
== '\'') {
2305 PRInt32 end
= publicId
.FindChar(ch
);
2309 * We didn't find an end quote, so just make sure we cut off
2310 * the '>' on the end of the doctype declaration
2313 end
= publicId
.FindChar('>');
2319 * If we didn't find a closing quote or a '>' we leave publicId as
2323 publicId
.Truncate(end
);
2326 // No quotes, ignore the public id
2327 publicId
.Truncate();
2331 * Make sure the 'SYSTEM' word we found is not inside the pubilc id
2333 PRInt32 pos
= docTypeStr
.Find(publicId
);
2335 if (systemStart
> 0) {
2336 if (systemStart
< pos
+ (PRInt32
)publicId
.Length()) {
2337 systemStart
= docTypeStr
.Find("SYSTEM", PR_TRUE
,
2338 pos
+ publicId
.Length());
2343 * If we didn't find 'SYSTEM' we treat everything after the public id
2346 if (systemStart
< 0) {
2347 // 1 is the end quote
2348 systemStart
= pos
+ publicId
.Length() + (hasQuote
? 1 : 0);
2352 if (systemStart
>= 0) {
2353 // We either found 'SYSTEM' or we have data after the public id
2354 docTypeStr
.Mid(systemId
, systemStart
,
2355 docTypeStr
.Length() - systemStart
);
2357 // Strip off 'SYSTEM' if we have it.
2358 if (StringBeginsWith(systemId
, NS_LITERAL_STRING("SYSTEM")))
2361 systemId
.Trim(" \t\n\r");
2364 PRUnichar ch
= systemId
.IsEmpty() ? '\0' : systemId
.First();
2366 if (ch
== '"' || ch
== '\'') {
2369 PRInt32 end
= systemId
.FindChar(ch
);
2372 // We didn't find an end quote, then we just make sure we
2373 // cut of the '>' on the end of the doctype declaration
2375 end
= systemId
.FindChar('>');
2378 // If we found an closing quote nor a '>' we truncate systemId
2381 systemId
.Truncate(end
);
2384 systemId
.Truncate();
2388 name
.Assign(docTypeStr
);
2391 // Cut out "<!DOCTYPE" or "DOCTYPE" from the name.
2392 if (StringBeginsWith(name
, NS_LITERAL_STRING("<!DOCTYPE"), nsCaseInsensitiveStringComparator())) {
2394 } else if (StringBeginsWith(name
, NS_LITERAL_STRING("DOCTYPE"), nsCaseInsensitiveStringComparator())) {
2398 name
.Trim(" \t\n\r");
2400 // Check if name contains whitespace chars. If it does and the first
2401 // char is not a quote, we set the name to contain the characters
2402 // before the whitespace
2403 PRInt32 nameEnd
= 0;
2405 if (name
.IsEmpty() || (name
.First() != '"' && name
.First() != '\'')) {
2406 nameEnd
= name
.FindCharInSet(" \n\r\t");
2409 // If we didn't find a public id we grab everything after the name
2410 // and use that as public id.
2411 if (publicStart
< 0) {
2412 name
.Mid(publicId
, nameEnd
, name
.Length() - nameEnd
);
2413 publicId
.Trim(" \t\n\r");
2415 PRUnichar ch
= publicId
.IsEmpty() ? '\0' : publicId
.First();
2417 if (ch
== '"' || ch
== '\'') {
2420 PRInt32 publicEnd
= publicId
.FindChar(ch
);
2422 if (publicEnd
< 0) {
2423 publicEnd
= publicId
.FindChar('>');
2426 if (publicEnd
< 0) {
2427 publicEnd
= publicId
.Length();
2430 publicId
.Truncate(publicEnd
);
2432 // No quotes, no public id
2433 publicId
.Truncate();
2438 name
.Truncate(nameEnd
);
2440 nameEnd
= name
.FindChar('>');
2443 name
.Truncate(nameEnd
);
2447 if (!publicId
.IsEmpty() || !systemId
.IsEmpty() || !name
.IsEmpty()) {
2448 nsCOMPtr
<nsIDOMDocumentType
> oldDocType
;
2449 nsCOMPtr
<nsIDOMDocumentType
> docType
;
2451 nsCOMPtr
<nsIDOMDocument
> doc(do_QueryInterface(mHTMLDocument
));
2452 doc
->GetDoctype(getter_AddRefs(oldDocType
));
2454 // Assign "HTML" if we don't have anything, and normalize
2455 // the name if it is something like "hTmL", per HTML5.
2456 if (name
.IsEmpty() || name
.LowerCaseEqualsLiteral("html")) {
2457 name
.AssignLiteral("HTML");
2460 nsCOMPtr
<nsIAtom
> nameAtom
= do_GetAtom(name
);
2462 return NS_ERROR_OUT_OF_MEMORY
;
2465 // Indicate that there is no internal subset (not just an empty one)
2466 nsAutoString voidString
;
2467 voidString
.SetIsVoid(PR_TRUE
);
2468 rv
= NS_NewDOMDocumentType(getter_AddRefs(docType
),
2469 mDocument
->NodeInfoManager(), nsnull
,
2470 nameAtom
, nsnull
, nsnull
, publicId
, systemId
,
2472 NS_ENSURE_SUCCESS(rv
, rv
);
2475 // If we already have a doctype we replace the old one.
2476 nsCOMPtr
<nsIDOMNode
> tmpNode
;
2477 rv
= doc
->ReplaceChild(oldDocType
, docType
, getter_AddRefs(tmpNode
));
2479 // If we don't already have one we insert it as the first child,
2480 // this might not be 100% correct but since this is called from
2481 // the content sink we assume that this is what we want.
2482 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(docType
);
2483 NS_ASSERTION(content
, "Doctype isn't content?");
2485 mDocument
->InsertChildAt(content
, 0, PR_TRUE
);
2493 HTMLContentSink::DidProcessTokens(void)
2499 HTMLContentSink::WillProcessAToken(void)
2505 HTMLContentSink::DidProcessAToken(void)
2507 return DidProcessATokenImpl();
2511 HTMLContentSink::WillInterrupt()
2513 return WillInterruptImpl();
2517 HTMLContentSink::WillResume()
2519 return WillResumeImpl();
2523 HTMLContentSink::NotifyTagObservers(nsIParserNode
* aNode
)
2526 // Inform observers that we're handling a document.write().
2527 // This information is necessary for the charset observer, atleast,
2528 // to make a decision whether a new charset loading is required or not.
2536 if (mHTMLDocument
&& mHTMLDocument
->IsWriting()) {
2537 flag
= nsIElementObserver::IS_DOCUMENT_WRITE
;
2540 return mObservers
->Notify(aNode
, mParser
, mDocShell
, flag
);
2544 HTMLContentSink::StartLayout(PRBool aIgnorePendingSheets
)
2546 if (mLayoutStarted
) {
2550 mHTMLDocument
->SetIsFrameset(mFrameset
!= nsnull
);
2552 nsContentSink::StartLayout(aIgnorePendingSheets
);
2556 HTMLContentSink::OpenHeadContext()
2558 if (mCurrentContext
&& mCurrentContext
->IsCurrentContainer(eHTMLTag_head
))
2561 // Flush everything in the current context so that we don't have
2562 // to worry about insertions resulting in inconsistent frame creation.
2564 // Try to do this only if needed (costly), i.e., only if we are sure
2565 // we are changing contexts from some other context to the head.
2567 // PERF: This call causes approximately a 2% slowdown in page load time
2568 // according to jrgm's page load tests, but seems to be a necessary evil
2569 if (mCurrentContext
&& (mCurrentContext
!= mHeadContext
)) {
2570 mCurrentContext
->FlushTags();
2573 if (!mHeadContext
) {
2574 mHeadContext
= new SinkContext(this);
2575 NS_ENSURE_TRUE(mHeadContext
, NS_ERROR_OUT_OF_MEMORY
);
2577 nsresult rv
= mHeadContext
->Begin(eHTMLTag_head
, mHead
, 0, -1);
2578 NS_ENSURE_SUCCESS(rv
, rv
);
2581 mContextStack
.AppendElement(mCurrentContext
);
2582 mCurrentContext
= mHeadContext
;
2588 HTMLContentSink::CloseHeadContext()
2590 if (mCurrentContext
) {
2591 if (!mCurrentContext
->IsCurrentContainer(eHTMLTag_head
))
2594 mCurrentContext
->FlushTextAndRelease();
2595 mCurrentContext
->FlushTags();
2598 if (!mContextStack
.IsEmpty())
2600 PRUint32 n
= mContextStack
.Length() - 1;
2601 mCurrentContext
= mContextStack
.ElementAt(n
);
2602 mContextStack
.RemoveElementAt(n
);
2607 HTMLContentSink::ProcessLINKTag(const nsIParserNode
& aNode
)
2609 nsresult result
= NS_OK
;
2611 if (mCurrentContext
) {
2612 // Create content object
2613 nsCOMPtr
<nsIContent
> element
;
2614 nsCOMPtr
<nsINodeInfo
> nodeInfo
;
2615 nodeInfo
= mNodeInfoManager
->GetNodeInfo(nsGkAtoms::link
, nsnull
, kNameSpaceID_XHTML
);
2617 result
= NS_NewHTMLElement(getter_AddRefs(element
), nodeInfo
, PR_FALSE
);
2618 NS_ENSURE_SUCCESS(result
, result
);
2620 nsCOMPtr
<nsIStyleSheetLinkingElement
> ssle(do_QueryInterface(element
));
2623 // XXX need prefs. check here.
2624 if (!mInsideNoXXXTag
) {
2625 ssle
->InitStyleLinkElement(PR_FALSE
);
2626 ssle
->SetEnableUpdates(PR_FALSE
);
2628 ssle
->InitStyleLinkElement(PR_TRUE
);
2632 // Add in the attributes and add the style content object to the
2634 result
= AddAttributes(aNode
, element
);
2635 if (NS_FAILED(result
)) {
2639 mCurrentContext
->AddLeaf(element
); // <link>s are leaves
2642 ssle
->SetEnableUpdates(PR_TRUE
);
2645 result
= ssle
->UpdateStyleSheet(this, &willNotify
, &isAlternate
);
2646 if (NS_SUCCEEDED(result
) && willNotify
&& !isAlternate
) {
2647 ++mPendingSheetCount
;
2648 mScriptLoader
->AddExecuteBlocker();
2651 // look for <link rel="next" href="url">
2652 nsAutoString relVal
;
2653 element
->GetAttr(kNameSpaceID_None
, nsGkAtoms::rel
, relVal
);
2654 if (!relVal
.IsEmpty()) {
2655 // XXX seems overkill to generate this string array
2656 nsAutoTArray
<nsString
, 4> linkTypes
;
2657 nsStyleLinkElement::ParseLinkTypes(relVal
, linkTypes
);
2658 PRBool hasPrefetch
= linkTypes
.Contains(NS_LITERAL_STRING("prefetch"));
2659 if (hasPrefetch
|| linkTypes
.Contains(NS_LITERAL_STRING("next"))) {
2660 nsAutoString hrefVal
;
2661 element
->GetAttr(kNameSpaceID_None
, nsGkAtoms::href
, hrefVal
);
2662 if (!hrefVal
.IsEmpty()) {
2663 PrefetchHref(hrefVal
, element
, hasPrefetch
);
2666 if (linkTypes
.Contains(NS_LITERAL_STRING("dns-prefetch"))) {
2667 nsAutoString hrefVal
;
2668 element
->GetAttr(kNameSpaceID_None
, nsGkAtoms::href
, hrefVal
);
2669 if (!hrefVal
.IsEmpty()) {
2670 PrefetchDNS(hrefVal
);
2682 HTMLContentSink::ForceReflow()
2684 mCurrentContext
->FlushTags();
2689 HTMLContentSink::NotifyInsert(nsIContent
* aContent
,
2690 nsIContent
* aChildContent
,
2691 PRInt32 aIndexInContainer
)
2693 if (aContent
&& aContent
->GetCurrentDoc() != mDocument
) {
2694 // aContent is not actually in our document anymore.... Just bail out of
2695 // here; notifying on our document for this insert would be wrong.
2702 // Scope so we call EndUpdate before we decrease mInNotification
2703 MOZ_AUTO_DOC_UPDATE(mDocument
, UPDATE_CONTENT_MODEL
, !mBeganUpdate
);
2704 nsNodeUtils::ContentInserted(NODE_FROM(aContent
, mDocument
),
2705 aChildContent
, aIndexInContainer
);
2706 mLastNotificationTime
= PR_Now();
2713 HTMLContentSink::NotifyRootInsertion()
2715 NS_PRECONDITION(!mNotifiedRootInsertion
, "Double-notifying on root?");
2716 NS_ASSERTION(!mLayoutStarted
,
2717 "How did we start layout without notifying on root?");
2718 // Now make sure to notify that we have now inserted our root. If
2719 // there has been no initial reflow yet it'll be a no-op, but if
2720 // there has been one we need this to get its frames constructed.
2721 // Note that if mNotifiedRootInsertion is true we don't notify here,
2722 // since that just means there are multiple <html> tags in the
2723 // document; in those cases we just want to put all the attrs on one
2725 mNotifiedRootInsertion
= PR_TRUE
;
2726 PRInt32 index
= mDocument
->IndexOf(mRoot
);
2727 NS_ASSERTION(index
!= -1, "mRoot not child of document?");
2728 NotifyInsert(nsnull
, mRoot
, index
);
2730 // Now update the notification information in all our
2731 // contexts, since we just inserted the root and notified on
2733 UpdateChildCounts();
2737 HTMLContentSink::IsMonolithicContainer(nsHTMLTag aTag
)
2739 if (aTag
== eHTMLTag_tr
||
2740 aTag
== eHTMLTag_select
||
2741 aTag
== eHTMLTag_applet
||
2742 aTag
== eHTMLTag_object
) {
2750 HTMLContentSink::UpdateChildCounts()
2752 PRUint32 numContexts
= mContextStack
.Length();
2753 for (PRUint32 i
= 0; i
< numContexts
; i
++) {
2754 SinkContext
* sc
= mContextStack
.ElementAt(i
);
2756 sc
->UpdateChildCounts();
2759 mCurrentContext
->UpdateChildCounts();
2763 HTMLContentSink::PreEvaluateScript()
2765 // Eagerly append all pending elements (including the current body child)
2766 // to the body (so that they can be seen by scripts) and force reflow.
2767 SINK_TRACE(gSinkLogModuleInfo
, SINK_TRACE_CALLS
,
2768 ("HTMLContentSink::PreEvaluateScript: flushing tags before "
2769 "evaluating script"));
2771 // XXX Should this call FlushTags()?
2772 mCurrentContext
->FlushText();
2776 HTMLContentSink::PostEvaluateScript(nsIScriptElement
*aElement
)
2778 mHTMLDocument
->ScriptExecuted(aElement
);
2782 HTMLContentSink::ProcessSCRIPTEndTag(nsGenericHTMLElement
*content
,
2785 // Flush all tags up front so that we are in as stable state as possible
2786 // when calling DoneAddingChildren. This may not be strictly needed since
2787 // any ScriptAvailable calls will cause us to flush anyway. But it gives a
2788 // warm fuzzy feeling to be in a stable state before even attempting to
2790 // It would however be needed if we properly called BeginUpdate and
2791 // EndUpdate while we were inserting stuff into the DOM.
2793 // XXX Should this call FlushTags()?
2794 mCurrentContext
->FlushText();
2796 nsCOMPtr
<nsIScriptElement
> sele
= do_QueryInterface(content
);
2797 NS_ASSERTION(sele
, "Not really closing a script tag?");
2800 // Make sure to serialize this script correctly, for nice round tripping.
2801 sele
->SetIsMalformed();
2804 sele
->PreventExecution();
2807 // Notify our document that we're loading this script.
2808 mHTMLDocument
->ScriptLoading(sele
);
2810 // Now tell the script that it's ready to go. This may execute the script
2811 // or return NS_ERROR_HTMLPARSER_BLOCK. Or neither if the script doesn't
2813 nsresult rv
= content
->DoneAddingChildren(PR_TRUE
);
2815 // If the act of insertion evaluated the script, we're fine.
2816 // Else, block the parser till the script has loaded.
2817 if (rv
== NS_ERROR_HTMLPARSER_BLOCK
) {
2818 // If this append fails we'll never unblock the parser, but the UI will
2819 // still remain responsive. There are other ways to deal with this, but
2820 // the end result is always that the page gets botched, so there is no
2821 // real point in making it more complicated.
2822 mScriptElements
.AppendObject(sele
);
2825 // This may have already happened if the script executed, but in case
2826 // it didn't then remove the element so that it doesn't get stuck forever.
2827 mHTMLDocument
->ScriptExecuted(sele
);
2830 // If the parser got blocked, make sure to return the appropriate rv.
2831 // I'm not sure if this is actually needed or not.
2832 if (mParser
&& !mParser
->IsParserEnabled()) {
2833 rv
= NS_ERROR_HTMLPARSER_BLOCK
;
2839 // 3 ways to load a style sheet: inline, style src=, link tag
2840 // XXX What does nav do if we have SRC= and some style data inline?
2843 HTMLContentSink::ProcessSTYLEEndTag(nsGenericHTMLElement
* content
)
2845 nsCOMPtr
<nsIStyleSheetLinkingElement
> ssle
= do_QueryInterface(content
);
2848 "html:style doesn't implement nsIStyleSheetLinkingElement");
2850 nsresult rv
= NS_OK
;
2853 // Note: if we are inside a noXXX tag, then we init'ed this style element
2854 // with mDontLoadStyle = PR_TRUE, so these two calls will have no effect.
2855 ssle
->SetEnableUpdates(PR_TRUE
);
2858 rv
= ssle
->UpdateStyleSheet(this, &willNotify
, &isAlternate
);
2859 if (NS_SUCCEEDED(rv
) && willNotify
&& !isAlternate
) {
2860 ++mPendingSheetCount
;
2861 mScriptLoader
->AddExecuteBlocker();
2869 HTMLContentSink::FlushPendingNotifications(mozFlushType aType
)
2871 // Only flush tags if we're not doing the notification ourselves
2872 // (since we aren't reentrant)
2873 if (!mInNotification
) {
2874 // Only flush if we're still a document observer (so that our child counts
2875 // should be correct).
2876 if (mIsDocumentObserver
) {
2877 if (aType
>= Flush_ContentAndNotify
) {
2880 else if (mCurrentContext
) {
2881 mCurrentContext
->FlushText();
2884 if (aType
>= Flush_InterruptibleLayout
) {
2885 // Make sure that layout has started so that the reflow flush
2886 // will actually happen.
2887 StartLayout(PR_TRUE
);
2893 HTMLContentSink::FlushTags()
2895 if (!mNotifiedRootInsertion
) {
2896 NotifyRootInsertion();
2900 return mCurrentContext
? mCurrentContext
->FlushTags() : NS_OK
;
2904 HTMLContentSink::SetDocumentCharset(nsACString
& aCharset
)
2907 // the following logic to get muCV is copied from
2908 // nsHTMLDocument::StartDocumentLoad
2909 // We need to call muCV->SetPrevDocCharacterSet here in case
2910 // the charset is detected by parser DetectMetaTag
2911 nsCOMPtr
<nsIMarkupDocumentViewer
> muCV
;
2912 nsCOMPtr
<nsIContentViewer
> cv
;
2913 mDocShell
->GetContentViewer(getter_AddRefs(cv
));
2915 muCV
= do_QueryInterface(cv
);
2917 // in this block of code, if we get an error result, we return
2918 // it but if we get a null pointer, that's perfectly legal for
2919 // parent and parentContentViewer
2921 nsCOMPtr
<nsIDocShellTreeItem
> docShellAsItem
=
2922 do_QueryInterface(mDocShell
);
2923 NS_ENSURE_TRUE(docShellAsItem
, NS_ERROR_FAILURE
);
2925 nsCOMPtr
<nsIDocShellTreeItem
> parentAsItem
;
2926 docShellAsItem
->GetSameTypeParent(getter_AddRefs(parentAsItem
));
2928 nsCOMPtr
<nsIDocShell
> parent(do_QueryInterface(parentAsItem
));
2930 nsCOMPtr
<nsIContentViewer
> parentContentViewer
;
2932 parent
->GetContentViewer(getter_AddRefs(parentContentViewer
));
2933 if (NS_SUCCEEDED(rv
) && parentContentViewer
) {
2934 muCV
= do_QueryInterface(parentContentViewer
);
2940 muCV
->SetPrevDocCharacterSet(aCharset
);
2945 mDocument
->SetDocumentCharacterSet(aCharset
);
2952 HTMLContentSink::GetTarget()
2958 HTMLContentSink::IsScriptExecuting()
2960 return IsScriptExecutingImpl();
2965 * This will dump content model into the output file.
2967 * @update harishd 05/25/00
2969 * @return NS_OK all went well, error on failure
2973 HTMLContentSink::DumpContentModel()
2975 FILE* out
= ::fopen("rtest_html.txt", "a");
2978 Element
* root
= mDocument
->GetRootElement();
2982 mDocumentURI
->GetSpec(buf
);
2983 fputs(buf
.get(), out
);
2987 root
->DumpContent(out
, 0, PR_FALSE
);