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
5 : *
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/
10 : *
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.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
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.
22 : *
23 : * Contributor(s):
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : * Peter Annema <disttsc@bart.nl>
26 : * Daniel Glazman <glazman@netscape.com>
27 : * Henri Sivonen <hsivonen@iki.fi>
28 : *
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.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #include "mozilla/Util.h"
44 :
45 : #include "nsContentSink.h"
46 : #include "nsCOMPtr.h"
47 : #include "nsReadableUtils.h"
48 : #include "nsUnicharUtils.h"
49 : #include "nsIHTMLContentSink.h"
50 : #include "nsIInterfaceRequestor.h"
51 : #include "nsIInterfaceRequestorUtils.h"
52 : #include "nsIParser.h"
53 : #include "nsScriptLoader.h"
54 : #include "nsIURI.h"
55 : #include "nsNetUtil.h"
56 : #include "nsIContentViewer.h"
57 : #include "nsIMarkupDocumentViewer.h"
58 : #include "nsINodeInfo.h"
59 : #include "nsHTMLTokens.h"
60 : #include "nsIAppShell.h"
61 : #include "nsCRT.h"
62 : #include "prtime.h"
63 : #include "prlog.h"
64 : #include "nsNodeUtils.h"
65 : #include "nsIContent.h"
66 : #include "mozilla/dom/Element.h"
67 : #include "mozilla/Preferences.h"
68 :
69 : #include "nsGenericHTMLElement.h"
70 :
71 : #include "nsIDOMDocument.h"
72 : #include "nsIDOMDocumentType.h"
73 : #include "nsIScriptElement.h"
74 :
75 : #include "nsIComponentManager.h"
76 : #include "nsIServiceManager.h"
77 :
78 : #include "nsGkAtoms.h"
79 : #include "nsContentUtils.h"
80 : #include "nsIChannel.h"
81 : #include "nsIHttpChannel.h"
82 : #include "nsIDocShell.h"
83 : #include "nsIDocument.h"
84 : #include "nsStubDocumentObserver.h"
85 : #include "nsIHTMLDocument.h"
86 : #include "nsINameSpaceManager.h"
87 : #include "nsIDOMHTMLMapElement.h"
88 : #include "nsICookieService.h"
89 : #include "nsTArray.h"
90 : #include "nsIScriptSecurityManager.h"
91 : #include "nsIPrincipal.h"
92 : #include "nsTextFragment.h"
93 : #include "nsIScriptGlobalObject.h"
94 : #include "nsIScriptGlobalObjectOwner.h"
95 :
96 : #include "nsIParserService.h"
97 :
98 : #include "nsIStyleSheetLinkingElement.h"
99 : #include "nsITimer.h"
100 : #include "nsDOMError.h"
101 : #include "nsContentPolicyUtils.h"
102 : #include "nsIScriptContext.h"
103 : #include "nsStyleLinkElement.h"
104 :
105 : #include "nsReadableUtils.h"
106 : #include "nsWeakReference.h" // nsHTMLElementFactory supports weak references
107 : #include "nsIPrompt.h"
108 : #include "nsLayoutCID.h"
109 : #include "nsIDocShellTreeItem.h"
110 :
111 : #include "nsEscape.h"
112 : #include "nsIElementObserver.h"
113 : #include "nsNodeInfoManager.h"
114 : #include "nsContentCreatorFunctions.h"
115 : #include "mozAutoDocUpdate.h"
116 :
117 : using namespace mozilla;
118 : using namespace mozilla::dom;
119 :
120 : #ifdef NS_DEBUG
121 : static PRLogModuleInfo* gSinkLogModuleInfo;
122 :
123 : #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj) \
124 : _obj->SinkTraceNode(_bit, _msg, _tag, _sp, this)
125 :
126 : #else
127 : #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj)
128 : #endif
129 :
130 : //----------------------------------------------------------------------
131 :
132 : typedef nsGenericHTMLElement*
133 : (*contentCreatorCallback)(already_AddRefed<nsINodeInfo>,
134 : FromParser aFromParser);
135 :
136 : nsGenericHTMLElement*
137 0 : NS_NewHTMLNOTUSEDElement(already_AddRefed<nsINodeInfo> aNodeInfo,
138 : FromParser aFromParser)
139 : {
140 0 : NS_NOTREACHED("The element ctor should never be called");
141 0 : return nsnull;
142 : }
143 :
144 : #define HTML_TAG(_tag, _classname) NS_NewHTML##_classname##Element,
145 : #define HTML_HTMLELEMENT_TAG(_tag) NS_NewHTMLElement,
146 : #define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement,
147 : static const contentCreatorCallback sContentCreatorCallbacks[] = {
148 : NS_NewHTMLUnknownElement,
149 : #include "nsHTMLTagList.h"
150 : #undef HTML_TAG
151 : #undef HTML_HTMLELEMENT_TAG
152 : #undef HTML_OTHER
153 : NS_NewHTMLUnknownElement
154 : };
155 :
156 : class SinkContext;
157 : class HTMLContentSink;
158 :
159 : class HTMLContentSink : public nsContentSink,
160 : #ifdef DEBUG
161 : public nsIDebugDumpContent,
162 : #endif
163 : public nsIHTMLContentSink
164 : {
165 : public:
166 : friend class SinkContext;
167 :
168 : HTMLContentSink();
169 : virtual ~HTMLContentSink();
170 :
171 0 : NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
172 :
173 : nsresult Init(nsIDocument* aDoc, nsIURI* aURI, nsISupports* aContainer,
174 : nsIChannel* aChannel);
175 :
176 : // nsISupports
177 : NS_DECL_ISUPPORTS_INHERITED
178 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentSink, nsContentSink)
179 :
180 : // nsIContentSink
181 : NS_IMETHOD WillParse(void);
182 : NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode);
183 : NS_IMETHOD DidBuildModel(bool aTerminated);
184 : NS_IMETHOD WillInterrupt(void);
185 : NS_IMETHOD WillResume(void);
186 : NS_IMETHOD SetParser(nsParserBase* aParser);
187 : virtual void FlushPendingNotifications(mozFlushType aType);
188 : NS_IMETHOD SetDocumentCharset(nsACString& aCharset);
189 : virtual nsISupports *GetTarget();
190 : virtual bool IsScriptExecuting();
191 :
192 : // nsIHTMLContentSink
193 0 : virtual bool IsAboutBlank() { return true; }
194 : NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
195 : NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
196 : NS_IMETHOD CloseMalformedContainer(const nsHTMLTag aTag);
197 : NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
198 : NS_IMETHOD DidProcessTokens(void);
199 : NS_IMETHOD WillProcessAToken(void);
200 : NS_IMETHOD DidProcessAToken(void);
201 : NS_IMETHOD BeginContext(PRInt32 aID);
202 : NS_IMETHOD EndContext(PRInt32 aID);
203 : NS_IMETHOD OpenHead();
204 : NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn);
205 :
206 : #ifdef DEBUG
207 : // nsIDebugDumpContent
208 : NS_IMETHOD DumpContentModel();
209 : #endif
210 :
211 : protected:
212 : // If aCheckIfPresent is true, will only set an attribute in cases
213 : // when it's not already set.
214 : nsresult AddAttributes(const nsIParserNode& aNode, nsIContent* aContent,
215 : bool aNotify = false,
216 : bool aCheckIfPresent = false);
217 : already_AddRefed<nsGenericHTMLElement>
218 : CreateContentObject(const nsIParserNode& aNode, nsHTMLTag aNodeType);
219 :
220 : #ifdef NS_DEBUG
221 : void SinkTraceNode(PRUint32 aBit,
222 : const char* aMsg,
223 : const nsHTMLTag aTag,
224 : PRInt32 aStackPos,
225 : void* aThis);
226 : #endif
227 :
228 : nsCOMPtr<nsIHTMLDocument> mHTMLDocument;
229 :
230 : // The maximum length of a text run
231 : PRInt32 mMaxTextRun;
232 :
233 : nsRefPtr<nsGenericHTMLElement> mRoot;
234 : nsRefPtr<nsGenericHTMLElement> mBody;
235 : nsRefPtr<nsGenericHTMLElement> mHead;
236 :
237 : nsAutoTArray<SinkContext*, 8> mContextStack;
238 : SinkContext* mCurrentContext;
239 : SinkContext* mHeadContext;
240 :
241 : // Boolean indicating whether we've seen a <head> tag that might have had
242 : // attributes once already.
243 : bool mHaveSeenHead;
244 :
245 : // Boolean indicating whether we've notified insertion of our root content
246 : // yet. We want to make sure to only do this once.
247 : bool mNotifiedRootInsertion;
248 :
249 : PRUint8 mScriptEnabled : 1;
250 : PRUint8 mFramesEnabled : 1;
251 : PRUint8 unused : 6; // bits available if someone needs one
252 :
253 : nsINodeInfo* mNodeInfoCache[NS_HTML_TAG_MAX + 1];
254 :
255 : nsresult FlushTags();
256 :
257 : // Routines for tags that require special handling
258 : nsresult CloseHTML();
259 : nsresult OpenBody(const nsIParserNode& aNode);
260 : nsresult CloseBody();
261 :
262 : nsresult OpenHeadContext();
263 : void CloseHeadContext();
264 :
265 : // nsContentSink overrides
266 : void UpdateChildCounts();
267 :
268 : void NotifyInsert(nsIContent* aContent,
269 : nsIContent* aChildContent,
270 : PRInt32 aIndexInContainer);
271 : void NotifyRootInsertion();
272 :
273 : bool IsMonolithicContainer(nsHTMLTag aTag);
274 :
275 : #ifdef NS_DEBUG
276 : void ForceReflow();
277 : #endif
278 : };
279 :
280 : class SinkContext
281 : {
282 : public:
283 : SinkContext(HTMLContentSink* aSink);
284 : ~SinkContext();
285 :
286 : nsresult Begin(nsHTMLTag aNodeType, nsGenericHTMLElement* aRoot,
287 : PRUint32 aNumFlushed, PRInt32 aInsertionPoint);
288 : nsresult OpenContainer(const nsIParserNode& aNode);
289 : nsresult CloseContainer(const nsHTMLTag aTag);
290 : nsresult AddLeaf(const nsIParserNode& aNode);
291 : nsresult AddLeaf(nsIContent* aContent);
292 : nsresult End();
293 :
294 : nsresult GrowStack();
295 : nsresult AddText(const nsAString& aText);
296 : nsresult FlushText(bool* aDidFlush = nsnull,
297 : bool aReleaseLast = false);
298 0 : nsresult FlushTextAndRelease(bool* aDidFlush = nsnull)
299 : {
300 0 : return FlushText(aDidFlush, true);
301 : }
302 :
303 : nsresult FlushTags();
304 :
305 : bool IsCurrentContainer(nsHTMLTag mType);
306 :
307 : void DidAddContent(nsIContent* aContent);
308 : void UpdateChildCounts();
309 :
310 : private:
311 : // Function to check whether we've notified for the current content.
312 : // What this actually does is check whether we've notified for all
313 : // of the parent's kids.
314 : bool HaveNotifiedForCurrentContent() const;
315 :
316 : public:
317 : HTMLContentSink* mSink;
318 : PRInt32 mNotifyLevel;
319 : nsCOMPtr<nsIContent> mLastTextNode;
320 : PRInt32 mLastTextNodeSize;
321 :
322 : struct Node {
323 : nsHTMLTag mType;
324 : nsGenericHTMLElement* mContent;
325 : PRUint32 mNumFlushed;
326 : PRInt32 mInsertionPoint;
327 :
328 : nsIContent *Add(nsIContent *child);
329 : };
330 :
331 : Node* mStack;
332 : PRInt32 mStackSize;
333 : PRInt32 mStackPos;
334 :
335 : PRUnichar* mText;
336 : PRInt32 mTextLength;
337 : PRInt32 mTextSize;
338 :
339 : private:
340 : bool mLastTextCharWasCR;
341 : };
342 :
343 : //----------------------------------------------------------------------
344 :
345 : #ifdef NS_DEBUG
346 : void
347 0 : HTMLContentSink::SinkTraceNode(PRUint32 aBit,
348 : const char* aMsg,
349 : const nsHTMLTag aTag,
350 : PRInt32 aStackPos,
351 : void* aThis)
352 : {
353 0 : if (SINK_LOG_TEST(gSinkLogModuleInfo, aBit)) {
354 0 : nsIParserService *parserService = nsContentUtils::GetParserService();
355 0 : if (!parserService)
356 0 : return;
357 :
358 0 : NS_ConvertUTF16toUTF8 tag(parserService->HTMLIdToStringTag(aTag));
359 : PR_LogPrint("%s: this=%p node='%s' stackPos=%d",
360 0 : aMsg, aThis, tag.get(), aStackPos);
361 : }
362 : }
363 : #endif
364 :
365 : nsresult
366 0 : HTMLContentSink::AddAttributes(const nsIParserNode& aNode,
367 : nsIContent* aContent, bool aNotify,
368 : bool aCheckIfPresent)
369 : {
370 : // Add tag attributes to the content attributes
371 :
372 0 : PRInt32 ac = aNode.GetAttributeCount();
373 :
374 0 : if (ac == 0) {
375 : // No attributes, nothing to do. Do an early return to avoid
376 : // constructing the nsAutoString object for nothing.
377 :
378 0 : return NS_OK;
379 : }
380 :
381 0 : nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
382 :
383 : // The attributes are on the parser node in the order they came in in the
384 : // source. What we want to happen if a single attribute is set multiple
385 : // times on an element is that the first time should "win". That is, <input
386 : // value="foo" value="bar"> should show "foo". So we loop over the
387 : // attributes backwards; this ensures that the first attribute in the set
388 : // wins. This does mean that we do some extra work in the case when the same
389 : // attribute is set multiple times, but we save a HasAttr call in the much
390 : // more common case of reasonable HTML. Note that if aCheckIfPresent is set
391 : // then we actually want to loop _forwards_ to preserve the "first attribute
392 : // wins" behavior. That does mean that when aCheckIfPresent is set the order
393 : // of attributes will get "reversed" from the point of view of the
394 : // serializer. But aCheckIfPresent is only true for malformed documents with
395 : // multiple <html>, <head>, or <body> tags, so we're doing fixup anyway at
396 : // that point.
397 :
398 : PRInt32 i, limit, step;
399 0 : if (aCheckIfPresent) {
400 0 : i = 0;
401 0 : limit = ac;
402 0 : step = 1;
403 : } else {
404 0 : i = ac - 1;
405 0 : limit = -1;
406 0 : step = -1;
407 : }
408 :
409 0 : nsAutoString key;
410 0 : for (; i != limit; i += step) {
411 : // Get lower-cased key
412 0 : nsresult rv = nsContentUtils::ASCIIToLower(aNode.GetKeyAt(i), key);
413 0 : if (NS_FAILED(rv)) {
414 0 : return rv;
415 : }
416 :
417 0 : nsCOMPtr<nsIAtom> keyAtom = do_GetAtom(key);
418 :
419 0 : if (aCheckIfPresent && aContent->HasAttr(kNameSpaceID_None, keyAtom)) {
420 0 : continue;
421 : }
422 :
423 : // Get value and remove mandatory quotes
424 : static const char* kWhitespace = "\n\r\t\b";
425 :
426 : // Bug 114997: Don't trim whitespace on <input value="...">:
427 : // Using ?: outside the function call would be more efficient, but
428 : // we don't trust ?: with references.
429 : const nsAString& v =
430 : nsContentUtils::TrimCharsInSet(
431 : (nodeType == eHTMLTag_input &&
432 0 : keyAtom == nsGkAtoms::value) ?
433 0 : "" : kWhitespace, aNode.GetValueAt(i));
434 :
435 0 : if (nodeType == eHTMLTag_a && keyAtom == nsGkAtoms::name) {
436 0 : NS_ConvertUTF16toUTF8 cname(v);
437 0 : NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
438 :
439 : // Add attribute to content
440 0 : aContent->SetAttr(kNameSpaceID_None, keyAtom, uv, aNotify);
441 : } else {
442 : // Add attribute to content
443 0 : aContent->SetAttr(kNameSpaceID_None, keyAtom, v, aNotify);
444 : }
445 : }
446 :
447 0 : return NS_OK;
448 : }
449 :
450 : /**
451 : * Factory subroutine to create all of the html content objects.
452 : */
453 : already_AddRefed<nsGenericHTMLElement>
454 0 : HTMLContentSink::CreateContentObject(const nsIParserNode& aNode,
455 : nsHTMLTag aNodeType)
456 : {
457 : // Find/create atom for the tag name
458 :
459 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
460 :
461 0 : if (aNodeType == eHTMLTag_userdefined) {
462 0 : nsAutoString lower;
463 0 : nsContentUtils::ASCIIToLower(aNode.GetText(), lower);
464 0 : nsCOMPtr<nsIAtom> name = do_GetAtom(lower);
465 : nodeInfo = mNodeInfoManager->GetNodeInfo(name, nsnull, kNameSpaceID_XHTML,
466 0 : nsIDOMNode::ELEMENT_NODE);
467 : }
468 0 : else if (mNodeInfoCache[aNodeType]) {
469 0 : nodeInfo = mNodeInfoCache[aNodeType];
470 : }
471 : else {
472 0 : nsIParserService *parserService = nsContentUtils::GetParserService();
473 0 : if (!parserService)
474 0 : return nsnull;
475 :
476 0 : nsIAtom *name = parserService->HTMLIdToAtomTag(aNodeType);
477 0 : NS_ASSERTION(name, "What? Reverse mapping of id to string broken!!!");
478 :
479 : nodeInfo = mNodeInfoManager->GetNodeInfo(name, nsnull, kNameSpaceID_XHTML,
480 0 : nsIDOMNode::ELEMENT_NODE);
481 0 : NS_IF_ADDREF(mNodeInfoCache[aNodeType] = nodeInfo);
482 : }
483 :
484 0 : NS_ENSURE_TRUE(nodeInfo, nsnull);
485 :
486 : // Make the content object
487 0 : return CreateHTMLElement(aNodeType, nodeInfo.forget(), FROM_PARSER_NETWORK);
488 : }
489 :
490 : nsresult
491 791 : NS_NewHTMLElement(nsIContent** aResult, already_AddRefed<nsINodeInfo> aNodeInfo,
492 : FromParser aFromParser)
493 : {
494 791 : *aResult = nsnull;
495 :
496 1582 : nsCOMPtr<nsINodeInfo> nodeInfo = aNodeInfo;
497 :
498 791 : nsIParserService* parserService = nsContentUtils::GetParserService();
499 791 : if (!parserService)
500 0 : return NS_ERROR_OUT_OF_MEMORY;
501 :
502 791 : nsIAtom *name = nodeInfo->NameAtom();
503 :
504 791 : NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
505 : "Trying to HTML elements that don't have the XHTML namespace");
506 :
507 : *aResult = CreateHTMLElement(parserService->
508 791 : HTMLCaseSensitiveAtomTagToId(name),
509 1582 : nodeInfo.forget(), aFromParser).get();
510 791 : return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
511 : }
512 :
513 : already_AddRefed<nsGenericHTMLElement>
514 791 : CreateHTMLElement(PRUint32 aNodeType, already_AddRefed<nsINodeInfo> aNodeInfo,
515 : FromParser aFromParser)
516 : {
517 791 : NS_ASSERTION(aNodeType <= NS_HTML_TAG_MAX ||
518 : aNodeType == eHTMLTag_userdefined,
519 : "aNodeType is out of bounds");
520 :
521 791 : contentCreatorCallback cb = sContentCreatorCallbacks[aNodeType];
522 :
523 791 : NS_ASSERTION(cb != NS_NewHTMLNOTUSEDElement,
524 : "Don't know how to construct tag element!");
525 :
526 791 : nsGenericHTMLElement* result = cb(aNodeInfo, aFromParser);
527 791 : NS_IF_ADDREF(result);
528 :
529 791 : return result;
530 : }
531 :
532 : //----------------------------------------------------------------------
533 :
534 0 : SinkContext::SinkContext(HTMLContentSink* aSink)
535 : : mSink(aSink),
536 : mNotifyLevel(0),
537 : mLastTextNodeSize(0),
538 : mStack(nsnull),
539 : mStackSize(0),
540 : mStackPos(0),
541 : mText(nsnull),
542 : mTextLength(0),
543 : mTextSize(0),
544 0 : mLastTextCharWasCR(false)
545 : {
546 0 : MOZ_COUNT_CTOR(SinkContext);
547 0 : }
548 :
549 0 : SinkContext::~SinkContext()
550 : {
551 0 : MOZ_COUNT_DTOR(SinkContext);
552 :
553 0 : if (mStack) {
554 0 : for (PRInt32 i = 0; i < mStackPos; i++) {
555 0 : NS_RELEASE(mStack[i].mContent);
556 : }
557 0 : delete [] mStack;
558 : }
559 :
560 0 : delete [] mText;
561 0 : }
562 :
563 : nsresult
564 0 : SinkContext::Begin(nsHTMLTag aNodeType,
565 : nsGenericHTMLElement* aRoot,
566 : PRUint32 aNumFlushed,
567 : PRInt32 aInsertionPoint)
568 : {
569 0 : if (mStackSize < 1) {
570 0 : nsresult rv = GrowStack();
571 0 : if (NS_FAILED(rv)) {
572 0 : return rv;
573 : }
574 : }
575 :
576 0 : mStack[0].mType = aNodeType;
577 0 : mStack[0].mContent = aRoot;
578 0 : mStack[0].mNumFlushed = aNumFlushed;
579 0 : mStack[0].mInsertionPoint = aInsertionPoint;
580 0 : NS_ADDREF(aRoot);
581 0 : mStackPos = 1;
582 0 : mTextLength = 0;
583 :
584 0 : return NS_OK;
585 : }
586 :
587 : bool
588 0 : SinkContext::IsCurrentContainer(nsHTMLTag aTag)
589 : {
590 0 : if (aTag == mStack[mStackPos - 1].mType) {
591 0 : return true;
592 : }
593 :
594 0 : return false;
595 : }
596 :
597 : void
598 0 : SinkContext::DidAddContent(nsIContent* aContent)
599 : {
600 0 : if ((mStackPos == 2) && (mSink->mBody == mStack[1].mContent)) {
601 : // We just finished adding something to the body
602 0 : mNotifyLevel = 0;
603 : }
604 :
605 : // If we just added content to a node for which
606 : // an insertion happen, we need to do an immediate
607 : // notification for that insertion.
608 0 : if (0 < mStackPos &&
609 0 : mStack[mStackPos - 1].mInsertionPoint != -1 &&
610 0 : mStack[mStackPos - 1].mNumFlushed <
611 0 : mStack[mStackPos - 1].mContent->GetChildCount()) {
612 0 : nsIContent* parent = mStack[mStackPos - 1].mContent;
613 :
614 : #ifdef NS_DEBUG
615 : // Tracing code
616 0 : nsIParserService *parserService = nsContentUtils::GetParserService();
617 0 : if (parserService) {
618 0 : nsHTMLTag tag = nsHTMLTag(mStack[mStackPos - 1].mType);
619 0 : NS_ConvertUTF16toUTF8 str(parserService->HTMLIdToStringTag(tag));
620 :
621 0 : SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
622 : ("SinkContext::DidAddContent: Insertion notification for "
623 : "parent=%s at position=%d and stackPos=%d",
624 : str.get(), mStack[mStackPos - 1].mInsertionPoint - 1,
625 : mStackPos - 1));
626 : }
627 : #endif
628 :
629 0 : PRInt32 childIndex = mStack[mStackPos - 1].mInsertionPoint - 1;
630 0 : NS_ASSERTION(parent->GetChildAt(childIndex) == aContent,
631 : "Flushing the wrong child.");
632 0 : mSink->NotifyInsert(parent, aContent, childIndex);
633 0 : mStack[mStackPos - 1].mNumFlushed = parent->GetChildCount();
634 0 : } else if (mSink->IsTimeToNotify()) {
635 0 : SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
636 : ("SinkContext::DidAddContent: Notification as a result of the "
637 : "interval expiring; backoff count: %d", mSink->mBackoffCount));
638 0 : FlushTags();
639 : }
640 0 : }
641 :
642 : nsresult
643 0 : SinkContext::OpenContainer(const nsIParserNode& aNode)
644 : {
645 0 : FlushTextAndRelease();
646 :
647 0 : SINK_TRACE_NODE(SINK_TRACE_CALLS,
648 : "SinkContext::OpenContainer",
649 : nsHTMLTag(aNode.GetNodeType()),
650 : mStackPos,
651 0 : mSink);
652 :
653 0 : if (mStackPos <= 0) {
654 0 : NS_ERROR("container w/o parent");
655 :
656 0 : return NS_ERROR_FAILURE;
657 : }
658 :
659 : nsresult rv;
660 0 : if (mStackPos + 1 > mStackSize) {
661 0 : rv = GrowStack();
662 0 : if (NS_FAILED(rv)) {
663 0 : return rv;
664 : }
665 : }
666 :
667 : // Create new container content object
668 0 : nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
669 : nsGenericHTMLElement* content =
670 0 : mSink->CreateContentObject(aNode, nodeType).get();
671 0 : if (!content) {
672 0 : return NS_ERROR_OUT_OF_MEMORY;
673 : }
674 :
675 0 : mStack[mStackPos].mType = nodeType;
676 0 : mStack[mStackPos].mContent = content;
677 0 : mStack[mStackPos].mNumFlushed = 0;
678 0 : mStack[mStackPos].mInsertionPoint = -1;
679 0 : ++mStackPos;
680 :
681 0 : rv = mSink->AddAttributes(aNode, content);
682 :
683 0 : mStack[mStackPos - 2].Add(content);
684 :
685 0 : NS_ENSURE_SUCCESS(rv, rv);
686 :
687 0 : if (mSink->IsMonolithicContainer(nodeType)) {
688 0 : mSink->mInMonolithicContainer++;
689 : }
690 :
691 : // Special handling for certain tags
692 0 : switch (nodeType) {
693 : case eHTMLTag_form:
694 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for forms.");
695 : break;
696 :
697 : case eHTMLTag_frameset:
698 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for frames.");
699 : break;
700 :
701 : case eHTMLTag_noembed:
702 : case eHTMLTag_noframes:
703 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for noembed/noframes.");
704 : break;
705 :
706 : case eHTMLTag_script:
707 : case eHTMLTag_style:
708 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for styles and scripts.");
709 : break;
710 :
711 : case eHTMLTag_button:
712 : #ifdef MOZ_MEDIA
713 : case eHTMLTag_audio:
714 : case eHTMLTag_video:
715 : #endif
716 0 : content->DoneCreatingElement();
717 0 : break;
718 :
719 : default:
720 0 : break;
721 : }
722 :
723 0 : return NS_OK;
724 : }
725 :
726 : bool
727 0 : SinkContext::HaveNotifiedForCurrentContent() const
728 : {
729 0 : if (0 < mStackPos) {
730 0 : nsIContent* parent = mStack[mStackPos - 1].mContent;
731 0 : return mStack[mStackPos-1].mNumFlushed == parent->GetChildCount();
732 : }
733 :
734 0 : return true;
735 : }
736 :
737 : nsIContent *
738 0 : SinkContext::Node::Add(nsIContent *child)
739 : {
740 0 : NS_ASSERTION(mContent, "No parent to insert/append into!");
741 0 : if (mInsertionPoint != -1) {
742 0 : NS_ASSERTION(mNumFlushed == mContent->GetChildCount(),
743 : "Inserting multiple children without flushing.");
744 0 : mContent->InsertChildAt(child, mInsertionPoint++, false);
745 : } else {
746 0 : mContent->AppendChildTo(child, false);
747 : }
748 0 : return child;
749 : }
750 :
751 : nsresult
752 0 : SinkContext::CloseContainer(const nsHTMLTag aTag)
753 : {
754 0 : nsresult result = NS_OK;
755 :
756 : // Flush any collected text content. Release the last text
757 : // node to indicate that no more should be added to it.
758 0 : FlushTextAndRelease();
759 :
760 0 : SINK_TRACE_NODE(SINK_TRACE_CALLS,
761 : "SinkContext::CloseContainer",
762 0 : aTag, mStackPos - 1, mSink);
763 :
764 0 : NS_ASSERTION(mStackPos > 0,
765 : "stack out of bounds. wrong context probably!");
766 :
767 0 : if (mStackPos <= 0) {
768 0 : return NS_OK; // Fix crash - Ref. bug 45975 or 45007
769 : }
770 :
771 0 : --mStackPos;
772 0 : nsHTMLTag nodeType = mStack[mStackPos].mType;
773 :
774 0 : NS_ASSERTION(nodeType == aTag,
775 : "Tag mismatch. Closing tag on wrong context or something?");
776 :
777 0 : nsGenericHTMLElement* content = mStack[mStackPos].mContent;
778 :
779 0 : content->Compact();
780 :
781 : // If we're in a state where we do append notifications as
782 : // we go up the tree, and we're at the level where the next
783 : // notification needs to be done, do the notification.
784 0 : if (mNotifyLevel >= mStackPos) {
785 : // Check to see if new content has been added after our last
786 : // notification
787 :
788 0 : if (mStack[mStackPos].mNumFlushed < content->GetChildCount()) {
789 : #ifdef NS_DEBUG
790 : {
791 : // Tracing code
792 0 : SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
793 : ("SinkContext::CloseContainer: reflow on notifyImmediate "
794 : "tag=%s newIndex=%d stackPos=%d",
795 : nsAtomCString(mStack[mStackPos].mContent->Tag()).get(),
796 : mStack[mStackPos].mNumFlushed, mStackPos));
797 : }
798 : #endif
799 0 : mSink->NotifyAppend(content, mStack[mStackPos].mNumFlushed);
800 0 : mStack[mStackPos].mNumFlushed = content->GetChildCount();
801 : }
802 :
803 : // Indicate that notification has now happened at this level
804 0 : mNotifyLevel = mStackPos - 1;
805 : }
806 :
807 0 : if (mSink->IsMonolithicContainer(nodeType)) {
808 0 : --mSink->mInMonolithicContainer;
809 : }
810 :
811 0 : DidAddContent(content);
812 :
813 : // Special handling for certain tags
814 0 : switch (nodeType) {
815 : case eHTMLTag_noembed:
816 : case eHTMLTag_noframes:
817 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for noembed/noframes.");
818 : break;
819 :
820 : case eHTMLTag_form:
821 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for forms.");
822 : break;
823 :
824 : #ifdef MOZ_MEDIA
825 : case eHTMLTag_video:
826 : case eHTMLTag_audio:
827 : #endif
828 : case eHTMLTag_select:
829 : case eHTMLTag_textarea:
830 : case eHTMLTag_object:
831 : case eHTMLTag_applet:
832 : case eHTMLTag_title:
833 0 : content->DoneAddingChildren(HaveNotifiedForCurrentContent());
834 0 : break;
835 :
836 : case eHTMLTag_script:
837 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink to run scripts.");
838 : result = NS_ERROR_NOT_IMPLEMENTED;
839 : break;
840 :
841 : case eHTMLTag_style:
842 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for styles.");
843 : result = NS_ERROR_NOT_IMPLEMENTED;
844 : break;
845 :
846 : default:
847 0 : break;
848 : }
849 :
850 0 : NS_IF_RELEASE(content);
851 :
852 : #ifdef DEBUG
853 0 : if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
854 0 : mSink->ForceReflow();
855 : }
856 : #endif
857 :
858 0 : return result;
859 : }
860 :
861 : nsresult
862 0 : SinkContext::AddLeaf(const nsIParserNode& aNode)
863 : {
864 0 : SINK_TRACE_NODE(SINK_TRACE_CALLS,
865 : "SinkContext::AddLeaf",
866 : nsHTMLTag(aNode.GetNodeType()),
867 0 : mStackPos, mSink);
868 :
869 0 : nsresult rv = NS_OK;
870 :
871 0 : switch (aNode.GetTokenType()) {
872 : case eToken_start:
873 : {
874 0 : FlushTextAndRelease();
875 :
876 : // Create new leaf content object
877 0 : nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
878 : nsRefPtr<nsGenericHTMLElement> content =
879 0 : mSink->CreateContentObject(aNode, nodeType);
880 0 : NS_ENSURE_TRUE(content, NS_ERROR_OUT_OF_MEMORY);
881 :
882 0 : rv = mSink->AddAttributes(aNode, content);
883 0 : NS_ENSURE_SUCCESS(rv, rv);
884 :
885 : // Add new leaf to its parent
886 0 : AddLeaf(content);
887 :
888 : // Additional processing needed once the element is in the tree
889 0 : switch (nodeType) {
890 : case eHTMLTag_meta:
891 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for metas.");
892 : rv = NS_ERROR_NOT_IMPLEMENTED;
893 : break;
894 :
895 : case eHTMLTag_input:
896 0 : content->DoneCreatingElement();
897 0 : break;
898 :
899 : case eHTMLTag_menuitem:
900 0 : content->DoneCreatingElement();
901 0 : break;
902 :
903 : default:
904 0 : break;
905 : }
906 : }
907 0 : break;
908 :
909 : case eToken_text:
910 : case eToken_whitespace:
911 : case eToken_newline:
912 0 : rv = AddText(aNode.GetText());
913 :
914 0 : break;
915 : case eToken_entity:
916 : {
917 0 : nsAutoString tmp;
918 0 : PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp);
919 0 : if (unicode < 0) {
920 0 : rv = AddText(aNode.GetText());
921 : } else {
922 : // Map carriage returns to newlines
923 0 : if (!tmp.IsEmpty()) {
924 0 : if (tmp.CharAt(0) == '\r') {
925 0 : tmp.Assign((PRUnichar)'\n');
926 : }
927 0 : rv = AddText(tmp);
928 : }
929 : }
930 : }
931 :
932 0 : break;
933 : default:
934 0 : break;
935 : }
936 :
937 0 : return rv;
938 : }
939 :
940 : nsresult
941 0 : SinkContext::AddLeaf(nsIContent* aContent)
942 : {
943 0 : NS_ASSERTION(mStackPos > 0, "leaf w/o container");
944 0 : if (mStackPos <= 0) {
945 0 : return NS_ERROR_FAILURE;
946 : }
947 :
948 0 : DidAddContent(mStack[mStackPos - 1].Add(aContent));
949 :
950 : #ifdef DEBUG
951 0 : if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
952 0 : mSink->ForceReflow();
953 : }
954 : #endif
955 :
956 0 : return NS_OK;
957 : }
958 :
959 : nsresult
960 0 : SinkContext::End()
961 : {
962 0 : for (PRInt32 i = 0; i < mStackPos; i++) {
963 0 : NS_RELEASE(mStack[i].mContent);
964 : }
965 :
966 0 : mStackPos = 0;
967 0 : mTextLength = 0;
968 :
969 0 : return NS_OK;
970 : }
971 :
972 : nsresult
973 0 : SinkContext::GrowStack()
974 : {
975 0 : PRInt32 newSize = mStackSize * 2;
976 0 : if (newSize == 0) {
977 0 : newSize = 32;
978 : }
979 :
980 0 : Node* stack = new Node[newSize];
981 0 : if (!stack) {
982 0 : return NS_ERROR_OUT_OF_MEMORY;
983 : }
984 :
985 0 : if (mStackPos != 0) {
986 0 : memcpy(stack, mStack, sizeof(Node) * mStackPos);
987 0 : delete [] mStack;
988 : }
989 :
990 0 : mStack = stack;
991 0 : mStackSize = newSize;
992 :
993 0 : return NS_OK;
994 : }
995 :
996 : /**
997 : * Add textual content to the current running text buffer. If the text buffer
998 : * overflows, flush out the text by creating a text content object and adding
999 : * it to the content tree.
1000 : */
1001 :
1002 : // XXX If we get a giant string grow the buffer instead of chopping it
1003 : // up???
1004 : nsresult
1005 0 : SinkContext::AddText(const nsAString& aText)
1006 : {
1007 0 : PRInt32 addLen = aText.Length();
1008 0 : if (addLen == 0) {
1009 0 : return NS_OK;
1010 : }
1011 :
1012 : // Create buffer when we first need it
1013 0 : if (mTextSize == 0) {
1014 0 : mText = new PRUnichar[4096];
1015 0 : if (!mText) {
1016 0 : return NS_ERROR_OUT_OF_MEMORY;
1017 : }
1018 0 : mTextSize = 4096;
1019 : }
1020 :
1021 : // Copy data from string into our buffer; flush buffer when it fills up
1022 0 : PRInt32 offset = 0;
1023 :
1024 0 : while (addLen != 0) {
1025 0 : PRInt32 amount = mTextSize - mTextLength;
1026 :
1027 0 : if (amount > addLen) {
1028 0 : amount = addLen;
1029 : }
1030 :
1031 0 : if (amount == 0) {
1032 : // Don't release last text node so we can add to it again
1033 0 : nsresult rv = FlushText();
1034 0 : if (NS_FAILED(rv)) {
1035 0 : return rv;
1036 : }
1037 :
1038 : // Go back to the top of the loop so we re-calculate amount and
1039 : // don't fall through to CopyNewlineNormalizedUnicodeTo with a
1040 : // zero-length amount (which invalidates mLastTextCharWasCR).
1041 0 : continue;
1042 : }
1043 :
1044 : mTextLength +=
1045 : nsContentUtils::CopyNewlineNormalizedUnicodeTo(aText, offset,
1046 : &mText[mTextLength],
1047 : amount,
1048 0 : mLastTextCharWasCR);
1049 0 : offset += amount;
1050 0 : addLen -= amount;
1051 : }
1052 :
1053 0 : return NS_OK;
1054 : }
1055 :
1056 : /**
1057 : * NOTE!! Forked into nsXMLContentSink. Please keep in sync.
1058 : *
1059 : * Flush all elements that have been seen so far such that
1060 : * they are visible in the tree. Specifically, make sure
1061 : * that they are all added to their respective parents.
1062 : * Also, do notification at the top for all content that
1063 : * has been newly added so that the frame tree is complete.
1064 : */
1065 : nsresult
1066 0 : SinkContext::FlushTags()
1067 : {
1068 0 : mSink->mDeferredFlushTags = false;
1069 0 : bool oldBeganUpdate = mSink->mBeganUpdate;
1070 0 : PRUint32 oldUpdates = mSink->mUpdatesInNotification;
1071 :
1072 0 : ++(mSink->mInNotification);
1073 0 : mSink->mUpdatesInNotification = 0;
1074 : {
1075 : // Scope so we call EndUpdate before we decrease mInNotification
1076 : mozAutoDocUpdate updateBatch(mSink->mDocument, UPDATE_CONTENT_MODEL,
1077 0 : true);
1078 0 : mSink->mBeganUpdate = true;
1079 :
1080 : // Don't release last text node in case we need to add to it again
1081 0 : FlushText();
1082 :
1083 : // Start from the base of the stack (growing downward) and do
1084 : // a notification from the node that is closest to the root of
1085 : // tree for any content that has been added.
1086 :
1087 : // Note that we can start at stackPos == 0 here, because it's the caller's
1088 : // responsibility to handle flushing interactions between contexts (see
1089 : // HTMLContentSink::BeginContext).
1090 0 : PRInt32 stackPos = 0;
1091 0 : bool flushed = false;
1092 : PRUint32 childCount;
1093 : nsGenericHTMLElement* content;
1094 :
1095 0 : while (stackPos < mStackPos) {
1096 0 : content = mStack[stackPos].mContent;
1097 0 : childCount = content->GetChildCount();
1098 :
1099 0 : if (!flushed && (mStack[stackPos].mNumFlushed < childCount)) {
1100 : #ifdef NS_DEBUG
1101 : {
1102 : // Tracing code
1103 0 : SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
1104 : ("SinkContext::FlushTags: tag=%s from newindex=%d at "
1105 : "stackPos=%d",
1106 : nsAtomCString(mStack[stackPos].mContent->Tag()).get(),
1107 : mStack[stackPos].mNumFlushed, stackPos));
1108 : }
1109 : #endif
1110 0 : if (mStack[stackPos].mInsertionPoint != -1) {
1111 : // We might have popped the child off our stack already
1112 : // but not notified on it yet, which is why we have to get it
1113 : // directly from its parent node.
1114 :
1115 0 : PRInt32 childIndex = mStack[stackPos].mInsertionPoint - 1;
1116 0 : nsIContent* child = content->GetChildAt(childIndex);
1117 : // Child not on stack anymore; can't assert it's correct
1118 0 : NS_ASSERTION(!(mStackPos > (stackPos + 1)) ||
1119 : (child == mStack[stackPos + 1].mContent),
1120 : "Flushing the wrong child.");
1121 0 : mSink->NotifyInsert(content, child, childIndex);
1122 : } else {
1123 0 : mSink->NotifyAppend(content, mStack[stackPos].mNumFlushed);
1124 : }
1125 :
1126 0 : flushed = true;
1127 : }
1128 :
1129 0 : mStack[stackPos].mNumFlushed = childCount;
1130 0 : stackPos++;
1131 : }
1132 0 : mNotifyLevel = mStackPos - 1;
1133 : }
1134 0 : --(mSink->mInNotification);
1135 :
1136 0 : if (mSink->mUpdatesInNotification > 1) {
1137 0 : UpdateChildCounts();
1138 : }
1139 :
1140 0 : mSink->mUpdatesInNotification = oldUpdates;
1141 0 : mSink->mBeganUpdate = oldBeganUpdate;
1142 :
1143 0 : return NS_OK;
1144 : }
1145 :
1146 : /**
1147 : * NOTE!! Forked into nsXMLContentSink. Please keep in sync.
1148 : */
1149 : void
1150 0 : SinkContext::UpdateChildCounts()
1151 : {
1152 : // Start from the top of the stack (growing upwards) and see if any
1153 : // new content has been appended. If so, we recognize that reflows
1154 : // have been generated for it and we should make sure that no
1155 : // further reflows occur. Note that we have to include stackPos == 0
1156 : // to properly notify on kids of <html>.
1157 0 : PRInt32 stackPos = mStackPos - 1;
1158 0 : while (stackPos >= 0) {
1159 0 : Node & node = mStack[stackPos];
1160 0 : node.mNumFlushed = node.mContent->GetChildCount();
1161 :
1162 0 : stackPos--;
1163 : }
1164 :
1165 0 : mNotifyLevel = mStackPos - 1;
1166 0 : }
1167 :
1168 : /**
1169 : * Flush any buffered text out by creating a text content object and
1170 : * adding it to the content.
1171 : */
1172 : nsresult
1173 0 : SinkContext::FlushText(bool* aDidFlush, bool aReleaseLast)
1174 : {
1175 0 : nsresult rv = NS_OK;
1176 0 : bool didFlush = false;
1177 :
1178 0 : if (mTextLength != 0) {
1179 0 : if (mLastTextNode) {
1180 0 : if ((mLastTextNodeSize + mTextLength) > mSink->mMaxTextRun) {
1181 0 : mLastTextNodeSize = 0;
1182 0 : mLastTextNode = nsnull;
1183 0 : FlushText(aDidFlush, aReleaseLast);
1184 : } else {
1185 0 : bool notify = HaveNotifiedForCurrentContent();
1186 : // We could probably always increase mInNotification here since
1187 : // if AppendText doesn't notify it shouldn't trigger evil code.
1188 : // But just in case it does, we don't want to mask any notifications.
1189 0 : if (notify) {
1190 0 : ++mSink->mInNotification;
1191 : }
1192 0 : rv = mLastTextNode->AppendText(mText, mTextLength, notify);
1193 0 : if (notify) {
1194 0 : --mSink->mInNotification;
1195 : }
1196 :
1197 0 : mLastTextNodeSize += mTextLength;
1198 0 : mTextLength = 0;
1199 0 : didFlush = true;
1200 : }
1201 : } else {
1202 0 : nsCOMPtr<nsIContent> textContent;
1203 0 : rv = NS_NewTextNode(getter_AddRefs(textContent),
1204 0 : mSink->mNodeInfoManager);
1205 0 : NS_ENSURE_SUCCESS(rv, rv);
1206 :
1207 0 : mLastTextNode = textContent;
1208 :
1209 : // Set the text in the text node
1210 0 : mLastTextNode->SetText(mText, mTextLength, false);
1211 :
1212 : // Eat up the rest of the text up in state.
1213 0 : mLastTextNodeSize += mTextLength;
1214 0 : mTextLength = 0;
1215 :
1216 0 : rv = AddLeaf(mLastTextNode);
1217 0 : NS_ENSURE_SUCCESS(rv, rv);
1218 :
1219 0 : didFlush = true;
1220 : }
1221 : }
1222 :
1223 0 : if (aDidFlush) {
1224 0 : *aDidFlush = didFlush;
1225 : }
1226 :
1227 0 : if (aReleaseLast) {
1228 0 : mLastTextNodeSize = 0;
1229 0 : mLastTextNode = nsnull;
1230 0 : mLastTextCharWasCR = false;
1231 : }
1232 :
1233 : #ifdef DEBUG
1234 0 : if (didFlush &&
1235 : SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
1236 0 : mSink->ForceReflow();
1237 : }
1238 : #endif
1239 :
1240 0 : return rv;
1241 : }
1242 :
1243 :
1244 : nsresult
1245 0 : NS_NewHTMLContentSink(nsIHTMLContentSink** aResult,
1246 : nsIDocument* aDoc,
1247 : nsIURI* aURI,
1248 : nsISupports* aContainer,
1249 : nsIChannel* aChannel)
1250 : {
1251 0 : NS_ENSURE_ARG_POINTER(aResult);
1252 :
1253 0 : nsRefPtr<HTMLContentSink> it = new HTMLContentSink();
1254 :
1255 0 : if (!it) {
1256 0 : return NS_ERROR_OUT_OF_MEMORY;
1257 : }
1258 :
1259 0 : nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
1260 :
1261 0 : NS_ENSURE_SUCCESS(rv, rv);
1262 :
1263 0 : *aResult = it;
1264 0 : NS_ADDREF(*aResult);
1265 :
1266 0 : return NS_OK;
1267 : }
1268 :
1269 0 : HTMLContentSink::HTMLContentSink()
1270 : {
1271 : // Note: operator new zeros our memory
1272 :
1273 :
1274 : #ifdef NS_DEBUG
1275 0 : if (!gSinkLogModuleInfo) {
1276 0 : gSinkLogModuleInfo = PR_NewLogModule("htmlcontentsink");
1277 : }
1278 : #endif
1279 0 : }
1280 :
1281 0 : HTMLContentSink::~HTMLContentSink()
1282 : {
1283 0 : if (mNotificationTimer) {
1284 0 : mNotificationTimer->Cancel();
1285 : }
1286 :
1287 0 : PRInt32 numContexts = mContextStack.Length();
1288 :
1289 0 : if (mCurrentContext == mHeadContext && numContexts > 0) {
1290 : // Pop off the second html context if it's not done earlier
1291 0 : mContextStack.RemoveElementAt(--numContexts);
1292 : }
1293 :
1294 : PRInt32 i;
1295 0 : for (i = 0; i < numContexts; i++) {
1296 0 : SinkContext* sc = mContextStack.ElementAt(i);
1297 0 : if (sc) {
1298 0 : sc->End();
1299 0 : if (sc == mCurrentContext) {
1300 0 : mCurrentContext = nsnull;
1301 : }
1302 :
1303 0 : delete sc;
1304 : }
1305 : }
1306 :
1307 0 : if (mCurrentContext == mHeadContext) {
1308 0 : mCurrentContext = nsnull;
1309 : }
1310 :
1311 0 : delete mCurrentContext;
1312 :
1313 0 : delete mHeadContext;
1314 :
1315 0 : for (i = 0; PRUint32(i) < ArrayLength(mNodeInfoCache); ++i) {
1316 0 : NS_IF_RELEASE(mNodeInfoCache[i]);
1317 : }
1318 0 : }
1319 :
1320 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLContentSink)
1321 :
1322 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLContentSink, nsContentSink)
1323 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mHTMLDocument)
1324 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
1325 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mBody)
1326 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mHead)
1327 0 : for (PRUint32 i = 0; i < ArrayLength(tmp->mNodeInfoCache); ++i) {
1328 0 : NS_IF_RELEASE(tmp->mNodeInfoCache[i]);
1329 : }
1330 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1331 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLContentSink,
1332 : nsContentSink)
1333 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHTMLDocument)
1334 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot)
1335 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mBody)
1336 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHead)
1337 0 : for (PRUint32 i = 0; i < ArrayLength(tmp->mNodeInfoCache); ++i) {
1338 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfoCache[i]");
1339 0 : cb.NoteXPCOMChild(tmp->mNodeInfoCache[i]);
1340 : }
1341 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1342 :
1343 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLContentSink)
1344 : NS_INTERFACE_TABLE_BEGIN
1345 : NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIContentSink)
1346 : NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIHTMLContentSink)
1347 : #if DEBUG
1348 : NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIDebugDumpContent)
1349 : #endif
1350 0 : NS_INTERFACE_TABLE_END
1351 0 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsContentSink)
1352 :
1353 0 : NS_IMPL_ADDREF_INHERITED(HTMLContentSink, nsContentSink)
1354 0 : NS_IMPL_RELEASE_INHERITED(HTMLContentSink, nsContentSink)
1355 :
1356 : static bool
1357 0 : IsScriptEnabled(nsIDocument *aDoc, nsIDocShell *aContainer)
1358 : {
1359 0 : NS_ENSURE_TRUE(aDoc && aContainer, true);
1360 :
1361 0 : nsCOMPtr<nsIScriptGlobalObject> globalObject = aDoc->GetScriptGlobalObject();
1362 :
1363 : // Getting context is tricky if the document hasn't had its
1364 : // GlobalObject set yet
1365 0 : if (!globalObject) {
1366 0 : nsCOMPtr<nsIScriptGlobalObjectOwner> owner = do_GetInterface(aContainer);
1367 0 : NS_ENSURE_TRUE(owner, true);
1368 :
1369 0 : globalObject = owner->GetScriptGlobalObject();
1370 0 : NS_ENSURE_TRUE(globalObject, true);
1371 : }
1372 :
1373 0 : nsIScriptContext *scriptContext = globalObject->GetContext();
1374 0 : NS_ENSURE_TRUE(scriptContext, true);
1375 :
1376 0 : JSContext* cx = scriptContext->GetNativeContext();
1377 0 : NS_ENSURE_TRUE(cx, true);
1378 :
1379 0 : bool enabled = true;
1380 0 : nsContentUtils::GetSecurityManager()->
1381 0 : CanExecuteScripts(cx, aDoc->NodePrincipal(), &enabled);
1382 0 : return enabled;
1383 : }
1384 :
1385 : nsresult
1386 0 : HTMLContentSink::Init(nsIDocument* aDoc,
1387 : nsIURI* aURI,
1388 : nsISupports* aContainer,
1389 : nsIChannel* aChannel)
1390 : {
1391 0 : NS_ENSURE_TRUE(aContainer, NS_ERROR_NULL_POINTER);
1392 :
1393 0 : nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
1394 0 : if (NS_FAILED(rv)) {
1395 0 : return rv;
1396 : }
1397 :
1398 0 : aDoc->AddObserver(this);
1399 0 : mIsDocumentObserver = true;
1400 0 : mHTMLDocument = do_QueryInterface(aDoc);
1401 :
1402 0 : NS_ASSERTION(mDocShell, "oops no docshell!");
1403 :
1404 : // Find out if subframes are enabled
1405 0 : if (mDocShell) {
1406 0 : bool subFramesEnabled = true;
1407 0 : mDocShell->GetAllowSubframes(&subFramesEnabled);
1408 0 : if (subFramesEnabled) {
1409 0 : mFramesEnabled = true;
1410 : }
1411 : }
1412 :
1413 : // Find out if scripts are enabled, if not, show <noscript> content
1414 0 : if (IsScriptEnabled(aDoc, mDocShell)) {
1415 0 : mScriptEnabled = true;
1416 : }
1417 :
1418 :
1419 : // Changed from 8192 to greatly improve page loading performance on
1420 : // large pages. See bugzilla bug 77540.
1421 0 : mMaxTextRun = Preferences::GetInt("content.maxtextrun", 8191);
1422 :
1423 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
1424 : nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nsnull,
1425 : kNameSpaceID_XHTML,
1426 0 : nsIDOMNode::ELEMENT_NODE);
1427 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
1428 :
1429 : // Make root part
1430 0 : mRoot = NS_NewHTMLHtmlElement(nodeInfo.forget());
1431 0 : if (!mRoot) {
1432 0 : return NS_ERROR_OUT_OF_MEMORY;
1433 : }
1434 :
1435 0 : NS_ASSERTION(mDocument->GetChildCount() == 0,
1436 : "Document should have no kids here!");
1437 0 : rv = mDocument->AppendChildTo(mRoot, false);
1438 0 : NS_ENSURE_SUCCESS(rv, rv);
1439 :
1440 : // Make head part
1441 : nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::head,
1442 : nsnull, kNameSpaceID_XHTML,
1443 0 : nsIDOMNode::ELEMENT_NODE);
1444 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
1445 :
1446 0 : mHead = NS_NewHTMLHeadElement(nodeInfo.forget());
1447 0 : if (NS_FAILED(rv)) {
1448 0 : return NS_ERROR_OUT_OF_MEMORY;
1449 : }
1450 :
1451 0 : mRoot->AppendChildTo(mHead, false);
1452 :
1453 0 : mCurrentContext = new SinkContext(this);
1454 0 : NS_ENSURE_TRUE(mCurrentContext, NS_ERROR_OUT_OF_MEMORY);
1455 0 : mCurrentContext->Begin(eHTMLTag_html, mRoot, 0, -1);
1456 0 : mContextStack.AppendElement(mCurrentContext);
1457 :
1458 : #ifdef NS_DEBUG
1459 0 : nsCAutoString spec;
1460 0 : (void)aURI->GetSpec(spec);
1461 0 : SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_CALLS,
1462 : ("HTMLContentSink::Init: this=%p url='%s'",
1463 : this, spec.get()));
1464 : #endif
1465 0 : return NS_OK;
1466 : }
1467 :
1468 : NS_IMETHODIMP
1469 0 : HTMLContentSink::WillParse(void)
1470 : {
1471 0 : return WillParseImpl();
1472 : }
1473 :
1474 : NS_IMETHODIMP
1475 0 : HTMLContentSink::WillBuildModel(nsDTDMode aDTDMode)
1476 : {
1477 0 : WillBuildModelImpl();
1478 :
1479 0 : if (mHTMLDocument) {
1480 0 : nsCompatibility mode = eCompatibility_NavQuirks;
1481 0 : switch (aDTDMode) {
1482 : case eDTDMode_full_standards:
1483 0 : mode = eCompatibility_FullStandards;
1484 0 : break;
1485 : case eDTDMode_almost_standards:
1486 0 : mode = eCompatibility_AlmostStandards;
1487 0 : break;
1488 : default:
1489 0 : break;
1490 : }
1491 0 : mHTMLDocument->SetCompatibilityMode(mode);
1492 : }
1493 :
1494 : // Notify document that the load is beginning
1495 0 : mDocument->BeginLoad();
1496 :
1497 0 : return NS_OK;
1498 : }
1499 :
1500 : NS_IMETHODIMP
1501 0 : HTMLContentSink::DidBuildModel(bool aTerminated)
1502 : {
1503 0 : DidBuildModelImpl(aTerminated);
1504 :
1505 : // Reflow the last batch of content
1506 0 : if (mBody) {
1507 0 : SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
1508 : ("HTMLContentSink::DidBuildModel: layout final content"));
1509 0 : mCurrentContext->FlushTags();
1510 0 : } else if (!mLayoutStarted) {
1511 : // We never saw the body, and layout never got started. Force
1512 : // layout *now*, to get an initial reflow.
1513 0 : SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
1514 : ("HTMLContentSink::DidBuildModel: forcing reflow on empty "
1515 : "document"));
1516 :
1517 : // NOTE: only force the layout if we are NOT destroying the
1518 : // docshell. If we are destroying it, then starting layout will
1519 : // likely cause us to crash, or at best waste a lot of time as we
1520 : // are just going to tear it down anyway.
1521 0 : bool bDestroying = true;
1522 0 : if (mDocShell) {
1523 0 : mDocShell->IsBeingDestroyed(&bDestroying);
1524 : }
1525 :
1526 0 : if (!bDestroying) {
1527 0 : StartLayout(false);
1528 : }
1529 : }
1530 :
1531 0 : ScrollToRef();
1532 :
1533 : // Make sure we no longer respond to document mutations. We've flushed all
1534 : // our notifications out, so there's no need to do anything else here.
1535 :
1536 : // XXXbz I wonder whether we could End() our contexts here too, or something,
1537 : // just to make sure we no longer notify... Or is the mIsDocumentObserver
1538 : // thing sufficient?
1539 0 : mDocument->RemoveObserver(this);
1540 0 : mIsDocumentObserver = false;
1541 :
1542 0 : mDocument->EndLoad();
1543 :
1544 0 : DropParserAndPerfHint();
1545 :
1546 0 : return NS_OK;
1547 : }
1548 :
1549 : NS_IMETHODIMP
1550 0 : HTMLContentSink::SetParser(nsParserBase* aParser)
1551 : {
1552 0 : NS_PRECONDITION(aParser, "Should have a parser here!");
1553 0 : mParser = aParser;
1554 0 : return NS_OK;
1555 : }
1556 :
1557 : NS_IMETHODIMP
1558 0 : HTMLContentSink::BeginContext(PRInt32 aPosition)
1559 : {
1560 0 : NS_PRECONDITION(aPosition > -1, "out of bounds");
1561 :
1562 0 : if (!mCurrentContext) {
1563 0 : NS_ERROR("Nonexistent context");
1564 :
1565 0 : return NS_ERROR_FAILURE;
1566 : }
1567 :
1568 : // Flush everything in the current context so that we don't have
1569 : // to worry about insertions resulting in inconsistent frame creation.
1570 0 : mCurrentContext->FlushTags();
1571 :
1572 : // Sanity check.
1573 0 : if (mCurrentContext->mStackPos <= aPosition) {
1574 0 : NS_ERROR("Out of bounds position");
1575 0 : return NS_ERROR_FAILURE;
1576 : }
1577 :
1578 0 : PRInt32 insertionPoint = -1;
1579 0 : nsHTMLTag nodeType = mCurrentContext->mStack[aPosition].mType;
1580 0 : nsGenericHTMLElement* content = mCurrentContext->mStack[aPosition].mContent;
1581 :
1582 : // If the content under which the new context is created
1583 : // has a child on the stack, the insertion point is
1584 : // before the last child.
1585 0 : if (aPosition < (mCurrentContext->mStackPos - 1)) {
1586 0 : insertionPoint = content->GetChildCount() - 1;
1587 : }
1588 :
1589 0 : SinkContext* sc = new SinkContext(this);
1590 : sc->Begin(nodeType,
1591 : content,
1592 0 : mCurrentContext->mStack[aPosition].mNumFlushed,
1593 0 : insertionPoint);
1594 0 : NS_ADDREF(sc->mSink);
1595 :
1596 0 : mContextStack.AppendElement(mCurrentContext);
1597 0 : mCurrentContext = sc;
1598 0 : return NS_OK;
1599 : }
1600 :
1601 : NS_IMETHODIMP
1602 0 : HTMLContentSink::EndContext(PRInt32 aPosition)
1603 : {
1604 0 : NS_PRECONDITION(mCurrentContext && aPosition > -1, "nonexistent context");
1605 :
1606 0 : PRUint32 n = mContextStack.Length() - 1;
1607 0 : SinkContext* sc = mContextStack.ElementAt(n);
1608 :
1609 0 : const SinkContext::Node &bottom = mCurrentContext->mStack[0];
1610 :
1611 0 : NS_ASSERTION(sc->mStack[aPosition].mType == bottom.mType,
1612 : "ending a wrong context");
1613 :
1614 0 : mCurrentContext->FlushTextAndRelease();
1615 :
1616 0 : NS_ASSERTION(bottom.mContent->GetChildCount() == bottom.mNumFlushed,
1617 : "Node at base of context stack not fully flushed.");
1618 :
1619 : // Flushing tags before the assertion on the previous line would
1620 : // undoubtedly prevent the assertion from failing, but it shouldn't
1621 : // be failing anyway, FlushTags or no. Flushing here is nevertheless
1622 : // a worthwhile precaution, since we lose some information (e.g.,
1623 : // mInsertionPoints) when we end the current context.
1624 0 : mCurrentContext->FlushTags();
1625 :
1626 0 : sc->mStack[aPosition].mNumFlushed = bottom.mNumFlushed;
1627 :
1628 0 : for (PRInt32 i = 0; i<mCurrentContext->mStackPos; i++) {
1629 0 : NS_IF_RELEASE(mCurrentContext->mStack[i].mContent);
1630 : }
1631 :
1632 0 : delete [] mCurrentContext->mStack;
1633 :
1634 0 : mCurrentContext->mStack = nsnull;
1635 0 : mCurrentContext->mStackPos = 0;
1636 0 : mCurrentContext->mStackSize = 0;
1637 :
1638 0 : delete [] mCurrentContext->mText;
1639 :
1640 0 : mCurrentContext->mText = nsnull;
1641 0 : mCurrentContext->mTextLength = 0;
1642 0 : mCurrentContext->mTextSize = 0;
1643 :
1644 0 : NS_IF_RELEASE(mCurrentContext->mSink);
1645 :
1646 0 : delete mCurrentContext;
1647 :
1648 0 : mCurrentContext = sc;
1649 0 : mContextStack.RemoveElementAt(n);
1650 0 : return NS_OK;
1651 : }
1652 :
1653 : nsresult
1654 0 : HTMLContentSink::CloseHTML()
1655 : {
1656 : SINK_TRACE_NODE(SINK_TRACE_CALLS,
1657 : "HTMLContentSink::CloseHTML",
1658 0 : eHTMLTag_html, 0, this);
1659 :
1660 0 : if (mHeadContext) {
1661 0 : if (mCurrentContext == mHeadContext) {
1662 0 : PRUint32 numContexts = mContextStack.Length();
1663 :
1664 : // Pop off the second html context if it's not done earlier
1665 0 : mCurrentContext = mContextStack.ElementAt(--numContexts);
1666 0 : mContextStack.RemoveElementAt(numContexts);
1667 : }
1668 :
1669 0 : NS_ASSERTION(mHeadContext->mTextLength == 0, "Losing text");
1670 :
1671 0 : mHeadContext->End();
1672 :
1673 0 : delete mHeadContext;
1674 0 : mHeadContext = nsnull;
1675 : }
1676 :
1677 0 : return NS_OK;
1678 : }
1679 :
1680 : nsresult
1681 0 : HTMLContentSink::OpenHead()
1682 : {
1683 0 : nsresult rv = OpenHeadContext();
1684 0 : return rv;
1685 : }
1686 :
1687 : nsresult
1688 0 : HTMLContentSink::OpenBody(const nsIParserNode& aNode)
1689 : {
1690 0 : SINK_TRACE_NODE(SINK_TRACE_CALLS,
1691 : "HTMLContentSink::OpenBody",
1692 : eHTMLTag_body,
1693 : mCurrentContext->mStackPos,
1694 0 : this);
1695 :
1696 0 : CloseHeadContext(); // do this just in case if the HEAD was left open!
1697 :
1698 : // Add attributes, if any, to the current BODY node
1699 0 : if (mBody) {
1700 0 : AddAttributes(aNode, mBody, true, true);
1701 0 : return NS_OK;
1702 : }
1703 :
1704 0 : nsresult rv = mCurrentContext->OpenContainer(aNode);
1705 :
1706 0 : if (NS_FAILED(rv)) {
1707 0 : return rv;
1708 : }
1709 :
1710 0 : mBody = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
1711 :
1712 0 : if (mCurrentContext->mStackPos > 1) {
1713 0 : PRInt32 parentIndex = mCurrentContext->mStackPos - 2;
1714 0 : nsGenericHTMLElement *parent = mCurrentContext->mStack[parentIndex].mContent;
1715 0 : PRInt32 numFlushed = mCurrentContext->mStack[parentIndex].mNumFlushed;
1716 0 : PRInt32 childCount = parent->GetChildCount();
1717 0 : NS_ASSERTION(numFlushed < childCount, "Already notified on the body?");
1718 :
1719 : PRInt32 insertionPoint =
1720 0 : mCurrentContext->mStack[parentIndex].mInsertionPoint;
1721 :
1722 : // XXX: I have yet to see a case where numFlushed is non-zero and
1723 : // insertionPoint is not -1, but this code will try to handle
1724 : // those cases too.
1725 :
1726 0 : PRUint32 oldUpdates = mUpdatesInNotification;
1727 0 : mUpdatesInNotification = 0;
1728 0 : if (insertionPoint != -1) {
1729 0 : NotifyInsert(parent, mBody, insertionPoint - 1);
1730 : } else {
1731 0 : NotifyAppend(parent, numFlushed);
1732 : }
1733 0 : mCurrentContext->mStack[parentIndex].mNumFlushed = childCount;
1734 0 : if (mUpdatesInNotification > 1) {
1735 0 : UpdateChildCounts();
1736 : }
1737 0 : mUpdatesInNotification = oldUpdates;
1738 : }
1739 :
1740 0 : StartLayout(false);
1741 :
1742 0 : return NS_OK;
1743 : }
1744 :
1745 : nsresult
1746 0 : HTMLContentSink::CloseBody()
1747 : {
1748 0 : SINK_TRACE_NODE(SINK_TRACE_CALLS,
1749 : "HTMLContentSink::CloseBody",
1750 : eHTMLTag_body,
1751 : mCurrentContext->mStackPos - 1,
1752 0 : this);
1753 :
1754 : bool didFlush;
1755 0 : nsresult rv = mCurrentContext->FlushTextAndRelease(&didFlush);
1756 0 : if (NS_FAILED(rv)) {
1757 0 : return rv;
1758 : }
1759 :
1760 : // Flush out anything that's left
1761 0 : SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
1762 : ("HTMLContentSink::CloseBody: layout final body content"));
1763 :
1764 0 : mCurrentContext->FlushTags();
1765 0 : mCurrentContext->CloseContainer(eHTMLTag_body);
1766 :
1767 0 : return NS_OK;
1768 : }
1769 :
1770 : NS_IMETHODIMP
1771 0 : HTMLContentSink::IsEnabled(PRInt32 aTag, bool* aReturn)
1772 : {
1773 0 : nsHTMLTag theHTMLTag = nsHTMLTag(aTag);
1774 :
1775 0 : if (theHTMLTag == eHTMLTag_script) {
1776 0 : *aReturn = mScriptEnabled;
1777 0 : } else if (theHTMLTag == eHTMLTag_frameset) {
1778 0 : *aReturn = mFramesEnabled;
1779 : } else {
1780 0 : *aReturn = false;
1781 : }
1782 :
1783 0 : return NS_OK;
1784 : }
1785 :
1786 : NS_IMETHODIMP
1787 0 : HTMLContentSink::OpenContainer(const nsIParserNode& aNode)
1788 : {
1789 0 : nsresult rv = NS_OK;
1790 :
1791 0 : switch (aNode.GetNodeType()) {
1792 : case eHTMLTag_frameset:
1793 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for frames.");
1794 : rv = NS_ERROR_NOT_IMPLEMENTED;
1795 : break;
1796 : case eHTMLTag_head:
1797 0 : rv = OpenHeadContext();
1798 0 : if (NS_SUCCEEDED(rv)) {
1799 0 : rv = AddAttributes(aNode, mHead, true, mHaveSeenHead);
1800 0 : mHaveSeenHead = true;
1801 : }
1802 0 : break;
1803 : case eHTMLTag_body:
1804 0 : rv = OpenBody(aNode);
1805 0 : break;
1806 : case eHTMLTag_html:
1807 0 : if (mRoot) {
1808 : // If we've already hit this code once, need to check for
1809 : // already-present attributes on the root.
1810 0 : AddAttributes(aNode, mRoot, true, mNotifiedRootInsertion);
1811 0 : if (!mNotifiedRootInsertion) {
1812 0 : NotifyRootInsertion();
1813 : }
1814 0 : ProcessOfflineManifest(mRoot);
1815 : }
1816 0 : break;
1817 : case eHTMLTag_form:
1818 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for forms.");
1819 : rv = NS_ERROR_NOT_IMPLEMENTED;
1820 : break;
1821 : default:
1822 0 : rv = mCurrentContext->OpenContainer(aNode);
1823 0 : break;
1824 : }
1825 :
1826 0 : return rv;
1827 : }
1828 :
1829 : NS_IMETHODIMP
1830 0 : HTMLContentSink::CloseContainer(const eHTMLTags aTag)
1831 : {
1832 0 : nsresult rv = NS_OK;
1833 :
1834 0 : switch (aTag) {
1835 : case eHTMLTag_frameset:
1836 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for frames.");
1837 : rv = NS_ERROR_NOT_IMPLEMENTED;
1838 : break;
1839 : case eHTMLTag_head:
1840 0 : CloseHeadContext();
1841 0 : break;
1842 : case eHTMLTag_body:
1843 0 : rv = CloseBody();
1844 0 : break;
1845 : case eHTMLTag_html:
1846 0 : rv = CloseHTML();
1847 0 : break;
1848 : case eHTMLTag_form:
1849 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for forms.");
1850 : rv = NS_ERROR_NOT_IMPLEMENTED;
1851 : break;
1852 : default:
1853 0 : rv = mCurrentContext->CloseContainer(aTag);
1854 0 : break;
1855 : }
1856 :
1857 0 : return rv;
1858 : }
1859 :
1860 : NS_IMETHODIMP
1861 0 : HTMLContentSink::CloseMalformedContainer(const eHTMLTags aTag)
1862 : {
1863 0 : return mCurrentContext->CloseContainer(aTag);
1864 : }
1865 :
1866 : NS_IMETHODIMP
1867 0 : HTMLContentSink::AddLeaf(const nsIParserNode& aNode)
1868 : {
1869 : nsresult rv;
1870 :
1871 0 : nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
1872 0 : switch (nodeType) {
1873 : case eHTMLTag_link:
1874 0 : rv = NS_ERROR_NOT_IMPLEMENTED;
1875 0 : MOZ_NOT_REACHED("Must not use HTMLContentSink for links.");
1876 :
1877 : break;
1878 : default:
1879 0 : rv = mCurrentContext->AddLeaf(aNode);
1880 :
1881 : break;
1882 : }
1883 :
1884 0 : return rv;
1885 : }
1886 :
1887 : NS_IMETHODIMP
1888 0 : HTMLContentSink::DidProcessTokens(void)
1889 : {
1890 0 : return NS_OK;
1891 : }
1892 :
1893 : NS_IMETHODIMP
1894 0 : HTMLContentSink::WillProcessAToken(void)
1895 : {
1896 0 : return NS_OK;
1897 : }
1898 :
1899 : NS_IMETHODIMP
1900 0 : HTMLContentSink::DidProcessAToken(void)
1901 : {
1902 0 : return DidProcessATokenImpl();
1903 : }
1904 :
1905 : NS_IMETHODIMP
1906 0 : HTMLContentSink::WillInterrupt()
1907 : {
1908 0 : return WillInterruptImpl();
1909 : }
1910 :
1911 : NS_IMETHODIMP
1912 0 : HTMLContentSink::WillResume()
1913 : {
1914 0 : return WillResumeImpl();
1915 : }
1916 :
1917 : nsresult
1918 0 : HTMLContentSink::OpenHeadContext()
1919 : {
1920 0 : if (mCurrentContext && mCurrentContext->IsCurrentContainer(eHTMLTag_head))
1921 0 : return NS_OK;
1922 :
1923 : // Flush everything in the current context so that we don't have
1924 : // to worry about insertions resulting in inconsistent frame creation.
1925 : //
1926 : // Try to do this only if needed (costly), i.e., only if we are sure
1927 : // we are changing contexts from some other context to the head.
1928 : //
1929 : // PERF: This call causes approximately a 2% slowdown in page load time
1930 : // according to jrgm's page load tests, but seems to be a necessary evil
1931 0 : if (mCurrentContext && (mCurrentContext != mHeadContext)) {
1932 0 : mCurrentContext->FlushTags();
1933 : }
1934 :
1935 0 : if (!mHeadContext) {
1936 0 : mHeadContext = new SinkContext(this);
1937 0 : NS_ENSURE_TRUE(mHeadContext, NS_ERROR_OUT_OF_MEMORY);
1938 :
1939 0 : nsresult rv = mHeadContext->Begin(eHTMLTag_head, mHead, 0, -1);
1940 0 : NS_ENSURE_SUCCESS(rv, rv);
1941 : }
1942 :
1943 0 : mContextStack.AppendElement(mCurrentContext);
1944 0 : mCurrentContext = mHeadContext;
1945 :
1946 0 : return NS_OK;
1947 : }
1948 :
1949 : void
1950 0 : HTMLContentSink::CloseHeadContext()
1951 : {
1952 0 : if (mCurrentContext) {
1953 0 : if (!mCurrentContext->IsCurrentContainer(eHTMLTag_head))
1954 0 : return;
1955 :
1956 0 : mCurrentContext->FlushTextAndRelease();
1957 0 : mCurrentContext->FlushTags();
1958 : }
1959 :
1960 0 : if (!mContextStack.IsEmpty())
1961 : {
1962 0 : PRUint32 n = mContextStack.Length() - 1;
1963 0 : mCurrentContext = mContextStack.ElementAt(n);
1964 0 : mContextStack.RemoveElementAt(n);
1965 : }
1966 : }
1967 :
1968 : #ifdef DEBUG
1969 : void
1970 0 : HTMLContentSink::ForceReflow()
1971 : {
1972 0 : mCurrentContext->FlushTags();
1973 0 : }
1974 : #endif
1975 :
1976 : void
1977 0 : HTMLContentSink::NotifyInsert(nsIContent* aContent,
1978 : nsIContent* aChildContent,
1979 : PRInt32 aIndexInContainer)
1980 : {
1981 0 : if (aContent && aContent->GetCurrentDoc() != mDocument) {
1982 : // aContent is not actually in our document anymore.... Just bail out of
1983 : // here; notifying on our document for this insert would be wrong.
1984 0 : return;
1985 : }
1986 :
1987 0 : mInNotification++;
1988 :
1989 : {
1990 : // Scope so we call EndUpdate before we decrease mInNotification
1991 0 : MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_CONTENT_MODEL, !mBeganUpdate);
1992 : nsNodeUtils::ContentInserted(NODE_FROM(aContent, mDocument),
1993 0 : aChildContent, aIndexInContainer);
1994 0 : mLastNotificationTime = PR_Now();
1995 : }
1996 :
1997 0 : mInNotification--;
1998 : }
1999 :
2000 : void
2001 0 : HTMLContentSink::NotifyRootInsertion()
2002 : {
2003 0 : NS_PRECONDITION(!mNotifiedRootInsertion, "Double-notifying on root?");
2004 0 : NS_ASSERTION(!mLayoutStarted,
2005 : "How did we start layout without notifying on root?");
2006 : // Now make sure to notify that we have now inserted our root. If
2007 : // there has been no initial reflow yet it'll be a no-op, but if
2008 : // there has been one we need this to get its frames constructed.
2009 : // Note that if mNotifiedRootInsertion is true we don't notify here,
2010 : // since that just means there are multiple <html> tags in the
2011 : // document; in those cases we just want to put all the attrs on one
2012 : // tag.
2013 0 : mNotifiedRootInsertion = true;
2014 0 : PRInt32 index = mDocument->IndexOf(mRoot);
2015 0 : NS_ASSERTION(index != -1, "mRoot not child of document?");
2016 0 : NotifyInsert(nsnull, mRoot, index);
2017 :
2018 : // Now update the notification information in all our
2019 : // contexts, since we just inserted the root and notified on
2020 : // our whole tree
2021 0 : UpdateChildCounts();
2022 0 : }
2023 :
2024 : bool
2025 0 : HTMLContentSink::IsMonolithicContainer(nsHTMLTag aTag)
2026 : {
2027 0 : if (aTag == eHTMLTag_tr ||
2028 : aTag == eHTMLTag_select ||
2029 : aTag == eHTMLTag_applet ||
2030 : aTag == eHTMLTag_object) {
2031 0 : return true;
2032 : }
2033 :
2034 0 : return false;
2035 : }
2036 :
2037 : void
2038 0 : HTMLContentSink::UpdateChildCounts()
2039 : {
2040 0 : PRUint32 numContexts = mContextStack.Length();
2041 0 : for (PRUint32 i = 0; i < numContexts; i++) {
2042 0 : SinkContext* sc = mContextStack.ElementAt(i);
2043 :
2044 0 : sc->UpdateChildCounts();
2045 : }
2046 :
2047 0 : mCurrentContext->UpdateChildCounts();
2048 0 : }
2049 :
2050 : void
2051 0 : HTMLContentSink::FlushPendingNotifications(mozFlushType aType)
2052 : {
2053 : // Only flush tags if we're not doing the notification ourselves
2054 : // (since we aren't reentrant)
2055 0 : if (!mInNotification) {
2056 : // Only flush if we're still a document observer (so that our child counts
2057 : // should be correct).
2058 0 : if (mIsDocumentObserver) {
2059 0 : if (aType >= Flush_ContentAndNotify) {
2060 0 : FlushTags();
2061 : }
2062 0 : else if (mCurrentContext) {
2063 0 : mCurrentContext->FlushText();
2064 : }
2065 : }
2066 0 : if (aType >= Flush_InterruptibleLayout) {
2067 : // Make sure that layout has started so that the reflow flush
2068 : // will actually happen.
2069 0 : StartLayout(true);
2070 : }
2071 : }
2072 0 : }
2073 :
2074 : nsresult
2075 0 : HTMLContentSink::FlushTags()
2076 : {
2077 0 : if (!mNotifiedRootInsertion) {
2078 0 : NotifyRootInsertion();
2079 0 : return NS_OK;
2080 : }
2081 :
2082 0 : return mCurrentContext ? mCurrentContext->FlushTags() : NS_OK;
2083 : }
2084 :
2085 : NS_IMETHODIMP
2086 0 : HTMLContentSink::SetDocumentCharset(nsACString& aCharset)
2087 : {
2088 0 : if (mDocShell) {
2089 : // the following logic to get muCV is copied from
2090 : // nsHTMLDocument::StartDocumentLoad
2091 : // We need to call muCV->SetPrevDocCharacterSet here in case
2092 : // the charset is detected by parser DetectMetaTag
2093 0 : nsCOMPtr<nsIMarkupDocumentViewer> muCV;
2094 0 : nsCOMPtr<nsIContentViewer> cv;
2095 0 : mDocShell->GetContentViewer(getter_AddRefs(cv));
2096 0 : if (cv) {
2097 0 : muCV = do_QueryInterface(cv);
2098 : } else {
2099 : // in this block of code, if we get an error result, we return
2100 : // it but if we get a null pointer, that's perfectly legal for
2101 : // parent and parentContentViewer
2102 :
2103 : nsCOMPtr<nsIDocShellTreeItem> docShellAsItem =
2104 0 : do_QueryInterface(mDocShell);
2105 0 : NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
2106 :
2107 0 : nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
2108 0 : docShellAsItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
2109 :
2110 0 : nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
2111 0 : if (parent) {
2112 0 : nsCOMPtr<nsIContentViewer> parentContentViewer;
2113 : nsresult rv =
2114 0 : parent->GetContentViewer(getter_AddRefs(parentContentViewer));
2115 0 : if (NS_SUCCEEDED(rv) && parentContentViewer) {
2116 0 : muCV = do_QueryInterface(parentContentViewer);
2117 : }
2118 : }
2119 : }
2120 :
2121 0 : if (muCV) {
2122 0 : muCV->SetPrevDocCharacterSet(aCharset);
2123 : }
2124 : }
2125 :
2126 0 : if (mDocument) {
2127 0 : mDocument->SetDocumentCharacterSet(aCharset);
2128 : }
2129 :
2130 0 : return NS_OK;
2131 : }
2132 :
2133 : nsISupports *
2134 0 : HTMLContentSink::GetTarget()
2135 : {
2136 0 : return mDocument;
2137 : }
2138 :
2139 : bool
2140 0 : HTMLContentSink::IsScriptExecuting()
2141 : {
2142 0 : return IsScriptExecutingImpl();
2143 : }
2144 :
2145 : #ifdef DEBUG
2146 : /**
2147 : * This will dump content model into the output file.
2148 : *
2149 : * @update harishd 05/25/00
2150 : * @param
2151 : * @return NS_OK all went well, error on failure
2152 : */
2153 :
2154 : NS_IMETHODIMP
2155 0 : HTMLContentSink::DumpContentModel()
2156 : {
2157 0 : FILE* out = ::fopen("rtest_html.txt", "a");
2158 0 : if (out) {
2159 0 : if (mDocument) {
2160 0 : Element* root = mDocument->GetRootElement();
2161 0 : if (root) {
2162 0 : if (mDocumentURI) {
2163 0 : nsCAutoString buf;
2164 0 : mDocumentURI->GetSpec(buf);
2165 0 : fputs(buf.get(), out);
2166 : }
2167 :
2168 0 : fputs(";", out);
2169 0 : root->DumpContent(out, 0, false);
2170 0 : fputs(";\n", out);
2171 : }
2172 : }
2173 :
2174 0 : fclose(out);
2175 : }
2176 :
2177 0 : return NS_OK;
2178 4392 : }
2179 : #endif
2180 :
|