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 https://mozilla.org/MPL/2.0/. */
8 #include "mozilla/dom/PrototypeDocumentContentSink.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/URL.h"
12 #include "nsIContent.h"
14 #include "nsNetUtil.h"
15 #include "nsHTMLParts.h"
17 #include "mozilla/StyleSheetInlines.h"
18 #include "mozilla/css/Loader.h"
19 #include "nsGkAtoms.h"
20 #include "nsContentUtils.h"
21 #include "nsDocElementCreatedNotificationRunner.h"
22 #include "nsIScriptContext.h"
23 #include "nsNameSpaceManager.h"
24 #include "nsIScriptError.h"
26 #include "mozilla/Logging.h"
28 #include "nsIScriptElement.h"
29 #include "nsReadableUtils.h"
30 #include "nsUnicharUtils.h"
31 #include "nsIChannel.h"
32 #include "nsNodeInfoManager.h"
33 #include "nsContentCreatorFunctions.h"
34 #include "nsIContentPolicy.h"
35 #include "nsContentPolicyUtils.h"
37 #include "nsIScriptGlobalObject.h"
38 #include "mozAutoDocUpdate.h"
39 #include "nsMimeTypes.h"
40 #include "nsHtml5SVGLoadDispatcher.h"
41 #include "nsTextNode.h"
42 #include "mozilla/dom/AutoEntryScript.h"
43 #include "mozilla/dom/CDATASection.h"
44 #include "mozilla/dom/Comment.h"
45 #include "mozilla/dom/DocumentType.h"
46 #include "mozilla/dom/Element.h"
47 #include "mozilla/dom/HTMLTemplateElement.h"
48 #include "mozilla/dom/ProcessingInstruction.h"
49 #include "mozilla/dom/XMLStylesheetProcessingInstruction.h"
50 #include "mozilla/dom/ScriptLoader.h"
51 #include "mozilla/LoadInfo.h"
52 #include "mozilla/PresShell.h"
53 #include "mozilla/ProfilerLabels.h"
54 #include "mozilla/RefPtr.h"
55 #include "mozilla/Try.h"
57 #include "nsXULPrototypeCache.h"
58 #include "nsXULElement.h"
59 #include "mozilla/CycleCollectedJSContext.h"
60 #include "js/CompilationAndEvaluation.h"
61 #include "js/experimental/JSStencil.h"
62 #include "js/Utility.h" // JS::FreePolicy
64 using namespace mozilla
;
65 using namespace mozilla::dom
;
67 LazyLogModule
PrototypeDocumentContentSink::gLog("PrototypeDocument");
69 nsresult
NS_NewPrototypeDocumentContentSink(nsIContentSink
** aResult
,
70 Document
* aDoc
, nsIURI
* aURI
,
71 nsISupports
* aContainer
,
72 nsIChannel
* aChannel
) {
73 MOZ_ASSERT(nullptr != aResult
, "null ptr");
74 if (nullptr == aResult
) {
75 return NS_ERROR_NULL_POINTER
;
77 RefPtr
<PrototypeDocumentContentSink
> it
= new PrototypeDocumentContentSink();
79 nsresult rv
= it
->Init(aDoc
, aURI
, aContainer
, aChannel
);
80 NS_ENSURE_SUCCESS(rv
, rv
);
86 namespace mozilla::dom
{
88 PrototypeDocumentContentSink::PrototypeDocumentContentSink()
89 : mNextSrcLoadWaiter(nullptr),
90 mCurrentScriptProto(nullptr),
91 mOffThreadCompiling(false),
95 PrototypeDocumentContentSink::~PrototypeDocumentContentSink() {
97 mNextSrcLoadWaiter
== nullptr,
98 "unreferenced document still waiting for script source to load?");
101 nsresult
PrototypeDocumentContentSink::Init(Document
* aDoc
, nsIURI
* aURI
,
102 nsISupports
* aContainer
,
103 nsIChannel
* aChannel
) {
104 MOZ_ASSERT(aDoc
, "null ptr");
105 MOZ_ASSERT(aURI
, "null ptr");
109 mDocument
->SetDelayFrameLoaderInitialization(true);
110 mDocument
->SetMayStartLayout(false);
112 // Get the URI. this should match the uri used for the OnNewURI call in
113 // nsDocShell::CreateDocumentViewer.
114 nsresult rv
= NS_GetFinalChannelURI(aChannel
, getter_AddRefs(mDocumentURI
));
115 NS_ENSURE_SUCCESS(rv
, rv
);
117 mScriptLoader
= mDocument
->ScriptLoader();
122 NS_IMPL_CYCLE_COLLECTION(PrototypeDocumentContentSink
, mParser
, mDocumentURI
,
123 mDocument
, mScriptLoader
, mContextStack
,
126 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PrototypeDocumentContentSink
)
127 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIContentSink
)
128 NS_INTERFACE_MAP_ENTRY(nsIContentSink
)
129 NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver
)
130 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver
)
131 NS_INTERFACE_MAP_ENTRY(nsIOffThreadScriptReceiver
)
134 NS_IMPL_CYCLE_COLLECTING_ADDREF(PrototypeDocumentContentSink
)
135 NS_IMPL_CYCLE_COLLECTING_RELEASE(PrototypeDocumentContentSink
)
137 //----------------------------------------------------------------------
139 // nsIContentSink interface
142 void PrototypeDocumentContentSink::SetDocumentCharset(
143 NotNull
<const Encoding
*> aEncoding
) {
145 mDocument
->SetDocumentCharacterSet(aEncoding
);
149 nsISupports
* PrototypeDocumentContentSink::GetTarget() {
150 return ToSupports(mDocument
);
153 bool PrototypeDocumentContentSink::IsScriptExecuting() {
154 return !!mScriptLoader
->GetCurrentScript();
158 PrototypeDocumentContentSink::SetParser(nsParserBase
* aParser
) {
159 MOZ_ASSERT(aParser
, "Should have a parser here!");
164 nsIParser
* PrototypeDocumentContentSink::GetParser() {
165 return static_cast<nsIParser
*>(mParser
.get());
168 void PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled() {
169 if (mParser
&& mParser
->IsParserEnabled()) {
170 GetParser()->ContinueInterruptedParsing();
174 void PrototypeDocumentContentSink::ContinueInterruptedParsingAsync() {
175 nsCOMPtr
<nsIRunnable
> ev
= NewRunnableMethod(
176 "PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled", this,
177 &PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled
);
178 mDocument
->Dispatch(ev
.forget());
181 //----------------------------------------------------------------------
183 // PrototypeDocumentContentSink::ContextStack
186 PrototypeDocumentContentSink::ContextStack::ContextStack()
187 : mTop(nullptr), mDepth(0) {}
189 PrototypeDocumentContentSink::ContextStack::~ContextStack() { Clear(); }
191 void PrototypeDocumentContentSink::ContextStack::Traverse(
192 nsCycleCollectionTraversalCallback
& aCallback
, const char* aName
,
194 aFlags
|= CycleCollectionEdgeNameArrayFlag
;
195 Entry
* current
= mTop
;
197 CycleCollectionNoteChild(aCallback
, current
->mElement
, aName
, aFlags
);
198 current
= current
->mNext
;
202 void PrototypeDocumentContentSink::ContextStack::Clear() {
204 Entry
* doomed
= mTop
;
206 NS_IF_RELEASE(doomed
->mElement
);
212 nsresult
PrototypeDocumentContentSink::ContextStack::Push(
213 nsXULPrototypeElement
* aPrototype
, nsIContent
* aElement
) {
214 Entry
* entry
= new Entry
;
215 entry
->mPrototype
= aPrototype
;
216 entry
->mElement
= aElement
;
217 NS_IF_ADDREF(entry
->mElement
);
227 nsresult
PrototypeDocumentContentSink::ContextStack::Pop() {
228 if (mDepth
== 0) return NS_ERROR_UNEXPECTED
;
230 Entry
* doomed
= mTop
;
234 NS_IF_RELEASE(doomed
->mElement
);
239 nsresult
PrototypeDocumentContentSink::ContextStack::Peek(
240 nsXULPrototypeElement
** aPrototype
, nsIContent
** aElement
,
242 if (mDepth
== 0) return NS_ERROR_UNEXPECTED
;
244 *aPrototype
= mTop
->mPrototype
;
245 *aElement
= mTop
->mElement
;
246 NS_IF_ADDREF(*aElement
);
247 *aIndex
= mTop
->mIndex
;
252 nsresult
PrototypeDocumentContentSink::ContextStack::SetTopIndex(
254 if (mDepth
== 0) return NS_ERROR_UNEXPECTED
;
256 mTop
->mIndex
= aIndex
;
260 //----------------------------------------------------------------------
262 // Content model walking routines
265 nsresult
PrototypeDocumentContentSink::OnPrototypeLoadDone(
266 nsXULPrototypeDocument
* aPrototype
) {
267 mCurrentPrototype
= aPrototype
;
268 mDocument
->SetPrototypeDocument(aPrototype
);
270 nsresult rv
= PrepareToWalk();
271 NS_ENSURE_SUCCESS(rv
, rv
);
278 nsresult
PrototypeDocumentContentSink::PrepareToWalk() {
279 MOZ_ASSERT(mCurrentPrototype
);
282 mStillWalking
= true;
284 // Notify document that the load is beginning
285 mDocument
->BeginLoad();
287 // Get the prototype's root element and initialize the context
288 // stack for the prototype walk.
289 nsXULPrototypeElement
* proto
= mCurrentPrototype
->GetRootElement();
292 if (MOZ_LOG_TEST(gLog
, LogLevel::Error
)) {
293 nsCOMPtr
<nsIURI
> url
= mCurrentPrototype
->GetURI();
295 nsAutoCString urlspec
;
296 rv
= url
->GetSpec(urlspec
);
297 if (NS_FAILED(rv
)) return rv
;
299 MOZ_LOG(gLog
, LogLevel::Error
,
300 ("prototype: error parsing '%s'", urlspec
.get()));
306 nsINode
* nodeToInsertBefore
= mDocument
->GetFirstChild();
308 const nsTArray
<RefPtr
<nsXULPrototypePI
> >& processingInstructions
=
309 mCurrentPrototype
->GetProcessingInstructions();
311 uint32_t total
= processingInstructions
.Length();
312 for (uint32_t i
= 0; i
< total
; ++i
) {
313 rv
= CreateAndInsertPI(processingInstructions
[i
], mDocument
,
315 if (NS_FAILED(rv
)) return rv
;
318 // Do one-time initialization.
319 RefPtr
<Element
> root
;
321 // Add the root element
322 rv
= CreateElementFromPrototype(proto
, getter_AddRefs(root
), nullptr);
323 if (NS_FAILED(rv
)) return rv
;
326 mDocument
->AppendChildTo(root
, false, error
);
327 if (error
.Failed()) {
328 return error
.StealNSResult();
331 // TODO(emilio): Should this really notify? We don't notify of appends anyhow,
332 // and we just appended the root so no styles can possibly depend on it.
333 mDocument
->UpdateDocumentStates(DocumentState::RTL_LOCALE
, true);
335 nsContentUtils::AddScriptRunner(
336 new nsDocElementCreatedNotificationRunner(mDocument
));
338 // There'd better not be anything on the context stack at this
339 // point! This is the basis case for our "induction" in
340 // ResumeWalk(), below, which'll assume that there's always a
341 // content element on the context stack if we're in the document.
342 NS_ASSERTION(mContextStack
.Depth() == 0,
343 "something's on the context stack already");
344 if (mContextStack
.Depth() != 0) return NS_ERROR_UNEXPECTED
;
346 rv
= mContextStack
.Push(proto
, root
);
347 if (NS_FAILED(rv
)) return rv
;
352 nsresult
PrototypeDocumentContentSink::CreateAndInsertPI(
353 const nsXULPrototypePI
* aProtoPI
, nsINode
* aParent
, nsINode
* aBeforeThis
) {
354 MOZ_ASSERT(aProtoPI
, "null ptr");
355 MOZ_ASSERT(aParent
, "null ptr");
357 RefPtr
<ProcessingInstruction
> node
=
358 NS_NewXMLProcessingInstruction(aParent
->OwnerDoc()->NodeInfoManager(),
359 aProtoPI
->mTarget
, aProtoPI
->mData
);
362 if (aProtoPI
->mTarget
.EqualsLiteral("xml-stylesheet")) {
363 MOZ_ASSERT(LinkStyle::FromNode(*node
),
364 "XML Stylesheet node does not implement LinkStyle!");
365 auto* pi
= static_cast<XMLStylesheetProcessingInstruction
*>(node
.get());
366 rv
= InsertXMLStylesheetPI(aProtoPI
, aParent
, aBeforeThis
, pi
);
368 // No special processing, just add the PI to the document.
370 aParent
->InsertChildBefore(node
->AsContent(),
371 aBeforeThis
? aBeforeThis
->AsContent() : nullptr,
373 rv
= error
.StealNSResult();
379 nsresult
PrototypeDocumentContentSink::InsertXMLStylesheetPI(
380 const nsXULPrototypePI
* aProtoPI
, nsINode
* aParent
, nsINode
* aBeforeThis
,
381 XMLStylesheetProcessingInstruction
* aPINode
) {
382 // We want to be notified when the style sheet finishes loading, so
383 // disable style sheet loading for now.
384 aPINode
->DisableUpdates();
385 aPINode
->OverrideBaseURI(mCurrentPrototype
->GetURI());
388 aParent
->InsertChildBefore(
389 aPINode
, aBeforeThis
? aBeforeThis
->AsContent() : nullptr, false, rv
);
391 return rv
.StealNSResult();
394 // load the stylesheet if necessary, passing ourselves as
396 auto result
= aPINode
->EnableUpdatesAndUpdateStyleSheet(this);
397 if (result
.isErr()) {
398 // Ignore errors from UpdateStyleSheet; we don't want failure to
399 // do that to break the XUL document load. But do propagate out
400 // NS_ERROR_OUT_OF_MEMORY.
401 if (result
.unwrapErr() == NS_ERROR_OUT_OF_MEMORY
) {
402 return result
.unwrapErr();
407 auto update
= result
.unwrap();
408 if (update
.ShouldBlock()) {
415 void PrototypeDocumentContentSink::CloseElement(Element
* aElement
,
417 if (nsIContent::RequiresDoneAddingChildren(
418 aElement
->NodeInfo()->NamespaceID(),
419 aElement
->NodeInfo()->NameAtom())) {
420 aElement
->DoneAddingChildren(false);
423 if (auto* linkStyle
= LinkStyle::FromNode(*aElement
)) {
424 auto result
= linkStyle
->EnableUpdatesAndUpdateStyleSheet(this);
425 if (result
.isOk() && result
.unwrap().ShouldBlock()) {
435 // See bug 370111 and bug 1495946. We don't cache inline styles nor module
436 // scripts in the prototype cache, and we don't notify on node insertion, so
437 // we need to do this for the stylesheet / script to be properly processed.
438 // This kinda sucks, but notifying was a pretty sizeable perf regression so...
439 if (aElement
->IsHTMLElement(nsGkAtoms::script
) ||
440 aElement
->IsSVGElement(nsGkAtoms::script
)) {
441 nsCOMPtr
<nsIScriptElement
> sele
= do_QueryInterface(aElement
);
442 MOZ_ASSERT(sele
, "Node didn't QI to script.");
443 if (sele
->GetScriptIsModule()) {
444 DebugOnly
<bool> block
= sele
->AttemptToExecute();
445 MOZ_ASSERT(!block
, "<script type=module> shouldn't block the parser");
450 nsresult
PrototypeDocumentContentSink::ResumeWalk() {
451 nsresult rv
= ResumeWalkInternal();
453 nsContentUtils::ReportToConsoleNonLocalized(
454 u
"Failed to load document from prototype document."_ns
,
455 nsIScriptError::errorFlag
, "Prototype Document"_ns
, mDocument
,
461 nsresult
PrototypeDocumentContentSink::ResumeWalkInternal() {
462 MOZ_ASSERT(mStillWalking
);
463 // Walk the prototype and build the delegate content model. The
464 // walk is performed in a top-down, left-to-right fashion. That
465 // is, a parent is built before any of its children; a node is
466 // only built after all of its siblings to the left are fully
469 // It is interruptable so that transcluded documents (e.g.,
470 // <html:script src="..." />) can be properly re-loaded if the
471 // cached copy of the document becomes stale.
473 nsCOMPtr
<nsIURI
> docURI
=
474 mCurrentPrototype
? mCurrentPrototype
->GetURI() : nullptr;
477 // Begin (or resume) walking the current prototype.
479 while (mContextStack
.Depth() > 0) {
480 // Look at the top of the stack to determine what we're
481 // currently working on.
482 // This will always be a node already constructed and
483 // inserted to the actual document.
484 nsXULPrototypeElement
* proto
;
485 nsCOMPtr
<nsIContent
> element
;
486 nsCOMPtr
<nsIContent
> nodeToPushTo
;
487 int32_t indx
; // all children of proto before indx (not
488 // inclusive) have already been constructed
489 rv
= mContextStack
.Peek(&proto
, getter_AddRefs(element
), &indx
);
490 if (NS_FAILED(rv
)) return rv
;
492 if (indx
>= (int32_t)proto
->mChildren
.Length()) {
494 // We've processed all of the prototype's children.
495 CloseElement(element
->AsElement(), /* aHadChildren = */ true);
497 // Now pop the context stack back up to the parent
498 // element and continue the prototype walk.
503 nodeToPushTo
= element
;
504 // For template elements append the content to the template's document
506 if (auto* templateElement
= HTMLTemplateElement::FromNode(element
)) {
507 nodeToPushTo
= templateElement
->Content();
510 // Grab the next child, and advance the current context stack
511 // to the next sibling to our right.
512 nsXULPrototypeNode
* childproto
= proto
->mChildren
[indx
];
513 mContextStack
.SetTopIndex(++indx
);
515 switch (childproto
->mType
) {
516 case nsXULPrototypeNode::eType_Element
: {
517 // An 'element', which may contain more content.
518 auto* protoele
= static_cast<nsXULPrototypeElement
*>(childproto
);
520 RefPtr
<Element
> child
;
521 MOZ_TRY(CreateElementFromPrototype(protoele
, getter_AddRefs(child
),
524 if (auto* linkStyle
= LinkStyle::FromNode(*child
)) {
525 linkStyle
->DisableUpdates();
528 // ...and append it to the content model.
530 nodeToPushTo
->AppendChildTo(child
, false, error
);
531 if (error
.Failed()) {
532 return error
.StealNSResult();
535 if (nsIContent::RequiresDoneCreatingElement(
536 protoele
->mNodeInfo
->NamespaceID(),
537 protoele
->mNodeInfo
->NameAtom())) {
538 child
->DoneCreatingElement();
541 // If it has children, push the element onto the context
542 // stack and begin to process them.
543 if (protoele
->mChildren
.Length() > 0) {
544 rv
= mContextStack
.Push(protoele
, child
);
545 if (NS_FAILED(rv
)) return rv
;
547 // If there are no children, close the element immediately.
548 CloseElement(child
, /* aHadChildren = */ false);
552 case nsXULPrototypeNode::eType_Script
: {
553 // A script reference. Execute the script immediately;
554 // this may have side effects in the content model.
555 auto* scriptproto
= static_cast<nsXULPrototypeScript
*>(childproto
);
556 if (scriptproto
->mSrcURI
) {
557 // A transcluded script reference; this may
558 // "block" our prototype walk if the script isn't
559 // cached, or the cached copy of the script is
560 // stale and must be reloaded.
562 rv
= LoadScript(scriptproto
, &blocked
);
563 // If the script cannot be loaded, just keep going!
565 if (NS_SUCCEEDED(rv
) && blocked
) return NS_OK
;
566 } else if (scriptproto
->HasStencil()) {
568 rv
= ExecuteScript(scriptproto
);
569 if (NS_FAILED(rv
)) return rv
;
573 case nsXULPrototypeNode::eType_Text
: {
574 nsNodeInfoManager
* nim
= nodeToPushTo
->NodeInfo()->NodeInfoManager();
575 // A simple text node.
576 RefPtr
<nsTextNode
> text
= new (nim
) nsTextNode(nim
);
578 auto* textproto
= static_cast<nsXULPrototypeText
*>(childproto
);
579 text
->SetText(textproto
->mValue
, false);
582 nodeToPushTo
->AppendChildTo(text
, false, error
);
583 if (error
.Failed()) {
584 return error
.StealNSResult();
588 case nsXULPrototypeNode::eType_PI
: {
589 auto* piProto
= static_cast<nsXULPrototypePI
*>(childproto
);
591 // <?xml-stylesheet?> doesn't have an effect
592 // outside the prolog, like it used to. Issue a warning.
594 if (piProto
->mTarget
.EqualsLiteral("xml-stylesheet")) {
595 AutoTArray
<nsString
, 1> params
= {piProto
->mTarget
};
597 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
598 "XUL Document"_ns
, nullptr,
599 nsContentUtils::eXUL_PROPERTIES
,
600 "PINotInProlog", params
, docURI
);
603 nsIContent
* parent
= element
.get();
605 // an inline script could have removed the root element
606 rv
= CreateAndInsertPI(piProto
, parent
, nullptr);
607 NS_ENSURE_SUCCESS(rv
, rv
);
612 MOZ_ASSERT_UNREACHABLE("Unexpected nsXULPrototypeNode::Type");
616 // Once we get here, the context stack will have been
617 // depleted. That means that the entire prototype has been
618 // walked and content has been constructed.
622 mStillWalking
= false;
623 return MaybeDoneWalking();
626 void PrototypeDocumentContentSink::InitialTranslationCompleted() {
630 nsresult
PrototypeDocumentContentSink::MaybeDoneWalking() {
631 if (mPendingSheets
> 0 || mStillWalking
) {
635 if (mDocument
->HasPendingInitialTranslation()) {
636 mDocument
->OnParsingCompleted();
640 return DoneWalking();
643 nsresult
PrototypeDocumentContentSink::DoneWalking() {
644 MOZ_ASSERT(mPendingSheets
== 0, "there are sheets to be loaded");
645 MOZ_ASSERT(!mStillWalking
, "walk not done");
646 MOZ_ASSERT(!mDocument
->HasPendingInitialTranslation(), "translation pending");
649 MOZ_ASSERT(mDocument
->GetReadyStateEnum() == Document::READYSTATE_LOADING
,
651 mDocument
->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE
);
652 mDocument
->NotifyPossibleTitleChange(false);
654 nsContentUtils::DispatchEventOnlyToChrome(mDocument
, mDocument
,
655 u
"MozBeforeInitialXULLayout"_ns
,
656 CanBubble::eYes
, Cancelable::eNo
);
660 mScriptLoader
->ParsingComplete(false);
661 mScriptLoader
->DeferCheckpointReached();
666 if (IsChromeURI(mDocumentURI
) &&
667 nsXULPrototypeCache::GetInstance()->IsEnabled()) {
669 nsXULPrototypeCache::GetInstance()->HasPrototype(mDocumentURI
,
671 if (!isCachedOnDisk
) {
672 if (!mDocument
->GetDocumentElement() ||
673 (mDocument
->GetDocumentElement()->NodeInfo()->Equals(
674 nsGkAtoms::parsererror
) &&
675 mDocument
->GetDocumentElement()->NodeInfo()->NamespaceEquals(
676 nsDependentAtomString(nsGkAtoms::nsuri_parsererror
)))) {
677 nsXULPrototypeCache::GetInstance()->RemovePrototype(mDocumentURI
);
679 nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype
);
684 mDocument
->SetDelayFrameLoaderInitialization(false);
685 RefPtr
<Document
> doc
= mDocument
;
686 doc
->MaybeInitializeFinalizeFrameLoaders();
688 // If the document we are loading has a reference or it is a
689 // frameset document, disable the scroll bars on the views.
691 doc
->SetScrollToRef(mDocument
->GetDocumentURI());
698 void PrototypeDocumentContentSink::StartLayout() {
699 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
700 "PrototypeDocumentContentSink::StartLayout", LAYOUT
,
701 mDocumentURI
->GetSpecOrDefault());
702 mDocument
->SetMayStartLayout(true);
703 RefPtr
<PresShell
> presShell
= mDocument
->GetPresShell();
704 if (presShell
&& !presShell
->DidInitialize()) {
705 nsresult rv
= presShell
->Initialize();
713 PrototypeDocumentContentSink::StyleSheetLoaded(StyleSheet
* aSheet
,
717 // Don't care about when alternate sheets finish loading
718 MOZ_ASSERT(mPendingSheets
> 0, "Unexpected StyleSheetLoaded notification");
722 return MaybeDoneWalking();
728 nsresult
PrototypeDocumentContentSink::LoadScript(
729 nsXULPrototypeScript
* aScriptProto
, bool* aBlock
) {
730 // Load a transcluded script
733 bool isChromeDoc
= IsChromeURI(mDocumentURI
);
735 if (isChromeDoc
&& aScriptProto
->HasStencil()) {
736 rv
= ExecuteScript(aScriptProto
);
738 // Ignore return value from execution, and don't block
743 // Try the XUL script cache, in case two XUL documents source the same
744 // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
745 // XXXbe the cache relies on aScriptProto's GC root!
746 bool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
748 if (isChromeDoc
&& useXULCache
) {
749 RefPtr
<JS::Stencil
> newStencil
=
750 nsXULPrototypeCache::GetInstance()->GetStencil(aScriptProto
->mSrcURI
);
752 // The script language for a proto must remain constant - we
753 // can't just change it for this unexpected language.
754 aScriptProto
->Set(newStencil
);
757 if (aScriptProto
->HasStencil()) {
758 rv
= ExecuteScript(aScriptProto
);
760 // Ignore return value from execution, and don't block
766 // Release stencil from FastLoad since we decided against using them
767 aScriptProto
->Set(nullptr);
769 // Set the current script prototype so that OnStreamComplete can report
770 // the right file if there are errors in the script.
771 NS_ASSERTION(!mCurrentScriptProto
,
772 "still loading a script when starting another load?");
773 mCurrentScriptProto
= aScriptProto
;
775 if (isChromeDoc
&& aScriptProto
->mSrcLoading
) {
776 // Another document load has started, which is still in progress.
777 // Remember to ResumeWalk this document when the load completes.
778 mNextSrcLoadWaiter
= aScriptProto
->mSrcLoadWaiters
;
779 aScriptProto
->mSrcLoadWaiters
= this;
782 nsCOMPtr
<nsILoadGroup
> group
=
784 ->GetDocumentLoadGroup(); // found in
785 // mozilla::dom::Document::SetScriptGlobalObject
787 // Note: the loader will keep itself alive while it's loading.
788 nsCOMPtr
<nsIStreamLoader
> loader
;
789 rv
= NS_NewStreamLoader(
790 getter_AddRefs(loader
), aScriptProto
->mSrcURI
,
792 mDocument
, // aRequestingContext
793 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT
,
794 nsIContentPolicy::TYPE_INTERNAL_SCRIPT
, group
);
797 mCurrentScriptProto
= nullptr;
801 aScriptProto
->mSrcLoading
= true;
804 // Block until OnStreamComplete resumes us.
810 PrototypeDocumentContentSink::OnStreamComplete(nsIStreamLoader
* aLoader
,
811 nsISupports
* context
,
814 const uint8_t* string
) {
815 nsCOMPtr
<nsIRequest
> request
;
816 aLoader
->GetRequest(getter_AddRefs(request
));
817 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
820 // print a load error on bad status
821 if (NS_FAILED(aStatus
)) {
823 nsCOMPtr
<nsIURI
> uri
;
824 channel
->GetURI(getter_AddRefs(uri
));
826 printf("Failed to load %s\n", uri
->GetSpecOrDefault().get());
832 // This is the completion routine that will be called when a
833 // transcluded script completes. Compile and execute the script
834 // if the load was successful, then continue building content
835 // from the prototype.
836 nsresult rv
= aStatus
;
838 NS_ASSERTION(mCurrentScriptProto
&& mCurrentScriptProto
->mSrcLoading
,
839 "script source not loading on unichar stream complete?");
840 if (!mCurrentScriptProto
) {
841 // XXX Wallpaper for bug 270042
845 if (NS_SUCCEEDED(aStatus
)) {
846 // If the including document is a FastLoad document, and we're
847 // compiling an out-of-line script (one with src=...), then we must
848 // be writing a new FastLoad file. If we were reading this script
849 // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
850 // nsXULContentSink.cpp) would have already deserialized a non-null
851 // script->mStencil, causing control flow at the top of LoadScript
852 // not to reach here.
853 nsCOMPtr
<nsIURI
> uri
= mCurrentScriptProto
->mSrcURI
;
855 // XXX should also check nsIHttpChannel::requestSucceeded
857 MOZ_ASSERT(!mOffThreadCompiling
,
858 "PrototypeDocument can't load multiple scripts at once");
860 UniquePtr
<Utf8Unit
[], JS::FreePolicy
> units
;
861 size_t unitsLength
= 0;
863 rv
= ScriptLoader::ConvertToUTF8(channel
, string
, stringLen
, u
""_ns
,
864 mDocument
, units
, unitsLength
);
865 if (NS_SUCCEEDED(rv
)) {
866 rv
= mCurrentScriptProto
->CompileMaybeOffThread(
867 std::move(units
), unitsLength
, uri
, 1, mDocument
, this);
868 if (NS_SUCCEEDED(rv
) && !mCurrentScriptProto
->HasStencil()) {
869 mOffThreadCompiling
= true;
870 mDocument
->BlockOnload();
876 return OnScriptCompileComplete(mCurrentScriptProto
->GetStencil(), rv
);
880 PrototypeDocumentContentSink::OnScriptCompileComplete(JS::Stencil
* aStencil
,
882 // The mCurrentScriptProto may have been cleared out by another
883 // PrototypeDocumentContentSink.
884 if (!mCurrentScriptProto
) {
888 // When compiling off thread the script will not have been attached to the
890 if (aStencil
&& !mCurrentScriptProto
->HasStencil()) {
891 mCurrentScriptProto
->Set(aStencil
);
894 // Allow load events to be fired once off thread compilation finishes.
895 if (mOffThreadCompiling
) {
896 mOffThreadCompiling
= false;
897 mDocument
->UnblockOnload(false);
900 // Clear mCurrentScriptProto now, but save it first for use below in
901 // the execute code, and in the while loop that resumes walks of other
902 // documents that raced to load this script.
903 nsXULPrototypeScript
* scriptProto
= mCurrentScriptProto
;
904 mCurrentScriptProto
= nullptr;
906 // Clear the prototype's loading flag before executing the script or
907 // resuming document walks, in case any of those control flows starts a
909 scriptProto
->mSrcLoading
= false;
911 nsresult rv
= aStatus
;
912 if (NS_SUCCEEDED(rv
)) {
913 rv
= ExecuteScript(scriptProto
);
915 // If the XUL cache is enabled, save the script object there in
916 // case different XUL documents source the same script.
918 // But don't save the script in the cache unless the master XUL
919 // document URL is a chrome: URL. It is valid for a URL such as
920 // about:config to translate into a master document URL, whose
921 // prototype document nodes -- including prototype scripts that
922 // hold GC roots protecting their mJSObject pointers -- are not
923 // cached in the XUL prototype cache. See StartDocumentLoad,
924 // the fillXULCache logic.
926 // A document such as about:config is free to load a script via
927 // a URL such as chrome://global/content/config.js, and we must
928 // not cache that script object without a prototype cache entry
929 // containing a companion nsXULPrototypeScript node that owns a
930 // GC root protecting the script object. Otherwise, the script
931 // cache entry will dangle once the uncached prototype document
932 // is released when its owning document is unloaded.
934 // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
935 // the true crime story.)
936 bool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
938 if (useXULCache
&& IsChromeURI(mDocumentURI
) && scriptProto
->HasStencil()) {
939 nsXULPrototypeCache::GetInstance()->PutStencil(scriptProto
->mSrcURI
,
940 scriptProto
->GetStencil());
942 // ignore any evaluation errors
947 // Load a pointer to the prototype-script's list of documents who
948 // raced to load the same script
949 PrototypeDocumentContentSink
** docp
= &scriptProto
->mSrcLoadWaiters
;
951 // Resume walking other documents that waited for this one's load, first
952 // executing the script we just compiled, in each doc's script context
953 PrototypeDocumentContentSink
* doc
;
954 while ((doc
= *docp
) != nullptr) {
955 NS_ASSERTION(doc
->mCurrentScriptProto
== scriptProto
,
956 "waiting for wrong script to load?");
957 doc
->mCurrentScriptProto
= nullptr;
959 // Unlink doc from scriptProto's list before executing and resuming
960 *docp
= doc
->mNextSrcLoadWaiter
;
961 doc
->mNextSrcLoadWaiter
= nullptr;
963 if (aStatus
== NS_BINDING_ABORTED
&& !scriptProto
->HasStencil()) {
964 // If the previous doc load was aborted, we want to try loading
965 // again for the next doc. Otherwise, one abort would lead to all
966 // subsequent waiting docs to abort as well.
968 doc
->LoadScript(scriptProto
, &block
);
973 // Execute only if we loaded and compiled successfully, then resume
974 if (NS_SUCCEEDED(aStatus
) && scriptProto
->HasStencil()) {
975 doc
->ExecuteScript(scriptProto
);
984 nsresult
PrototypeDocumentContentSink::ExecuteScript(
985 nsXULPrototypeScript
* aScript
) {
986 MOZ_ASSERT(aScript
!= nullptr, "null ptr");
987 NS_ENSURE_TRUE(aScript
, NS_ERROR_NULL_POINTER
);
989 nsIScriptGlobalObject
* scriptGlobalObject
;
990 bool aHasHadScriptHandlingObject
;
992 mDocument
->GetScriptHandlingObject(aHasHadScriptHandlingObject
);
994 NS_ENSURE_TRUE(scriptGlobalObject
, NS_ERROR_NOT_INITIALIZED
);
997 rv
= scriptGlobalObject
->EnsureScriptEnvironment();
998 NS_ENSURE_SUCCESS(rv
, rv
);
1000 // Execute the precompiled script with the given version
1003 // We're about to run script via JS_ExecuteScript, so we need an
1004 // AutoEntryScript. This is Gecko specific and not in any spec.
1005 AutoEntryScript
aes(scriptGlobalObject
, "precompiled XUL <script> element");
1006 JSContext
* cx
= aes
.cx();
1008 JS::Rooted
<JSScript
*> scriptObject(cx
);
1009 rv
= aScript
->InstantiateScript(cx
, &scriptObject
);
1010 NS_ENSURE_SUCCESS(rv
, rv
);
1012 JS::Rooted
<JSObject
*> global(cx
, JS::CurrentGlobalOrNull(cx
));
1013 NS_ENSURE_TRUE(xpc::Scriptability::Get(global
).Allowed(), NS_OK
);
1015 // On failure, ~AutoScriptEntry will handle exceptions, so
1016 // there is no need to manually check the return value.
1017 JS::Rooted
<JS::Value
> rval(cx
);
1018 Unused
<< JS_ExecuteScript(cx
, scriptObject
, &rval
);
1023 nsresult
PrototypeDocumentContentSink::CreateElementFromPrototype(
1024 nsXULPrototypeElement
* aPrototype
, Element
** aResult
, nsIContent
* aParent
) {
1025 // Create a content model element from a prototype element.
1026 MOZ_ASSERT(aPrototype
, "null ptr");
1027 if (!aPrototype
) return NS_ERROR_NULL_POINTER
;
1030 nsresult rv
= NS_OK
;
1032 if (MOZ_LOG_TEST(gLog
, LogLevel::Debug
)) {
1034 gLog
, LogLevel::Debug
,
1035 ("prototype: creating <%s> from prototype",
1036 NS_ConvertUTF16toUTF8(aPrototype
->mNodeInfo
->QualifiedName()).get()));
1039 RefPtr
<Element
> result
;
1041 Document
* doc
= aParent
? aParent
->OwnerDoc() : mDocument
.get();
1042 if (aPrototype
->mNodeInfo
->NamespaceEquals(kNameSpaceID_XUL
)) {
1043 const bool isRoot
= !aParent
;
1044 // If it's a XUL element, it'll be lightweight until somebody
1046 rv
= nsXULElement::CreateFromPrototype(aPrototype
, doc
, true, isRoot
,
1047 getter_AddRefs(result
));
1048 if (NS_FAILED(rv
)) return rv
;
1050 // If it's not a XUL element, it's gonna be heavyweight no matter
1051 // what. So we need to copy everything out of the prototype
1052 // into the element. Get a nodeinfo from our nodeinfo manager
1054 RefPtr
<NodeInfo
> newNodeInfo
= doc
->NodeInfoManager()->GetNodeInfo(
1055 aPrototype
->mNodeInfo
->NameAtom(),
1056 aPrototype
->mNodeInfo
->GetPrefixAtom(),
1057 aPrototype
->mNodeInfo
->NamespaceID(), nsINode::ELEMENT_NODE
);
1059 return NS_ERROR_OUT_OF_MEMORY
;
1061 const bool isScript
=
1062 newNodeInfo
->Equals(nsGkAtoms::script
, kNameSpaceID_XHTML
) ||
1063 newNodeInfo
->Equals(nsGkAtoms::script
, kNameSpaceID_SVG
);
1064 if (aPrototype
->mIsAtom
&&
1065 newNodeInfo
->NamespaceID() == kNameSpaceID_XHTML
) {
1066 rv
= NS_NewHTMLElement(getter_AddRefs(result
), newNodeInfo
.forget(),
1067 NOT_FROM_PARSER
, aPrototype
->mIsAtom
);
1069 rv
= NS_NewElement(getter_AddRefs(result
), newNodeInfo
.forget(),
1072 if (NS_FAILED(rv
)) return rv
;
1074 rv
= AddAttributes(aPrototype
, result
);
1075 if (NS_FAILED(rv
)) return rv
;
1078 nsCOMPtr
<nsIScriptElement
> sele
= do_QueryInterface(result
);
1079 MOZ_ASSERT(sele
, "Node didn't QI to script.");
1081 sele
->FreezeExecutionAttrs(doc
);
1082 // Script loading is handled by the this content sink, so prevent the
1083 // script from loading when it is bound to the document.
1085 // NOTE(emilio): This is only done for non-module scripts, because we
1086 // don't support caching modules properly yet, see the comment in
1087 // XULContentSinkImpl::OpenScript. For non-inline scripts, this is enough,
1088 // since we can start the load when the node is inserted. Non-inline
1089 // scripts need another special-case in CloseElement.
1090 if (!sele
->GetScriptIsModule()) {
1091 sele
->PreventExecution();
1096 // FIXME(bug 1627474): Is this right if this is inside an <html:template>?
1097 if (result
->HasAttr(nsGkAtoms::datal10nid
)) {
1098 mDocument
->mL10nProtoElements
.InsertOrUpdate(result
, RefPtr
{aPrototype
});
1099 result
->SetElementCreatedFromPrototypeAndHasUnmodifiedL10n();
1101 result
.forget(aResult
);
1105 nsresult
PrototypeDocumentContentSink::AddAttributes(
1106 nsXULPrototypeElement
* aPrototype
, Element
* aElement
) {
1109 for (size_t i
= 0; i
< aPrototype
->mAttributes
.Length(); ++i
) {
1110 nsXULPrototypeAttribute
* protoattr
= &(aPrototype
->mAttributes
[i
]);
1111 nsAutoString valueStr
;
1112 protoattr
->mValue
.ToString(valueStr
);
1114 rv
= aElement
->SetAttr(protoattr
->mName
.NamespaceID(),
1115 protoattr
->mName
.LocalName(),
1116 protoattr
->mName
.GetPrefix(), valueStr
, false);
1117 if (NS_FAILED(rv
)) return rv
;
1123 } // namespace mozilla::dom