Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /**
8 : * This file is near-OBSOLETE. It is used for about:blank only and for the
9 : * HTML element factory.
10 : * Don't bother adding new stuff in this file.
11 : */
12 :
13 : #include "mozilla/ArrayUtils.h"
14 :
15 : #include "nsContentSink.h"
16 : #include "nsCOMPtr.h"
17 : #include "nsReadableUtils.h"
18 : #include "nsUnicharUtils.h"
19 : #include "nsIHTMLContentSink.h"
20 : #include "nsIInterfaceRequestor.h"
21 : #include "nsIInterfaceRequestorUtils.h"
22 : #include "nsIURI.h"
23 : #include "nsIContentViewer.h"
24 : #include "mozilla/dom/NodeInfo.h"
25 : #include "mozilla/dom/ScriptLoader.h"
26 : #include "nsIAppShell.h"
27 : #include "nsCRT.h"
28 : #include "prtime.h"
29 : #include "mozilla/Logging.h"
30 : #include "nsNodeUtils.h"
31 : #include "nsIContent.h"
32 : #include "mozilla/dom/Element.h"
33 : #include "mozilla/Preferences.h"
34 :
35 : #include "nsGenericHTMLElement.h"
36 :
37 : #include "nsIDOMDocument.h"
38 : #include "nsIDOMDocumentType.h"
39 : #include "nsIScriptElement.h"
40 :
41 : #include "nsIComponentManager.h"
42 : #include "nsIServiceManager.h"
43 :
44 : #include "nsGkAtoms.h"
45 : #include "nsContentUtils.h"
46 : #include "nsIChannel.h"
47 : #include "nsIHttpChannel.h"
48 : #include "nsIDocShell.h"
49 : #include "nsIDocument.h"
50 : #include "nsStubDocumentObserver.h"
51 : #include "nsIHTMLDocument.h"
52 : #include "nsIDOMHTMLMapElement.h"
53 : #include "nsICookieService.h"
54 : #include "nsTArray.h"
55 : #include "nsIScriptSecurityManager.h"
56 : #include "nsIPrincipal.h"
57 : #include "nsTextFragment.h"
58 : #include "nsIScriptGlobalObject.h"
59 : #include "nsNameSpaceManager.h"
60 :
61 : #include "nsIParserService.h"
62 :
63 : #include "nsIStyleSheetLinkingElement.h"
64 : #include "nsITimer.h"
65 : #include "nsError.h"
66 : #include "nsContentPolicyUtils.h"
67 : #include "nsIScriptContext.h"
68 : #include "nsStyleLinkElement.h"
69 :
70 : #include "nsWeakReference.h" // nsHTMLElementFactory supports weak references
71 : #include "nsIPrompt.h"
72 : #include "nsLayoutCID.h"
73 : #include "nsIDocShellTreeItem.h"
74 :
75 : #include "nsEscape.h"
76 : #include "nsNodeInfoManager.h"
77 : #include "nsContentCreatorFunctions.h"
78 : #include "mozAutoDocUpdate.h"
79 : #include "nsTextNode.h"
80 :
81 : using namespace mozilla;
82 : using namespace mozilla::dom;
83 :
84 : //----------------------------------------------------------------------
85 :
86 : typedef nsGenericHTMLElement*
87 : (*contentCreatorCallback)(already_AddRefed<mozilla::dom::NodeInfo>&&,
88 : FromParser aFromParser);
89 :
90 : nsGenericHTMLElement*
91 0 : NS_NewHTMLNOTUSEDElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
92 : FromParser aFromParser)
93 : {
94 0 : NS_NOTREACHED("The element ctor should never be called");
95 0 : return nullptr;
96 : }
97 :
98 : #define HTML_TAG(_tag, _classname, _interfacename) NS_NewHTML##_classname##Element,
99 : #define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement,
100 : static const contentCreatorCallback sContentCreatorCallbacks[] = {
101 : NS_NewHTMLUnknownElement,
102 : #include "nsHTMLTagList.h"
103 : #undef HTML_TAG
104 : #undef HTML_OTHER
105 : NS_NewHTMLUnknownElement
106 : };
107 :
108 : class SinkContext;
109 : class HTMLContentSink;
110 :
111 : /**
112 : * This class is near-OBSOLETE. It is used for about:blank only.
113 : * Don't bother adding new stuff in this file.
114 : */
115 : class HTMLContentSink : public nsContentSink,
116 : public nsIHTMLContentSink
117 : {
118 : public:
119 : friend class SinkContext;
120 :
121 : HTMLContentSink();
122 :
123 : nsresult Init(nsIDocument* aDoc, nsIURI* aURI, nsISupports* aContainer,
124 : nsIChannel* aChannel);
125 :
126 : // nsISupports
127 : NS_DECL_ISUPPORTS_INHERITED
128 1 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentSink, nsContentSink)
129 :
130 : // nsIContentSink
131 : NS_IMETHOD WillParse(void) override;
132 : NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) override;
133 : NS_IMETHOD DidBuildModel(bool aTerminated) override;
134 : NS_IMETHOD WillInterrupt(void) override;
135 : NS_IMETHOD WillResume(void) override;
136 : NS_IMETHOD SetParser(nsParserBase* aParser) override;
137 : virtual void FlushPendingNotifications(FlushType aType) override;
138 : virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding) override;
139 : virtual nsISupports *GetTarget() override;
140 : virtual bool IsScriptExecuting() override;
141 :
142 : // nsIHTMLContentSink
143 : NS_IMETHOD OpenContainer(ElementType aNodeType) override;
144 : NS_IMETHOD CloseContainer(ElementType aTag) override;
145 :
146 : protected:
147 : virtual ~HTMLContentSink();
148 :
149 : nsCOMPtr<nsIHTMLDocument> mHTMLDocument;
150 :
151 : // The maximum length of a text run
152 : int32_t mMaxTextRun;
153 :
154 : RefPtr<nsGenericHTMLElement> mRoot;
155 : RefPtr<nsGenericHTMLElement> mBody;
156 : RefPtr<nsGenericHTMLElement> mHead;
157 :
158 : AutoTArray<SinkContext*, 8> mContextStack;
159 : SinkContext* mCurrentContext;
160 : SinkContext* mHeadContext;
161 :
162 : // Boolean indicating whether we've seen a <head> tag that might have had
163 : // attributes once already.
164 : bool mHaveSeenHead;
165 :
166 : // Boolean indicating whether we've notified insertion of our root content
167 : // yet. We want to make sure to only do this once.
168 : bool mNotifiedRootInsertion;
169 :
170 : nsresult FlushTags() override;
171 :
172 : // Routines for tags that require special handling
173 : nsresult CloseHTML();
174 : nsresult OpenBody();
175 : nsresult CloseBody();
176 :
177 : void CloseHeadContext();
178 :
179 : // nsContentSink overrides
180 : void UpdateChildCounts() override;
181 :
182 : void NotifyInsert(nsIContent* aContent,
183 : nsIContent* aChildContent,
184 : int32_t aIndexInContainer);
185 : void NotifyRootInsertion();
186 : };
187 :
188 : class SinkContext
189 : {
190 : public:
191 : explicit SinkContext(HTMLContentSink* aSink);
192 : ~SinkContext();
193 :
194 : nsresult Begin(nsHTMLTag aNodeType, nsGenericHTMLElement* aRoot,
195 : uint32_t aNumFlushed, int32_t aInsertionPoint);
196 : nsresult OpenBody();
197 : nsresult CloseBody();
198 : nsresult End();
199 :
200 : nsresult GrowStack();
201 : nsresult FlushTags();
202 :
203 : bool IsCurrentContainer(nsHTMLTag mType);
204 :
205 : void DidAddContent(nsIContent* aContent);
206 : void UpdateChildCounts();
207 :
208 : private:
209 : // Function to check whether we've notified for the current content.
210 : // What this actually does is check whether we've notified for all
211 : // of the parent's kids.
212 : bool HaveNotifiedForCurrentContent() const;
213 :
214 : public:
215 : HTMLContentSink* mSink;
216 : int32_t mNotifyLevel;
217 :
218 : struct Node {
219 : nsHTMLTag mType;
220 : nsGenericHTMLElement* mContent;
221 : uint32_t mNumFlushed;
222 : int32_t mInsertionPoint;
223 :
224 : nsIContent *Add(nsIContent *child);
225 : };
226 :
227 : Node* mStack;
228 : int32_t mStackSize;
229 : int32_t mStackPos;
230 : };
231 :
232 : nsresult
233 74 : NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
234 : FromParser aFromParser, const nsAString* aIs)
235 : {
236 74 : *aResult = nullptr;
237 :
238 148 : RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
239 :
240 74 : nsIParserService* parserService = nsContentUtils::GetParserService();
241 74 : if (!parserService)
242 0 : return NS_ERROR_OUT_OF_MEMORY;
243 :
244 74 : nsIAtom *name = nodeInfo->NameAtom();
245 :
246 74 : NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
247 : "Trying to HTML elements that don't have the XHTML namespace");
248 :
249 74 : int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name);
250 :
251 : // Per the Custom Element specification, unknown tags that are valid custom
252 : // element names should be HTMLElement instead of HTMLUnknownElement.
253 74 : bool isCustomElementName = (tag == eHTMLTag_userdefined &&
254 74 : nsContentUtils::IsCustomElementName(name));
255 74 : if (isCustomElementName) {
256 0 : NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
257 : } else {
258 74 : *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
259 : }
260 :
261 74 : if (!*aResult) {
262 0 : return NS_ERROR_OUT_OF_MEMORY;
263 : }
264 :
265 74 : if (CustomElementRegistry::IsCustomElementEnabled() &&
266 0 : (isCustomElementName || aIs)) {
267 0 : nsContentUtils::SetupCustomElement(*aResult, aIs);
268 : }
269 :
270 74 : return NS_OK;
271 : }
272 :
273 : already_AddRefed<nsGenericHTMLElement>
274 74 : CreateHTMLElement(uint32_t aNodeType,
275 : already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
276 : FromParser aFromParser)
277 : {
278 74 : NS_ASSERTION(aNodeType <= NS_HTML_TAG_MAX ||
279 : aNodeType == eHTMLTag_userdefined,
280 : "aNodeType is out of bounds");
281 :
282 74 : contentCreatorCallback cb = sContentCreatorCallbacks[aNodeType];
283 :
284 74 : NS_ASSERTION(cb != NS_NewHTMLNOTUSEDElement,
285 : "Don't know how to construct tag element!");
286 :
287 148 : RefPtr<nsGenericHTMLElement> result = cb(Move(aNodeInfo), aFromParser);
288 :
289 148 : return result.forget();
290 : }
291 :
292 : //----------------------------------------------------------------------
293 :
294 1 : SinkContext::SinkContext(HTMLContentSink* aSink)
295 : : mSink(aSink),
296 : mNotifyLevel(0),
297 : mStack(nullptr),
298 : mStackSize(0),
299 1 : mStackPos(0)
300 : {
301 1 : MOZ_COUNT_CTOR(SinkContext);
302 1 : }
303 :
304 0 : SinkContext::~SinkContext()
305 : {
306 0 : MOZ_COUNT_DTOR(SinkContext);
307 :
308 0 : if (mStack) {
309 0 : for (int32_t i = 0; i < mStackPos; i++) {
310 0 : NS_RELEASE(mStack[i].mContent);
311 : }
312 0 : delete [] mStack;
313 : }
314 0 : }
315 :
316 : nsresult
317 1 : SinkContext::Begin(nsHTMLTag aNodeType,
318 : nsGenericHTMLElement* aRoot,
319 : uint32_t aNumFlushed,
320 : int32_t aInsertionPoint)
321 : {
322 1 : if (mStackSize < 1) {
323 1 : nsresult rv = GrowStack();
324 1 : if (NS_FAILED(rv)) {
325 0 : return rv;
326 : }
327 : }
328 :
329 1 : mStack[0].mType = aNodeType;
330 1 : mStack[0].mContent = aRoot;
331 1 : mStack[0].mNumFlushed = aNumFlushed;
332 1 : mStack[0].mInsertionPoint = aInsertionPoint;
333 1 : NS_ADDREF(aRoot);
334 1 : mStackPos = 1;
335 :
336 1 : return NS_OK;
337 : }
338 :
339 : bool
340 1 : SinkContext::IsCurrentContainer(nsHTMLTag aTag)
341 : {
342 1 : if (aTag == mStack[mStackPos - 1].mType) {
343 0 : return true;
344 : }
345 :
346 1 : return false;
347 : }
348 :
349 : void
350 1 : SinkContext::DidAddContent(nsIContent* aContent)
351 : {
352 1 : if ((mStackPos == 2) && (mSink->mBody == mStack[1].mContent)) {
353 : // We just finished adding something to the body
354 0 : mNotifyLevel = 0;
355 : }
356 :
357 : // If we just added content to a node for which
358 : // an insertion happen, we need to do an immediate
359 : // notification for that insertion.
360 3 : if (0 < mStackPos &&
361 1 : mStack[mStackPos - 1].mInsertionPoint != -1 &&
362 0 : mStack[mStackPos - 1].mNumFlushed <
363 0 : mStack[mStackPos - 1].mContent->GetChildCount()) {
364 0 : nsIContent* parent = mStack[mStackPos - 1].mContent;
365 0 : int32_t childIndex = mStack[mStackPos - 1].mInsertionPoint - 1;
366 0 : NS_ASSERTION(parent->GetChildAt(childIndex) == aContent,
367 : "Flushing the wrong child.");
368 0 : mSink->NotifyInsert(parent, aContent, childIndex);
369 0 : mStack[mStackPos - 1].mNumFlushed = parent->GetChildCount();
370 1 : } else if (mSink->IsTimeToNotify()) {
371 0 : FlushTags();
372 : }
373 1 : }
374 :
375 : nsresult
376 1 : SinkContext::OpenBody()
377 : {
378 1 : if (mStackPos <= 0) {
379 0 : NS_ERROR("container w/o parent");
380 :
381 0 : return NS_ERROR_FAILURE;
382 : }
383 :
384 : nsresult rv;
385 1 : if (mStackPos + 1 > mStackSize) {
386 0 : rv = GrowStack();
387 0 : if (NS_FAILED(rv)) {
388 0 : return rv;
389 : }
390 : }
391 :
392 : RefPtr<mozilla::dom::NodeInfo> nodeInfo =
393 2 : mSink->mNodeInfoManager->GetNodeInfo(nsGkAtoms::body, nullptr,
394 : kNameSpaceID_XHTML,
395 2 : nsIDOMNode::ELEMENT_NODE);
396 1 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_UNEXPECTED);
397 :
398 : // Make the content object
399 : RefPtr<nsGenericHTMLElement> body =
400 2 : NS_NewHTMLBodyElement(nodeInfo.forget(), FROM_PARSER_NETWORK);
401 1 : if (!body) {
402 0 : return NS_ERROR_OUT_OF_MEMORY;
403 : }
404 :
405 1 : mStack[mStackPos].mType = eHTMLTag_body;
406 1 : body.forget(&mStack[mStackPos].mContent);
407 1 : mStack[mStackPos].mNumFlushed = 0;
408 1 : mStack[mStackPos].mInsertionPoint = -1;
409 1 : ++mStackPos;
410 1 : mStack[mStackPos - 2].Add(mStack[mStackPos - 1].mContent);
411 :
412 1 : return NS_OK;
413 : }
414 :
415 : bool
416 0 : SinkContext::HaveNotifiedForCurrentContent() const
417 : {
418 0 : if (0 < mStackPos) {
419 0 : nsIContent* parent = mStack[mStackPos - 1].mContent;
420 0 : return mStack[mStackPos-1].mNumFlushed == parent->GetChildCount();
421 : }
422 :
423 0 : return true;
424 : }
425 :
426 : nsIContent *
427 1 : SinkContext::Node::Add(nsIContent *child)
428 : {
429 1 : NS_ASSERTION(mContent, "No parent to insert/append into!");
430 1 : if (mInsertionPoint != -1) {
431 0 : NS_ASSERTION(mNumFlushed == mContent->GetChildCount(),
432 : "Inserting multiple children without flushing.");
433 0 : mContent->InsertChildAt(child, mInsertionPoint++, false);
434 : } else {
435 1 : mContent->AppendChildTo(child, false);
436 : }
437 1 : return child;
438 : }
439 :
440 : nsresult
441 1 : SinkContext::CloseBody()
442 : {
443 1 : NS_ASSERTION(mStackPos > 0,
444 : "stack out of bounds. wrong context probably!");
445 :
446 1 : if (mStackPos <= 0) {
447 0 : return NS_OK; // Fix crash - Ref. bug 45975 or 45007
448 : }
449 :
450 1 : --mStackPos;
451 1 : NS_ASSERTION(mStack[mStackPos].mType == eHTMLTag_body,
452 : "Tag mismatch. Closing tag on wrong context or something?");
453 :
454 1 : nsGenericHTMLElement* content = mStack[mStackPos].mContent;
455 :
456 1 : content->Compact();
457 :
458 : // If we're in a state where we do append notifications as
459 : // we go up the tree, and we're at the level where the next
460 : // notification needs to be done, do the notification.
461 1 : if (mNotifyLevel >= mStackPos) {
462 : // Check to see if new content has been added after our last
463 : // notification
464 :
465 1 : if (mStack[mStackPos].mNumFlushed < content->GetChildCount()) {
466 0 : mSink->NotifyAppend(content, mStack[mStackPos].mNumFlushed);
467 0 : mStack[mStackPos].mNumFlushed = content->GetChildCount();
468 : }
469 :
470 : // Indicate that notification has now happened at this level
471 1 : mNotifyLevel = mStackPos - 1;
472 : }
473 :
474 1 : DidAddContent(content);
475 1 : NS_IF_RELEASE(content);
476 :
477 1 : return NS_OK;
478 : }
479 :
480 : nsresult
481 0 : SinkContext::End()
482 : {
483 0 : for (int32_t i = 0; i < mStackPos; i++) {
484 0 : NS_RELEASE(mStack[i].mContent);
485 : }
486 :
487 0 : mStackPos = 0;
488 :
489 0 : return NS_OK;
490 : }
491 :
492 : nsresult
493 1 : SinkContext::GrowStack()
494 : {
495 1 : int32_t newSize = mStackSize * 2;
496 1 : if (newSize == 0) {
497 1 : newSize = 32;
498 : }
499 :
500 2 : Node* stack = new Node[newSize];
501 :
502 1 : if (mStackPos != 0) {
503 0 : memcpy(stack, mStack, sizeof(Node) * mStackPos);
504 0 : delete [] mStack;
505 : }
506 :
507 1 : mStack = stack;
508 1 : mStackSize = newSize;
509 :
510 1 : return NS_OK;
511 : }
512 :
513 : /**
514 : * NOTE!! Forked into nsXMLContentSink. Please keep in sync.
515 : *
516 : * Flush all elements that have been seen so far such that
517 : * they are visible in the tree. Specifically, make sure
518 : * that they are all added to their respective parents.
519 : * Also, do notification at the top for all content that
520 : * has been newly added so that the frame tree is complete.
521 : */
522 : nsresult
523 3 : SinkContext::FlushTags()
524 : {
525 3 : mSink->mDeferredFlushTags = false;
526 3 : bool oldBeganUpdate = mSink->mBeganUpdate;
527 3 : uint32_t oldUpdates = mSink->mUpdatesInNotification;
528 :
529 3 : ++(mSink->mInNotification);
530 3 : mSink->mUpdatesInNotification = 0;
531 : {
532 : // Scope so we call EndUpdate before we decrease mInNotification
533 3 : mozAutoDocUpdate updateBatch(mSink->mDocument, UPDATE_CONTENT_MODEL,
534 6 : true);
535 3 : mSink->mBeganUpdate = true;
536 :
537 : // Start from the base of the stack (growing downward) and do
538 : // a notification from the node that is closest to the root of
539 : // tree for any content that has been added.
540 :
541 : // Note that we can start at stackPos == 0 here, because it's the caller's
542 : // responsibility to handle flushing interactions between contexts (see
543 : // HTMLContentSink::BeginContext).
544 3 : int32_t stackPos = 0;
545 3 : bool flushed = false;
546 : uint32_t childCount;
547 : nsGenericHTMLElement* content;
548 :
549 13 : while (stackPos < mStackPos) {
550 5 : content = mStack[stackPos].mContent;
551 5 : childCount = content->GetChildCount();
552 :
553 5 : if (!flushed && (mStack[stackPos].mNumFlushed < childCount)) {
554 0 : if (mStack[stackPos].mInsertionPoint != -1) {
555 : // We might have popped the child off our stack already
556 : // but not notified on it yet, which is why we have to get it
557 : // directly from its parent node.
558 :
559 0 : int32_t childIndex = mStack[stackPos].mInsertionPoint - 1;
560 0 : nsIContent* child = content->GetChildAt(childIndex);
561 : // Child not on stack anymore; can't assert it's correct
562 0 : NS_ASSERTION(!(mStackPos > (stackPos + 1)) ||
563 : (child == mStack[stackPos + 1].mContent),
564 : "Flushing the wrong child.");
565 0 : mSink->NotifyInsert(content, child, childIndex);
566 : } else {
567 0 : mSink->NotifyAppend(content, mStack[stackPos].mNumFlushed);
568 : }
569 :
570 0 : flushed = true;
571 : }
572 :
573 5 : mStack[stackPos].mNumFlushed = childCount;
574 5 : stackPos++;
575 : }
576 3 : mNotifyLevel = mStackPos - 1;
577 : }
578 3 : --(mSink->mInNotification);
579 :
580 3 : if (mSink->mUpdatesInNotification > 1) {
581 0 : UpdateChildCounts();
582 : }
583 :
584 3 : mSink->mUpdatesInNotification = oldUpdates;
585 3 : mSink->mBeganUpdate = oldBeganUpdate;
586 :
587 3 : return NS_OK;
588 : }
589 :
590 : /**
591 : * NOTE!! Forked into nsXMLContentSink. Please keep in sync.
592 : */
593 : void
594 2 : SinkContext::UpdateChildCounts()
595 : {
596 : // Start from the top of the stack (growing upwards) and see if any
597 : // new content has been appended. If so, we recognize that reflows
598 : // have been generated for it and we should make sure that no
599 : // further reflows occur. Note that we have to include stackPos == 0
600 : // to properly notify on kids of <html>.
601 2 : int32_t stackPos = mStackPos - 1;
602 6 : while (stackPos >= 0) {
603 2 : Node & node = mStack[stackPos];
604 2 : node.mNumFlushed = node.mContent->GetChildCount();
605 :
606 2 : stackPos--;
607 : }
608 :
609 2 : mNotifyLevel = mStackPos - 1;
610 2 : }
611 :
612 : nsresult
613 1 : NS_NewHTMLContentSink(nsIHTMLContentSink** aResult,
614 : nsIDocument* aDoc,
615 : nsIURI* aURI,
616 : nsISupports* aContainer,
617 : nsIChannel* aChannel)
618 : {
619 1 : NS_ENSURE_ARG_POINTER(aResult);
620 :
621 2 : RefPtr<HTMLContentSink> it = new HTMLContentSink();
622 :
623 1 : nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
624 :
625 1 : NS_ENSURE_SUCCESS(rv, rv);
626 :
627 1 : *aResult = it;
628 1 : NS_ADDREF(*aResult);
629 :
630 1 : return NS_OK;
631 : }
632 :
633 1 : HTMLContentSink::HTMLContentSink()
634 : : mMaxTextRun(0)
635 : , mCurrentContext(nullptr)
636 : , mHeadContext(nullptr)
637 : , mHaveSeenHead(false)
638 1 : , mNotifiedRootInsertion(false)
639 : {
640 1 : }
641 :
642 0 : HTMLContentSink::~HTMLContentSink()
643 : {
644 0 : if (mNotificationTimer) {
645 0 : mNotificationTimer->Cancel();
646 : }
647 :
648 0 : int32_t numContexts = mContextStack.Length();
649 :
650 0 : if (mCurrentContext == mHeadContext && numContexts > 0) {
651 : // Pop off the second html context if it's not done earlier
652 0 : mContextStack.RemoveElementAt(--numContexts);
653 : }
654 :
655 : int32_t i;
656 0 : for (i = 0; i < numContexts; i++) {
657 0 : SinkContext* sc = mContextStack.ElementAt(i);
658 0 : if (sc) {
659 0 : sc->End();
660 0 : if (sc == mCurrentContext) {
661 0 : mCurrentContext = nullptr;
662 : }
663 :
664 0 : delete sc;
665 : }
666 : }
667 :
668 0 : if (mCurrentContext == mHeadContext) {
669 0 : mCurrentContext = nullptr;
670 : }
671 :
672 0 : delete mCurrentContext;
673 :
674 0 : delete mHeadContext;
675 0 : }
676 :
677 : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLContentSink)
678 :
679 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLContentSink, nsContentSink)
680 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mHTMLDocument)
681 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
682 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mBody)
683 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mHead)
684 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
685 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLContentSink,
686 : nsContentSink)
687 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHTMLDocument)
688 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
689 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBody)
690 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHead)
691 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
692 :
693 6 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLContentSink)
694 : NS_INTERFACE_TABLE_BEGIN
695 : NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIContentSink)
696 : NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIHTMLContentSink)
697 5 : NS_INTERFACE_TABLE_END
698 5 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsContentSink)
699 :
700 19 : NS_IMPL_ADDREF_INHERITED(HTMLContentSink, nsContentSink)
701 19 : NS_IMPL_RELEASE_INHERITED(HTMLContentSink, nsContentSink)
702 :
703 : nsresult
704 1 : HTMLContentSink::Init(nsIDocument* aDoc,
705 : nsIURI* aURI,
706 : nsISupports* aContainer,
707 : nsIChannel* aChannel)
708 : {
709 1 : NS_ENSURE_TRUE(aContainer, NS_ERROR_NULL_POINTER);
710 :
711 1 : nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
712 1 : if (NS_FAILED(rv)) {
713 0 : return rv;
714 : }
715 :
716 1 : aDoc->AddObserver(this);
717 1 : mIsDocumentObserver = true;
718 1 : mHTMLDocument = do_QueryInterface(aDoc);
719 :
720 1 : NS_ASSERTION(mDocShell, "oops no docshell!");
721 :
722 : // Changed from 8192 to greatly improve page loading performance on
723 : // large pages. See bugzilla bug 77540.
724 1 : mMaxTextRun = Preferences::GetInt("content.maxtextrun", 8191);
725 :
726 2 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
727 2 : nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nullptr,
728 : kNameSpaceID_XHTML,
729 1 : nsIDOMNode::ELEMENT_NODE);
730 :
731 : // Make root part
732 1 : mRoot = NS_NewHTMLHtmlElement(nodeInfo.forget());
733 1 : if (!mRoot) {
734 0 : return NS_ERROR_OUT_OF_MEMORY;
735 : }
736 :
737 1 : NS_ASSERTION(mDocument->GetChildCount() == 0,
738 : "Document should have no kids here!");
739 1 : rv = mDocument->AppendChildTo(mRoot, false);
740 1 : NS_ENSURE_SUCCESS(rv, rv);
741 :
742 : // Make head part
743 2 : nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::head,
744 : nullptr, kNameSpaceID_XHTML,
745 1 : nsIDOMNode::ELEMENT_NODE);
746 :
747 1 : mHead = NS_NewHTMLHeadElement(nodeInfo.forget());
748 1 : if (NS_FAILED(rv)) {
749 0 : return NS_ERROR_OUT_OF_MEMORY;
750 : }
751 :
752 1 : mRoot->AppendChildTo(mHead, false);
753 :
754 1 : mCurrentContext = new SinkContext(this);
755 1 : mCurrentContext->Begin(eHTMLTag_html, mRoot, 0, -1);
756 1 : mContextStack.AppendElement(mCurrentContext);
757 :
758 1 : return NS_OK;
759 : }
760 :
761 : NS_IMETHODIMP
762 1 : HTMLContentSink::WillParse(void)
763 : {
764 1 : return WillParseImpl();
765 : }
766 :
767 : NS_IMETHODIMP
768 1 : HTMLContentSink::WillBuildModel(nsDTDMode aDTDMode)
769 : {
770 1 : WillBuildModelImpl();
771 :
772 1 : if (mHTMLDocument) {
773 1 : nsCompatibility mode = eCompatibility_NavQuirks;
774 1 : switch (aDTDMode) {
775 : case eDTDMode_full_standards:
776 0 : mode = eCompatibility_FullStandards;
777 0 : break;
778 : case eDTDMode_almost_standards:
779 0 : mode = eCompatibility_AlmostStandards;
780 0 : break;
781 : default:
782 1 : break;
783 : }
784 1 : mHTMLDocument->SetCompatibilityMode(mode);
785 : }
786 :
787 : // Notify document that the load is beginning
788 1 : mDocument->BeginLoad();
789 :
790 1 : return NS_OK;
791 : }
792 :
793 : NS_IMETHODIMP
794 1 : HTMLContentSink::DidBuildModel(bool aTerminated)
795 : {
796 1 : DidBuildModelImpl(aTerminated);
797 :
798 : // Reflow the last batch of content
799 1 : if (mBody) {
800 1 : mCurrentContext->FlushTags();
801 0 : } else if (!mLayoutStarted) {
802 : // We never saw the body, and layout never got started. Force
803 : // layout *now*, to get an initial reflow.
804 : // NOTE: only force the layout if we are NOT destroying the
805 : // docshell. If we are destroying it, then starting layout will
806 : // likely cause us to crash, or at best waste a lot of time as we
807 : // are just going to tear it down anyway.
808 0 : bool bDestroying = true;
809 0 : if (mDocShell) {
810 0 : mDocShell->IsBeingDestroyed(&bDestroying);
811 : }
812 :
813 0 : if (!bDestroying) {
814 0 : StartLayout(false);
815 : }
816 : }
817 :
818 1 : ScrollToRef();
819 :
820 : // Make sure we no longer respond to document mutations. We've flushed all
821 : // our notifications out, so there's no need to do anything else here.
822 :
823 : // XXXbz I wonder whether we could End() our contexts here too, or something,
824 : // just to make sure we no longer notify... Or is the mIsDocumentObserver
825 : // thing sufficient?
826 1 : mDocument->RemoveObserver(this);
827 1 : mIsDocumentObserver = false;
828 :
829 1 : mDocument->EndLoad();
830 :
831 1 : DropParserAndPerfHint();
832 :
833 1 : return NS_OK;
834 : }
835 :
836 : NS_IMETHODIMP
837 1 : HTMLContentSink::SetParser(nsParserBase* aParser)
838 : {
839 1 : NS_PRECONDITION(aParser, "Should have a parser here!");
840 1 : mParser = aParser;
841 1 : return NS_OK;
842 : }
843 :
844 : nsresult
845 1 : HTMLContentSink::CloseHTML()
846 : {
847 1 : if (mHeadContext) {
848 0 : if (mCurrentContext == mHeadContext) {
849 0 : uint32_t numContexts = mContextStack.Length();
850 :
851 : // Pop off the second html context if it's not done earlier
852 0 : mCurrentContext = mContextStack.ElementAt(--numContexts);
853 0 : mContextStack.RemoveElementAt(numContexts);
854 : }
855 :
856 0 : mHeadContext->End();
857 :
858 0 : delete mHeadContext;
859 0 : mHeadContext = nullptr;
860 : }
861 :
862 1 : return NS_OK;
863 : }
864 :
865 : nsresult
866 1 : HTMLContentSink::OpenBody()
867 : {
868 1 : CloseHeadContext(); // do this just in case if the HEAD was left open!
869 :
870 : // if we already have a body we're done
871 1 : if (mBody) {
872 0 : return NS_OK;
873 : }
874 :
875 1 : nsresult rv = mCurrentContext->OpenBody();
876 :
877 1 : if (NS_FAILED(rv)) {
878 0 : return rv;
879 : }
880 :
881 1 : mBody = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
882 :
883 1 : if (mCurrentContext->mStackPos > 1) {
884 1 : int32_t parentIndex = mCurrentContext->mStackPos - 2;
885 1 : nsGenericHTMLElement *parent = mCurrentContext->mStack[parentIndex].mContent;
886 1 : int32_t numFlushed = mCurrentContext->mStack[parentIndex].mNumFlushed;
887 1 : int32_t childCount = parent->GetChildCount();
888 1 : NS_ASSERTION(numFlushed < childCount, "Already notified on the body?");
889 :
890 : int32_t insertionPoint =
891 1 : mCurrentContext->mStack[parentIndex].mInsertionPoint;
892 :
893 : // XXX: I have yet to see a case where numFlushed is non-zero and
894 : // insertionPoint is not -1, but this code will try to handle
895 : // those cases too.
896 :
897 1 : uint32_t oldUpdates = mUpdatesInNotification;
898 1 : mUpdatesInNotification = 0;
899 1 : if (insertionPoint != -1) {
900 0 : NotifyInsert(parent, mBody, insertionPoint - 1);
901 : } else {
902 1 : NotifyAppend(parent, numFlushed);
903 : }
904 1 : mCurrentContext->mStack[parentIndex].mNumFlushed = childCount;
905 1 : if (mUpdatesInNotification > 1) {
906 0 : UpdateChildCounts();
907 : }
908 1 : mUpdatesInNotification = oldUpdates;
909 : }
910 :
911 1 : StartLayout(false);
912 :
913 1 : return NS_OK;
914 : }
915 :
916 : nsresult
917 1 : HTMLContentSink::CloseBody()
918 : {
919 : // Flush out anything that's left
920 1 : mCurrentContext->FlushTags();
921 1 : mCurrentContext->CloseBody();
922 :
923 1 : return NS_OK;
924 : }
925 :
926 : NS_IMETHODIMP
927 2 : HTMLContentSink::OpenContainer(ElementType aElementType)
928 : {
929 2 : nsresult rv = NS_OK;
930 :
931 2 : switch (aElementType) {
932 : case eBody:
933 1 : rv = OpenBody();
934 1 : break;
935 : case eHTML:
936 1 : if (mRoot) {
937 : // If we've already hit this code once, then we're done
938 1 : if (!mNotifiedRootInsertion) {
939 1 : NotifyRootInsertion();
940 : }
941 1 : ProcessOfflineManifest(mRoot);
942 : }
943 1 : break;
944 : }
945 :
946 2 : return rv;
947 : }
948 :
949 : NS_IMETHODIMP
950 2 : HTMLContentSink::CloseContainer(const ElementType aTag)
951 : {
952 2 : nsresult rv = NS_OK;
953 :
954 2 : switch (aTag) {
955 : case eBody:
956 1 : rv = CloseBody();
957 1 : break;
958 : case eHTML:
959 1 : rv = CloseHTML();
960 1 : break;
961 : }
962 :
963 2 : return rv;
964 : }
965 :
966 : NS_IMETHODIMP
967 0 : HTMLContentSink::WillInterrupt()
968 : {
969 0 : return WillInterruptImpl();
970 : }
971 :
972 : NS_IMETHODIMP
973 1 : HTMLContentSink::WillResume()
974 : {
975 1 : return WillResumeImpl();
976 : }
977 :
978 : void
979 1 : HTMLContentSink::CloseHeadContext()
980 : {
981 1 : if (mCurrentContext) {
982 1 : if (!mCurrentContext->IsCurrentContainer(eHTMLTag_head))
983 1 : return;
984 :
985 0 : mCurrentContext->FlushTags();
986 : }
987 :
988 0 : if (!mContextStack.IsEmpty())
989 : {
990 0 : uint32_t n = mContextStack.Length() - 1;
991 0 : mCurrentContext = mContextStack.ElementAt(n);
992 0 : mContextStack.RemoveElementAt(n);
993 : }
994 : }
995 :
996 : void
997 1 : HTMLContentSink::NotifyInsert(nsIContent* aContent,
998 : nsIContent* aChildContent,
999 : int32_t aIndexInContainer)
1000 : {
1001 1 : if (aContent && aContent->GetUncomposedDoc() != mDocument) {
1002 : // aContent is not actually in our document anymore.... Just bail out of
1003 : // here; notifying on our document for this insert would be wrong.
1004 0 : return;
1005 : }
1006 :
1007 1 : mInNotification++;
1008 :
1009 : {
1010 : // Scope so we call EndUpdate before we decrease mInNotification
1011 2 : MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_CONTENT_MODEL, !mBeganUpdate);
1012 1 : nsNodeUtils::ContentInserted(NODE_FROM(aContent, mDocument),
1013 1 : aChildContent, aIndexInContainer);
1014 1 : mLastNotificationTime = PR_Now();
1015 : }
1016 :
1017 1 : mInNotification--;
1018 : }
1019 :
1020 : void
1021 1 : HTMLContentSink::NotifyRootInsertion()
1022 : {
1023 1 : NS_PRECONDITION(!mNotifiedRootInsertion, "Double-notifying on root?");
1024 1 : NS_ASSERTION(!mLayoutStarted,
1025 : "How did we start layout without notifying on root?");
1026 : // Now make sure to notify that we have now inserted our root. If
1027 : // there has been no initial reflow yet it'll be a no-op, but if
1028 : // there has been one we need this to get its frames constructed.
1029 : // Note that if mNotifiedRootInsertion is true we don't notify here,
1030 : // since that just means there are multiple <html> tags in the
1031 : // document; in those cases we just want to put all the attrs on one
1032 : // tag.
1033 1 : mNotifiedRootInsertion = true;
1034 1 : int32_t index = mDocument->IndexOf(mRoot);
1035 1 : NS_ASSERTION(index != -1, "mRoot not child of document?");
1036 1 : NotifyInsert(nullptr, mRoot, index);
1037 :
1038 : // Now update the notification information in all our
1039 : // contexts, since we just inserted the root and notified on
1040 : // our whole tree
1041 1 : UpdateChildCounts();
1042 1 : }
1043 :
1044 : void
1045 1 : HTMLContentSink::UpdateChildCounts()
1046 : {
1047 1 : uint32_t numContexts = mContextStack.Length();
1048 2 : for (uint32_t i = 0; i < numContexts; i++) {
1049 1 : SinkContext* sc = mContextStack.ElementAt(i);
1050 :
1051 1 : sc->UpdateChildCounts();
1052 : }
1053 :
1054 1 : mCurrentContext->UpdateChildCounts();
1055 1 : }
1056 :
1057 : void
1058 0 : HTMLContentSink::FlushPendingNotifications(FlushType aType)
1059 : {
1060 : // Only flush tags if we're not doing the notification ourselves
1061 : // (since we aren't reentrant)
1062 0 : if (!mInNotification) {
1063 : // Only flush if we're still a document observer (so that our child counts
1064 : // should be correct).
1065 0 : if (mIsDocumentObserver) {
1066 0 : if (aType >= FlushType::ContentAndNotify) {
1067 0 : FlushTags();
1068 : }
1069 : }
1070 0 : if (aType >= FlushType::EnsurePresShellInitAndFrames) {
1071 : // Make sure that layout has started so that the reflow flush
1072 : // will actually happen.
1073 0 : StartLayout(true);
1074 : }
1075 : }
1076 0 : }
1077 :
1078 : nsresult
1079 1 : HTMLContentSink::FlushTags()
1080 : {
1081 1 : if (!mNotifiedRootInsertion) {
1082 0 : NotifyRootInsertion();
1083 0 : return NS_OK;
1084 : }
1085 :
1086 1 : return mCurrentContext ? mCurrentContext->FlushTags() : NS_OK;
1087 : }
1088 :
1089 : void
1090 0 : HTMLContentSink::SetDocumentCharset(NotNull<const Encoding*> aEncoding)
1091 : {
1092 0 : MOZ_ASSERT_UNREACHABLE("<meta charset> case doesn't occur with about:blank");
1093 : }
1094 :
1095 : nsISupports *
1096 0 : HTMLContentSink::GetTarget()
1097 : {
1098 0 : return mDocument;
1099 : }
1100 :
1101 : bool
1102 1 : HTMLContentSink::IsScriptExecuting()
1103 : {
1104 1 : return IsScriptExecutingImpl();
1105 : }
|