1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 et tw=80: */
3 /* 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/. */
7 #include "mozilla/DebugOnly.h"
8 #include "mozilla/Likely.h"
9 #include "mozilla/dom/BrowsingContext.h"
10 #include "mozilla/dom/MediaList.h"
11 #include "mozilla/dom/ScriptLoader.h"
12 #include "mozilla/dom/nsCSPContext.h"
13 #include "mozilla/dom/nsCSPService.h"
15 #include "mozAutoDocUpdate.h"
16 #include "mozilla/IdleTaskRunner.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/ProfilerLabels.h"
19 #include "mozilla/ProfilerMarkers.h"
20 #include "mozilla/StaticPrefs_content.h"
21 #include "mozilla/StaticPrefs_security.h"
22 #include "mozilla/StaticPrefs_view_source.h"
23 #include "mozilla/Telemetry.h"
24 #include "mozilla/css/Loader.h"
25 #include "mozilla/fallible.h"
26 #include "nsContentUtils.h"
27 #include "nsDocShell.h"
29 #include "nsHTMLDocument.h"
30 #include "nsHtml5AutoPauseUpdate.h"
31 #include "nsHtml5Parser.h"
32 #include "nsHtml5StreamParser.h"
33 #include "nsHtml5Tokenizer.h"
34 #include "nsHtml5TreeBuilder.h"
35 #include "nsHtml5TreeOpExecutor.h"
36 #include "nsIContentSecurityPolicy.h"
37 #include "nsIDocShell.h"
38 #include "nsIDocShellTreeItem.h"
39 #include "nsINestedURI.h"
40 #include "nsIHttpChannel.h"
41 #include "nsIScriptContext.h"
42 #include "nsIScriptError.h"
43 #include "nsIScriptGlobalObject.h"
44 #include "nsIViewSourceChannel.h"
45 #include "nsNetUtil.h"
46 #include "xpcpublic.h"
48 using namespace mozilla
;
50 static LazyLogModule
gCharsetMenuLog("Chardetng");
52 #define LOGCHARDETNG(args) MOZ_LOG(gCharsetMenuLog, LogLevel::Debug, args)
54 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(nsHtml5TreeOpExecutor
,
55 nsHtml5DocumentBuilder
,
58 class nsHtml5ExecutorReflusher
: public Runnable
{
60 RefPtr
<nsHtml5TreeOpExecutor
> mExecutor
;
63 explicit nsHtml5ExecutorReflusher(nsHtml5TreeOpExecutor
* aExecutor
)
64 : Runnable("nsHtml5ExecutorReflusher"), mExecutor(aExecutor
) {}
65 NS_IMETHOD
Run() override
{
66 dom::Document
* doc
= mExecutor
->GetDocument();
67 if (XRE_IsContentProcess() &&
69 HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
71 // Possible early paint pending, reuse the runnable and try to
72 // call RunFlushLoop later.
73 nsCOMPtr
<nsIRunnable
> flusher
= this;
75 doc
->Dispatch(TaskCategory::Network
, flusher
.forget()))) {
76 PROFILER_MARKER_UNTYPED("HighPrio blocking parser flushing(2)", DOM
);
80 mExecutor
->RunFlushLoop();
85 class MOZ_RAII nsHtml5AutoFlush final
{
87 RefPtr
<nsHtml5TreeOpExecutor
> mExecutor
;
91 explicit nsHtml5AutoFlush(nsHtml5TreeOpExecutor
* aExecutor
)
92 : mExecutor(aExecutor
), mOpsToRemove(aExecutor
->OpQueueLength()) {
93 mExecutor
->BeginFlush();
94 mExecutor
->BeginDocUpdate();
97 if (mExecutor
->IsInDocUpdate()) {
98 mExecutor
->EndDocUpdate();
100 // We aren't in an update if nsHtml5AutoPauseUpdate
101 // caused something to terminate the parser.
103 mExecutor
->IsComplete(),
104 "How do we have mParser but the doc update isn't open?");
106 mExecutor
->EndFlush();
107 mExecutor
->RemoveFromStartOfOpQueue(mOpsToRemove
);
109 void SetNumberOfOpsToRemove(size_t aOpsToRemove
) {
110 MOZ_ASSERT(aOpsToRemove
< mOpsToRemove
,
111 "Requested partial clearing of op queue but the number to clear "
112 "wasn't less than the length of the queue.");
113 mOpsToRemove
= aOpsToRemove
;
117 static LinkedList
<nsHtml5TreeOpExecutor
>* gBackgroundFlushList
= nullptr;
118 StaticRefPtr
<IdleTaskRunner
> gBackgroundFlushRunner
;
120 nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
121 : nsHtml5DocumentBuilder(false),
123 mReadingFromStage(false),
124 mStreamParser(nullptr),
125 mPreloadedURLs(23), // Mean # of preloadable resources per page on dmoz
127 mRunFlushLoopOnStack(false),
128 mCallContinueInterruptedParsingIfEnabled(false),
129 mAlreadyComplainedAboutCharset(false),
130 mAlreadyComplainedAboutDeepTree(false) {}
132 nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor() {
133 if (gBackgroundFlushList
&& isInList()) {
135 removeFrom(*gBackgroundFlushList
);
136 if (gBackgroundFlushList
->isEmpty()) {
137 delete gBackgroundFlushList
;
138 gBackgroundFlushList
= nullptr;
139 if (gBackgroundFlushRunner
) {
140 gBackgroundFlushRunner
->Cancel();
141 gBackgroundFlushRunner
= nullptr;
145 MOZ_ASSERT(NS_FAILED(mBroken
) || mOpQueue
.IsEmpty(),
146 "Somehow there's stuff in the op queue.");
151 nsHtml5TreeOpExecutor::WillParse() {
152 MOZ_ASSERT_UNREACHABLE("No one should call this");
153 return NS_ERROR_NOT_IMPLEMENTED
;
156 nsresult
nsHtml5TreeOpExecutor::WillBuildModel() {
157 mDocument
->AddObserver(this);
158 WillBuildModelImpl();
159 GetDocument()->BeginLoad();
160 if (mDocShell
&& !GetDocument()->GetWindow() && !IsExternalViewSource()) {
161 // Not loading as data but script global object not ready
162 return MarkAsBroken(NS_ERROR_DOM_INVALID_STATE_ERR
);
167 // This is called when the tree construction has ended
169 nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated
) {
170 if (mRunsToCompletion
) {
174 MOZ_RELEASE_ASSERT(!IsInDocUpdate(),
175 "DidBuildModel from inside a doc update.");
177 RefPtr
<nsHtml5TreeOpExecutor
> pin(this);
178 auto queueClearer
= MakeScopeExit([&] {
179 if (aTerminated
&& (mFlushState
== eNotFlushing
)) {
180 ClearOpQueue(); // clear in order to be able to assert in destructor
184 // This comes from nsXMLContentSink and nsHTMLContentSink
185 // If this parser has been marked as broken, treat the end of parse as
186 // forced termination.
187 DidBuildModelImpl(aTerminated
|| NS_FAILED(IsBroken()));
189 bool destroying
= true;
191 mDocShell
->IsBeingDestroyed(&destroying
);
195 mDocument
->OnParsingCompleted();
197 if (!mLayoutStarted
) {
198 // We never saw the body, and layout never got started. Force
199 // layout *now*, to get an initial reflow.
201 // NOTE: only force the layout if we are NOT destroying the
202 // docshell. If we are destroying it, then starting layout will
203 // likely cause us to crash, or at best waste a lot of time as we
204 // are just going to tear it down anyway.
205 nsContentSink::StartLayout(false);
210 mDocument
->RemoveObserver(this);
212 // DidBuildModelImpl may cause mParser to be nulled out
213 // Return early to avoid unblocking the onload event too many times.
217 // We may not have called BeginLoad() if loading is terminated before
218 // OnStartRequest call.
220 mDocument
->EndLoad();
222 // Gather telemetry only for top-level content navigations in order to
223 // avoid noise from ad iframes.
224 bool topLevel
= false;
225 if (mozilla::dom::BrowsingContext
* bc
= mDocument
->GetBrowsingContext()) {
226 topLevel
= bc
->IsTopContent();
229 // Gather telemetry only for text/html and text/plain (excluding CSS, JS,
230 // etc. being viewed as text.)
231 nsAutoString contentType
;
232 mDocument
->GetContentType(contentType
);
233 bool htmlOrPlain
= contentType
.EqualsLiteral(u
"text/html") ||
234 contentType
.EqualsLiteral(u
"text/plain");
236 // Gather telemetry only for HTTP status code 200 in order to exclude
239 nsCOMPtr
<nsIChannel
> channel
;
240 nsresult rv
= GetParser()->GetChannel(getter_AddRefs(channel
));
241 if (NS_SUCCEEDED(rv
) && channel
) {
242 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(channel
);
245 rv
= httpChannel
->GetResponseStatus(&httpStatus
);
246 if (NS_SUCCEEDED(rv
) && httpStatus
== 200) {
252 // Gather chardetng telemetry
253 MOZ_ASSERT(mDocument
->IsHTMLDocument());
254 if (httpOk
&& htmlOrPlain
&& topLevel
&& !aTerminated
&&
255 !mDocument
->AsHTMLDocument()->IsViewSource()) {
256 // We deliberately measure only normally-completed (non-aborted) loads
257 // that are not View Source loads. This seems like a better place for
258 // checking normal completion than anything in nsHtml5StreamParser.
259 bool plain
= mDocument
->AsHTMLDocument()->IsPlainText();
260 int32_t charsetSource
= mDocument
->GetDocumentCharacterSetSource();
261 switch (charsetSource
) {
262 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8
:
264 LOGCHARDETNG(("TEXT::UtfInitial"));
265 Telemetry::AccumulateCategorical(
266 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::UtfInitial
);
268 LOGCHARDETNG(("HTML::UtfInitial"));
269 Telemetry::AccumulateCategorical(
270 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::UtfInitial
);
273 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic
:
275 LOGCHARDETNG(("TEXT::GenericInitial"));
276 Telemetry::AccumulateCategorical(
277 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::
280 LOGCHARDETNG(("HTML::GenericInitial"));
281 Telemetry::AccumulateCategorical(
282 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::
286 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content
:
288 LOGCHARDETNG(("TEXT::ContentInitial"));
289 Telemetry::AccumulateCategorical(
290 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::
293 LOGCHARDETNG(("HTML::ContentInitial"));
294 Telemetry::AccumulateCategorical(
295 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::
299 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD
:
301 LOGCHARDETNG(("TEXT::TldInitial"));
302 Telemetry::AccumulateCategorical(
303 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::TldInitial
);
305 LOGCHARDETNG(("HTML::TldInitial"));
306 Telemetry::AccumulateCategorical(
307 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::TldInitial
);
310 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII
:
312 LOGCHARDETNG(("TEXT::UtfFinal"));
313 Telemetry::AccumulateCategorical(
314 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::UtfFinal
);
316 LOGCHARDETNG(("HTML::UtfFinal"));
317 Telemetry::AccumulateCategorical(
318 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::UtfFinal
);
321 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic
:
323 LOGCHARDETNG(("TEXT::GenericFinal"));
324 Telemetry::AccumulateCategorical(
325 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::
328 LOGCHARDETNG(("HTML::GenericFinal"));
329 Telemetry::AccumulateCategorical(
330 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::
334 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII
:
336 LOGCHARDETNG(("TEXT::GenericFinalA"));
337 Telemetry::AccumulateCategorical(
338 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::
341 LOGCHARDETNG(("HTML::GenericFinalA"));
342 Telemetry::AccumulateCategorical(
343 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::
347 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content
:
349 LOGCHARDETNG(("TEXT::ContentFinal"));
350 Telemetry::AccumulateCategorical(
351 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::
354 LOGCHARDETNG(("HTML::ContentFinal"));
355 Telemetry::AccumulateCategorical(
356 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::
360 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII
:
362 LOGCHARDETNG(("TEXT::ContentFinalA"));
363 Telemetry::AccumulateCategorical(
364 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::
367 LOGCHARDETNG(("HTML::ContentFinalA"));
368 Telemetry::AccumulateCategorical(
369 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::
373 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD
:
375 LOGCHARDETNG(("TEXT::TldFinal"));
376 Telemetry::AccumulateCategorical(
377 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::TldFinal
);
379 LOGCHARDETNG(("HTML::TldFinal"));
380 Telemetry::AccumulateCategorical(
381 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::TldFinal
);
384 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII
:
386 LOGCHARDETNG(("TEXT::TldFinalA"));
387 Telemetry::AccumulateCategorical(
388 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_TEXT::TldFinalA
);
390 LOGCHARDETNG(("HTML::TldFinalA"));
391 Telemetry::AccumulateCategorical(
392 Telemetry::LABELS_ENCODING_DETECTION_OUTCOME_HTML::TldFinalA
);
396 // Chardetng didn't run automatically or the input was all ASCII.
402 // Dropping the stream parser changes the parser's apparent
403 // script-createdness, which is why the stream parser must not be dropped
404 // before this executor's nsHtml5Parser has been made unreachable from its
405 // nsHTMLDocument. (mDocument->EndLoad() above drops the parser from the
407 GetParser()->DropStreamParser();
408 DropParserAndPerfHint();
409 #ifdef GATHER_DOCWRITE_STATISTICS
410 printf("UNSAFE SCRIPTS: %d\n", sUnsafeDocWrites
);
411 printf("TOKENIZER-SAFE SCRIPTS: %d\n", sTokenSafeDocWrites
);
412 printf("TREEBUILDER-SAFE SCRIPTS: %d\n", sTreeSafeDocWrites
);
414 #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
415 printf("MAX NOTIFICATION BATCH LEN: %d\n", sAppendBatchMaxSize
);
416 if (sAppendBatchExaminations
!= 0) {
417 printf("AVERAGE SLOTS EXAMINED: %d\n",
418 sAppendBatchSlotsExamined
/ sAppendBatchExaminations
);
425 nsHtml5TreeOpExecutor::WillInterrupt() {
426 MOZ_ASSERT_UNREACHABLE("Don't call. For interface compat only.");
427 return NS_ERROR_NOT_IMPLEMENTED
;
430 void nsHtml5TreeOpExecutor::WillResume() {
431 MOZ_ASSERT_UNREACHABLE("Don't call. For interface compat only.");
435 nsHtml5TreeOpExecutor::SetParser(nsParserBase
* aParser
) {
440 void nsHtml5TreeOpExecutor::InitialTranslationCompleted() {
441 nsContentSink::StartLayout(false);
444 void nsHtml5TreeOpExecutor::FlushPendingNotifications(FlushType aType
) {
445 if (aType
>= FlushType::EnsurePresShellInitAndFrames
) {
446 // Bug 577508 / 253951
447 nsContentSink::StartLayout(true);
451 nsISupports
* nsHtml5TreeOpExecutor::GetTarget() {
452 return ToSupports(mDocument
);
455 nsresult
nsHtml5TreeOpExecutor::MarkAsBroken(nsresult aReason
) {
456 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
459 mStreamParser
->Terminate();
461 // We are under memory pressure, but let's hope the following allocation
462 // works out so that we get to terminate and clean up the parser from
464 if (mParser
&& mDocument
) { // can mParser ever be null here?
465 nsCOMPtr
<nsIRunnable
> terminator
= NewRunnableMethod(
466 "nsHtml5Parser::Terminate", GetParser(), &nsHtml5Parser::Terminate
);
468 mDocument
->Dispatch(TaskCategory::Network
, terminator
.forget()))) {
469 NS_WARNING("failed to dispatch executor flush event");
475 static bool BackgroundFlushCallback(TimeStamp
/*aDeadline*/) {
476 RefPtr
<nsHtml5TreeOpExecutor
> ex
= gBackgroundFlushList
->popFirst();
480 if (gBackgroundFlushList
&& gBackgroundFlushList
->isEmpty()) {
481 delete gBackgroundFlushList
;
482 gBackgroundFlushList
= nullptr;
483 gBackgroundFlushRunner
->Cancel();
484 gBackgroundFlushRunner
= nullptr;
490 void nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync() {
491 if (mDocument
&& !mDocument
->IsInBackgroundWindow()) {
492 nsCOMPtr
<nsIRunnable
> flusher
= new nsHtml5ExecutorReflusher(this);
494 mDocument
->Dispatch(TaskCategory::Network
, flusher
.forget()))) {
495 NS_WARNING("failed to dispatch executor flush event");
498 if (!gBackgroundFlushList
) {
499 gBackgroundFlushList
= new LinkedList
<nsHtml5TreeOpExecutor
>();
502 gBackgroundFlushList
->insertBack(this);
504 if (gBackgroundFlushRunner
) {
507 // Now we set up a repetitive idle scheduler for flushing background list.
508 gBackgroundFlushRunner
= IdleTaskRunner::Create(
509 &BackgroundFlushCallback
,
510 "nsHtml5TreeOpExecutor::BackgroundFlushCallback",
511 0, // Start looking for idle time immediately.
512 TimeDuration::FromMilliseconds(250), // The hard deadline.
513 TimeDuration::FromMicroseconds(
514 StaticPrefs::content_sink_interactive_parse_time()), // Required
517 [] { return false; }); // MayStopProcessing
521 void nsHtml5TreeOpExecutor::FlushSpeculativeLoads() {
522 nsTArray
<nsHtml5SpeculativeLoad
> speculativeLoadQueue
;
523 mStage
.MoveSpeculativeLoadsTo(speculativeLoadQueue
);
524 nsHtml5SpeculativeLoad
* start
= speculativeLoadQueue
.Elements();
525 nsHtml5SpeculativeLoad
* end
= start
+ speculativeLoadQueue
.Length();
526 for (nsHtml5SpeculativeLoad
* iter
= start
; iter
< end
; ++iter
) {
527 if (MOZ_UNLIKELY(!mParser
)) {
528 // An extension terminated the parser from a HTTP observer.
535 class nsHtml5FlushLoopGuard
{
537 RefPtr
<nsHtml5TreeOpExecutor
> mExecutor
;
538 #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
542 explicit nsHtml5FlushLoopGuard(nsHtml5TreeOpExecutor
* aExecutor
)
543 : mExecutor(aExecutor
)
544 #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
546 mStartTime(PR_IntervalToMilliseconds(PR_IntervalNow()))
549 mExecutor
->mRunFlushLoopOnStack
= true;
551 ~nsHtml5FlushLoopGuard() {
552 #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
553 uint32_t timeOffTheEventLoop
=
554 PR_IntervalToMilliseconds(PR_IntervalNow()) - mStartTime
;
555 if (timeOffTheEventLoop
>
556 nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop
) {
557 nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop
= timeOffTheEventLoop
;
559 printf("Longest time off the event loop: %d\n",
560 nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop
);
563 mExecutor
->mRunFlushLoopOnStack
= false;
568 * The purpose of the loop here is to avoid returning to the main event loop
570 void nsHtml5TreeOpExecutor::RunFlushLoop() {
571 AUTO_PROFILER_LABEL("nsHtml5TreeOpExecutor::RunFlushLoop", OTHER
);
573 if (mRunFlushLoopOnStack
) {
574 // There's already a RunFlushLoop() on the call stack.
578 nsHtml5FlushLoopGuard
guard(this); // this is also the self-kungfu!
580 RefPtr
<nsParserBase
> parserKungFuDeathGrip(mParser
);
581 RefPtr
<nsHtml5StreamParser
> streamParserGrip
;
583 streamParserGrip
= GetParser()->GetStreamParser();
585 Unused
<< streamParserGrip
; // Intentionally not used within function
587 // Remember the entry time
588 (void)nsContentSink::WillParseImpl();
592 // Parse has terminated.
593 ClearOpQueue(); // clear in order to be able to assert in destructor
597 if (NS_FAILED(IsBroken())) {
601 if (!parserKungFuDeathGrip
->IsParserEnabled()) {
602 // The parser is blocked.
606 if (mFlushState
!= eNotFlushing
) {
607 // XXX Can this happen? In case it can, let's avoid crashing.
611 // If there are scripts executing, then the content sink is jumping the gun
612 // (probably due to a synchronous XMLHttpRequest) and will re-enable us
613 // later, see bug 460706.
614 if (IsScriptExecuting()) {
618 if (mReadingFromStage
) {
619 nsTArray
<nsHtml5SpeculativeLoad
> speculativeLoadQueue
;
620 MOZ_RELEASE_ASSERT(mFlushState
== eNotFlushing
,
621 "mOpQueue modified during flush.");
622 if (!mStage
.MoveOpsAndSpeculativeLoadsTo(mOpQueue
,
623 speculativeLoadQueue
)) {
624 MarkAsBroken(nsresult::NS_ERROR_OUT_OF_MEMORY
);
628 // Make sure speculative loads never start after the corresponding
629 // normal loads for the same URLs.
630 nsHtml5SpeculativeLoad
* start
= speculativeLoadQueue
.Elements();
631 nsHtml5SpeculativeLoad
* end
= start
+ speculativeLoadQueue
.Length();
632 for (nsHtml5SpeculativeLoad
* iter
= start
; iter
< end
; ++iter
) {
634 if (MOZ_UNLIKELY(!mParser
)) {
635 // An extension terminated the parser from a HTTP observer.
636 ClearOpQueue(); // clear in order to be able to assert in destructor
641 FlushSpeculativeLoads(); // Make sure speculative loads never start after
642 // the corresponding normal loads for the same
644 if (MOZ_UNLIKELY(!mParser
)) {
645 // An extension terminated the parser from a HTTP observer.
646 ClearOpQueue(); // clear in order to be able to assert in destructor
649 // Now parse content left in the document.write() buffer queue if any.
650 // This may generate tree ops on its own or dequeue a speculation.
651 nsresult rv
= GetParser()->ParseUntilBlocked();
653 // ParseUntilBlocked flushes operations from the stage to the OpQueue.
654 // Those operations may have accompanying speculative operations.
655 // If so, we have to flush those speculative loads so that we maintain
656 // the invariant that no speculative load starts after the corresponding
657 // normal load for the same URL. See
658 // https://bugzilla.mozilla.org/show_bug.cgi?id=1513292#c80
659 // for a more detailed explanation of why this is necessary.
660 FlushSpeculativeLoads();
668 if (mOpQueue
.IsEmpty()) {
669 // Avoid bothering the rest of the engine with a doc update if there's
674 nsIContent
* scriptElement
= nullptr;
675 bool interrupted
= false;
676 bool streamEnded
= false;
679 // autoFlush clears mOpQueue in its destructor unless
680 // SetNumberOfOpsToRemove is called first, in which case only
681 // some ops from the start of the queue are cleared.
682 nsHtml5AutoFlush
autoFlush(this);
684 nsHtml5TreeOperation
* first
= mOpQueue
.Elements();
685 nsHtml5TreeOperation
* last
= first
+ mOpQueue
.Length() - 1;
686 for (nsHtml5TreeOperation
* iter
= first
;; ++iter
) {
687 if (MOZ_UNLIKELY(!mParser
)) {
688 // The previous tree op caused a call to nsIParser::Terminate().
691 MOZ_ASSERT(IsInDocUpdate(),
692 "Tried to perform tree op outside update batch.");
694 iter
->Perform(this, &scriptElement
, &interrupted
, &streamEnded
);
700 // Be sure not to check the deadline if the last op was just performed.
701 if (MOZ_UNLIKELY(iter
== last
)) {
703 } else if (MOZ_UNLIKELY(interrupted
) ||
704 MOZ_UNLIKELY(nsContentSink::DidProcessATokenImpl() ==
705 NS_ERROR_HTMLPARSER_INTERRUPTED
)) {
706 autoFlush
.SetNumberOfOpsToRemove((iter
- first
) + 1);
708 nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
713 if (MOZ_UNLIKELY(!mParser
)) {
714 // The parse ended during an update pause.
718 GetParser()->PermanentlyUndefineInsertionPoint();
722 if (MOZ_UNLIKELY(!mParser
)) {
723 // Ending the doc update caused a call to nsIParser::Terminate().
728 DidBuildModel(false);
731 nsCOMPtr
<nsIScriptElement
> sele
= do_QueryInterface(scriptElement
);
733 MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled
,
734 "Node didn't QI to script, but SVG wasn't disabled.");
736 MOZ_ASSERT(sele
->IsMalformed(), "Script wasn't marked as malformed.");
739 } else if (scriptElement
) {
740 // must be tail call when mFlushState is eNotFlushing
741 RunScript(scriptElement
);
743 // Always check the clock in nsContentSink right after a script
745 if (nsContentSink::DidProcessATokenImpl() ==
746 NS_ERROR_HTMLPARSER_INTERRUPTED
) {
747 #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
748 printf("REFLUSH SCHEDULED (after script): %d\n",
749 ++sTimesFlushLoopInterrupted
);
751 nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
758 nsresult
nsHtml5TreeOpExecutor::FlushDocumentWrite() {
759 nsresult rv
= IsBroken();
760 NS_ENSURE_SUCCESS(rv
, rv
);
762 FlushSpeculativeLoads(); // Make sure speculative loads never start after the
763 // corresponding normal loads for the same URLs.
765 if (MOZ_UNLIKELY(!mParser
)) {
766 // The parse has ended.
767 ClearOpQueue(); // clear in order to be able to assert in destructor
771 if (mFlushState
!= eNotFlushing
) {
772 // XXX Can this happen? In case it can, let's avoid crashing.
776 // avoid crashing near EOF
777 RefPtr
<nsHtml5TreeOpExecutor
> kungFuDeathGrip(this);
778 RefPtr
<nsParserBase
> parserKungFuDeathGrip(mParser
);
779 Unused
<< parserKungFuDeathGrip
; // Intentionally not used within function
780 RefPtr
<nsHtml5StreamParser
> streamParserGrip
;
782 streamParserGrip
= GetParser()->GetStreamParser();
784 Unused
<< streamParserGrip
; // Intentionally not used within function
786 MOZ_RELEASE_ASSERT(!mReadingFromStage
,
787 "Got doc write flush when reading from stage");
790 mStage
.AssertEmpty();
793 nsIContent
* scriptElement
= nullptr;
794 bool interrupted
= false;
795 bool streamEnded
= false;
798 // autoFlush clears mOpQueue in its destructor.
799 nsHtml5AutoFlush
autoFlush(this);
801 nsHtml5TreeOperation
* start
= mOpQueue
.Elements();
802 nsHtml5TreeOperation
* end
= start
+ mOpQueue
.Length();
803 for (nsHtml5TreeOperation
* iter
= start
; iter
< end
; ++iter
) {
804 if (MOZ_UNLIKELY(!mParser
)) {
805 // The previous tree op caused a call to nsIParser::Terminate().
808 NS_ASSERTION(IsInDocUpdate(),
809 "Tried to perform tree op outside update batch.");
810 rv
= iter
->Perform(this, &scriptElement
, &interrupted
, &streamEnded
);
817 if (MOZ_UNLIKELY(!mParser
)) {
818 // The parse ended during an update pause.
822 // This should be redundant but let's do it just in case.
823 GetParser()->PermanentlyUndefineInsertionPoint();
827 if (MOZ_UNLIKELY(!mParser
)) {
828 // Ending the doc update caused a call to nsIParser::Terminate().
833 DidBuildModel(false);
836 nsCOMPtr
<nsIScriptElement
> sele
= do_QueryInterface(scriptElement
);
838 MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled
,
839 "Node didn't QI to script, but SVG wasn't disabled.");
841 MOZ_ASSERT(sele
->IsMalformed(), "Script wasn't marked as malformed.");
844 } else if (scriptElement
) {
845 // must be tail call when mFlushState is eNotFlushing
846 RunScript(scriptElement
);
851 void nsHtml5TreeOpExecutor::CommitToInternalEncoding() {
852 if (MOZ_UNLIKELY(!mParser
|| !mStreamParser
)) {
853 // An extension terminated the parser from a HTTP observer.
854 ClearOpQueue(); // clear in order to be able to assert in destructor
857 mStreamParser
->ContinueAfterScriptsOrEncodingCommitment(nullptr, nullptr,
861 [[nodiscard
]] bool nsHtml5TreeOpExecutor::TakeOpsFromStage() {
862 return mStage
.MoveOpsTo(mOpQueue
);
865 // copied from HTML content sink
866 bool nsHtml5TreeOpExecutor::IsScriptEnabled() {
867 // Note that if we have no document or no docshell or no global or whatnot we
868 // want to claim script _is_ enabled, so we don't parse the contents of
870 if (!mDocument
|| !mDocShell
) {
874 return mDocument
->IsScriptEnabled();
877 void nsHtml5TreeOpExecutor::StartLayout(bool* aInterrupted
) {
878 if (mLayoutStarted
|| !mDocument
) {
882 nsHtml5AutoPauseUpdate
autoPause(this);
884 if (MOZ_UNLIKELY(!mParser
)) {
889 nsContentSink::StartLayout(false);
892 *aInterrupted
= !GetParser()->IsParserEnabled();
896 void nsHtml5TreeOpExecutor::PauseDocUpdate(bool* aInterrupted
) {
897 // Pausing the document update allows JS to run, and potentially block
899 nsHtml5AutoPauseUpdate
autoPause(this);
901 if (MOZ_LIKELY(mParser
)) {
902 *aInterrupted
= !GetParser()->IsParserEnabled();
907 * The reason why this code is here and not in the tree builder even in the
908 * main-thread case is to allow the control to return from the tokenizer
909 * before scripts run. This way, the tokenizer is not invoked re-entrantly
910 * although the parser is.
912 * The reason why this is called as a tail call when mFlushState is set to
913 * eNotFlushing is to allow re-entry to Flush() but only after the current
914 * Flush() has cleared the op queue and is otherwise done cleaning up after
917 void nsHtml5TreeOpExecutor::RunScript(nsIContent
* aScriptElement
) {
918 if (mRunsToCompletion
) {
919 // We are in createContextualFragment() or in the upcoming document.parse().
920 // Do nothing. Let's not even mark scripts malformed here, because that
921 // could cause serialization weirdness later.
925 MOZ_ASSERT(mParser
, "Trying to run script with a terminated parser.");
926 MOZ_ASSERT(aScriptElement
, "No script to run");
927 nsCOMPtr
<nsIScriptElement
> sele
= do_QueryInterface(aScriptElement
);
929 MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled
,
930 "Node didn't QI to script, but SVG wasn't disabled.");
934 if (sele
->GetScriptDeferred() || sele
->GetScriptAsync()) {
935 DebugOnly
<bool> block
= sele
->AttemptToExecute();
936 NS_ASSERTION(!block
, "Defer or async script tried to block.");
940 MOZ_RELEASE_ASSERT(mFlushState
== eNotFlushing
,
941 "Tried to run script while flushing.");
943 mReadingFromStage
= false;
945 sele
->SetCreatorParser(GetParser());
947 // Copied from nsXMLContentSink
948 // Now tell the script that it's ready to go. This may execute the script
949 // or return true, or neither if the script doesn't need executing.
950 bool block
= sele
->AttemptToExecute();
952 // If the act of insertion evaluated the script, we're fine.
953 // Else, block the parser till the script has loaded.
956 GetParser()->BlockParser();
959 // mParser may have been nulled out by now, but the flusher deals
961 // If this event isn't needed, it doesn't do anything. It is sometimes
962 // necessary for the parse to continue after complex situations.
963 nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
967 void nsHtml5TreeOpExecutor::Start() {
968 MOZ_ASSERT(!mStarted
, "Tried to start when already started.");
972 void nsHtml5TreeOpExecutor::UpdateCharsetSource(
973 nsCharsetSource aCharsetSource
) {
975 mDocument
->SetDocumentCharacterSetSource(aCharsetSource
);
979 void nsHtml5TreeOpExecutor::SetDocumentCharsetAndSource(
980 NotNull
<const Encoding
*> aEncoding
, nsCharsetSource aCharsetSource
) {
982 mDocument
->SetDocumentCharacterSetSource(aCharsetSource
);
983 mDocument
->SetDocumentCharacterSet(aEncoding
);
987 void nsHtml5TreeOpExecutor::NeedsCharsetSwitchTo(
988 NotNull
<const Encoding
*> aEncoding
, int32_t aSource
, uint32_t aLineNumber
) {
989 nsHtml5AutoPauseUpdate
autoPause(this);
990 if (MOZ_UNLIKELY(!mParser
)) {
999 nsDocShell
* docShell
= static_cast<nsDocShell
*>(mDocShell
.get());
1001 if (NS_SUCCEEDED(docShell
->CharsetChangeStopDocumentLoad())) {
1002 docShell
->CharsetChangeReloadDocument(aEncoding
, aSource
);
1004 // if the charset switch was accepted, mDocShell has called Terminate() on the
1010 GetParser()->ContinueAfterFailedCharsetSwitch();
1013 void nsHtml5TreeOpExecutor::MaybeComplainAboutCharset(const char* aMsgId
,
1015 uint32_t aLineNumber
) {
1016 // Encoding errors don't count towards already complaining
1017 if (!(!strcmp(aMsgId
, "EncError") || !strcmp(aMsgId
, "EncErrorFrame") ||
1018 !strcmp(aMsgId
, "EncErrorFramePlain"))) {
1019 if (mAlreadyComplainedAboutCharset
) {
1022 mAlreadyComplainedAboutCharset
= true;
1024 nsContentUtils::ReportToConsole(
1025 aError
? nsIScriptError::errorFlag
: nsIScriptError::warningFlag
,
1026 "HTML parser"_ns
, mDocument
, nsContentUtils::eHTMLPARSER_PROPERTIES
,
1027 aMsgId
, nsTArray
<nsString
>(), nullptr, u
""_ns
, aLineNumber
);
1030 void nsHtml5TreeOpExecutor::ComplainAboutBogusProtocolCharset(
1031 Document
* aDoc
, bool aUnrecognized
) {
1032 NS_ASSERTION(!mAlreadyComplainedAboutCharset
,
1033 "How come we already managed to complain?");
1034 mAlreadyComplainedAboutCharset
= true;
1035 nsContentUtils::ReportToConsole(
1036 nsIScriptError::errorFlag
, "HTML parser"_ns
, aDoc
,
1037 nsContentUtils::eHTMLPARSER_PROPERTIES
,
1038 aUnrecognized
? "EncProtocolUnsupported" : "EncProtocolReplacement");
1041 void nsHtml5TreeOpExecutor::MaybeComplainAboutDeepTree(uint32_t aLineNumber
) {
1042 if (mAlreadyComplainedAboutDeepTree
) {
1045 mAlreadyComplainedAboutDeepTree
= true;
1046 nsContentUtils::ReportToConsole(
1047 nsIScriptError::errorFlag
, "HTML parser"_ns
, mDocument
,
1048 nsContentUtils::eHTMLPARSER_PROPERTIES
, "errDeepTree",
1049 nsTArray
<nsString
>(), nullptr, u
""_ns
, aLineNumber
);
1052 nsHtml5Parser
* nsHtml5TreeOpExecutor::GetParser() {
1053 MOZ_ASSERT(!mRunsToCompletion
);
1054 return static_cast<nsHtml5Parser
*>(mParser
.get());
1057 [[nodiscard
]] bool nsHtml5TreeOpExecutor::MoveOpsFrom(
1058 nsTArray
<nsHtml5TreeOperation
>& aOpQueue
) {
1059 MOZ_RELEASE_ASSERT(mFlushState
== eNotFlushing
,
1060 "Ops added to mOpQueue during tree op execution.");
1061 return !!mOpQueue
.AppendElements(std::move(aOpQueue
), mozilla::fallible_t());
1064 void nsHtml5TreeOpExecutor::ClearOpQueue() {
1065 MOZ_RELEASE_ASSERT(mFlushState
== eNotFlushing
,
1066 "mOpQueue cleared during tree op execution.");
1070 void nsHtml5TreeOpExecutor::RemoveFromStartOfOpQueue(
1071 size_t aNumberOfOpsToRemove
) {
1072 MOZ_RELEASE_ASSERT(mFlushState
== eNotFlushing
,
1073 "Ops removed from mOpQueue during tree op execution.");
1074 mOpQueue
.RemoveElementsAt(0, aNumberOfOpsToRemove
);
1077 void nsHtml5TreeOpExecutor::InitializeDocWriteParserState(
1078 nsAHtml5TreeBuilderState
* aState
, int32_t aLine
) {
1079 GetParser()->InitializeDocWriteParserState(aState
, aLine
);
1082 nsIURI
* nsHtml5TreeOpExecutor::GetViewSourceBaseURI() {
1083 if (!mViewSourceBaseURI
) {
1084 // We query the channel for the baseURI because in certain situations it
1085 // cannot otherwise be determined. If this process fails, fall back to the
1087 nsCOMPtr
<nsIViewSourceChannel
> vsc
=
1088 do_QueryInterface(mDocument
->GetChannel());
1090 nsresult rv
= vsc
->GetBaseURI(getter_AddRefs(mViewSourceBaseURI
));
1091 if (NS_SUCCEEDED(rv
) && mViewSourceBaseURI
) {
1092 return mViewSourceBaseURI
;
1096 nsCOMPtr
<nsIURI
> orig
= mDocument
->GetOriginalURI();
1097 if (orig
->SchemeIs("view-source")) {
1098 nsCOMPtr
<nsINestedURI
> nested
= do_QueryInterface(orig
);
1099 NS_ASSERTION(nested
, "URI with scheme view-source didn't QI to nested!");
1100 nested
->GetInnerURI(getter_AddRefs(mViewSourceBaseURI
));
1102 // Fail gracefully if the base URL isn't a view-source: URL.
1103 // Not sure if this can ever happen.
1104 mViewSourceBaseURI
= orig
;
1107 return mViewSourceBaseURI
;
1110 bool nsHtml5TreeOpExecutor::IsExternalViewSource() {
1111 if (!StaticPrefs::view_source_editor_external()) {
1115 return mDocumentURI
->SchemeIs("view-source");
1120 // Speculative loading
1122 nsIURI
* nsHtml5TreeOpExecutor::BaseURIForPreload() {
1123 // The URL of the document without <base>
1124 nsIURI
* documentURI
= mDocument
->GetDocumentURI();
1125 // The URL of the document with non-speculative <base>
1126 nsIURI
* documentBaseURI
= mDocument
->GetDocBaseURI();
1128 // If the two above are different, use documentBaseURI. If they are the same,
1129 // the document object isn't aware of a <base>, so attempt to use the
1130 // mSpeculationBaseURI or, failing, that, documentURI.
1131 return (documentURI
== documentBaseURI
)
1132 ? (mSpeculationBaseURI
? mSpeculationBaseURI
.get() : documentURI
)
1136 already_AddRefed
<nsIURI
>
1137 nsHtml5TreeOpExecutor::ConvertIfNotPreloadedYetAndMediaApplies(
1138 const nsAString
& aURL
, const nsAString
& aMedia
) {
1139 nsCOMPtr
<nsIURI
> uri
= ConvertIfNotPreloadedYet(aURL
);
1144 if (!MediaApplies(aMedia
)) {
1147 return uri
.forget();
1150 bool nsHtml5TreeOpExecutor::MediaApplies(const nsAString
& aMedia
) {
1151 using dom::MediaList
;
1153 if (aMedia
.IsEmpty()) {
1156 RefPtr
<MediaList
> media
= MediaList::Create(NS_ConvertUTF16toUTF8(aMedia
));
1157 return media
->Matches(*mDocument
);
1160 already_AddRefed
<nsIURI
> nsHtml5TreeOpExecutor::ConvertIfNotPreloadedYet(
1161 const nsAString
& aURL
) {
1162 if (aURL
.IsEmpty()) {
1166 nsIURI
* base
= BaseURIForPreload();
1167 auto encoding
= mDocument
->GetDocumentCharacterSet();
1168 nsCOMPtr
<nsIURI
> uri
;
1169 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aURL
, encoding
, base
);
1170 if (NS_FAILED(rv
)) {
1171 NS_WARNING("Failed to create a URI");
1175 if (ShouldPreloadURI(uri
)) {
1176 return uri
.forget();
1182 bool nsHtml5TreeOpExecutor::ShouldPreloadURI(nsIURI
* aURI
) {
1184 nsresult rv
= aURI
->GetSpec(spec
);
1185 NS_ENSURE_SUCCESS(rv
, false);
1186 return mPreloadedURLs
.EnsureInserted(spec
);
1189 dom::ReferrerPolicy
nsHtml5TreeOpExecutor::GetPreloadReferrerPolicy(
1190 const nsAString
& aReferrerPolicy
) {
1191 dom::ReferrerPolicy referrerPolicy
=
1192 dom::ReferrerInfo::ReferrerPolicyAttributeFromString(aReferrerPolicy
);
1193 return GetPreloadReferrerPolicy(referrerPolicy
);
1196 dom::ReferrerPolicy
nsHtml5TreeOpExecutor::GetPreloadReferrerPolicy(
1197 ReferrerPolicy aReferrerPolicy
) {
1198 if (aReferrerPolicy
!= dom::ReferrerPolicy::_empty
) {
1199 return aReferrerPolicy
;
1202 return mDocument
->GetPreloadReferrerInfo()->ReferrerPolicy();
1205 void nsHtml5TreeOpExecutor::PreloadScript(
1206 const nsAString
& aURL
, const nsAString
& aCharset
, const nsAString
& aType
,
1207 const nsAString
& aCrossOrigin
, const nsAString
& aMedia
,
1208 const nsAString
& aNonce
, const nsAString
& aIntegrity
,
1209 dom::ReferrerPolicy aReferrerPolicy
, bool aScriptFromHead
, bool aAsync
,
1210 bool aDefer
, bool aNoModule
, bool aLinkPreload
) {
1211 nsCOMPtr
<nsIURI
> uri
= ConvertIfNotPreloadedYetAndMediaApplies(aURL
, aMedia
);
1215 auto key
= PreloadHashKey::CreateAsScript(uri
, aCrossOrigin
, aType
);
1216 if (mDocument
->Preloads().PreloadExists(key
)) {
1219 mDocument
->ScriptLoader()->PreloadURI(
1220 uri
, aCharset
, aType
, aCrossOrigin
, aNonce
, aIntegrity
, aScriptFromHead
,
1221 aAsync
, aDefer
, aNoModule
, aLinkPreload
,
1222 GetPreloadReferrerPolicy(aReferrerPolicy
), 0);
1225 void nsHtml5TreeOpExecutor::PreloadStyle(
1226 const nsAString
& aURL
, const nsAString
& aCharset
,
1227 const nsAString
& aCrossOrigin
, const nsAString
& aMedia
,
1228 const nsAString
& aReferrerPolicy
, const nsAString
& aNonce
,
1229 const nsAString
& aIntegrity
, bool aLinkPreload
) {
1230 nsCOMPtr
<nsIURI
> uri
= ConvertIfNotPreloadedYetAndMediaApplies(aURL
, aMedia
);
1236 auto hashKey
= PreloadHashKey::CreateAsStyle(
1237 uri
, mDocument
->NodePrincipal(),
1238 dom::Element::StringToCORSMode(aCrossOrigin
),
1239 css::eAuthorSheetFeatures
);
1240 if (mDocument
->Preloads().PreloadExists(hashKey
)) {
1245 mDocument
->PreloadStyle(
1246 uri
, Encoding::ForLabel(aCharset
), aCrossOrigin
,
1247 GetPreloadReferrerPolicy(aReferrerPolicy
), aNonce
, aIntegrity
,
1248 aLinkPreload
? css::StylePreloadKind::FromLinkRelPreloadElement
1249 : css::StylePreloadKind::FromParser
,
1253 void nsHtml5TreeOpExecutor::PreloadImage(
1254 const nsAString
& aURL
, const nsAString
& aCrossOrigin
,
1255 const nsAString
& aMedia
, const nsAString
& aSrcset
, const nsAString
& aSizes
,
1256 const nsAString
& aImageReferrerPolicy
, bool aLinkPreload
,
1257 const TimeStamp
& aInitTimestamp
) {
1258 nsCOMPtr
<nsIURI
> baseURI
= BaseURIForPreload();
1259 bool isImgSet
= false;
1260 nsCOMPtr
<nsIURI
> uri
=
1261 mDocument
->ResolvePreloadImage(baseURI
, aURL
, aSrcset
, aSizes
, &isImgSet
);
1262 if (uri
&& ShouldPreloadURI(uri
) && MediaApplies(aMedia
)) {
1263 // use document wide referrer policy
1264 mDocument
->MaybePreLoadImage(uri
, aCrossOrigin
,
1265 GetPreloadReferrerPolicy(aImageReferrerPolicy
),
1266 isImgSet
, aLinkPreload
, aInitTimestamp
);
1270 // These calls inform the document of picture state and seen sources, such that
1271 // it can use them to inform ResolvePreLoadImage as necessary
1272 void nsHtml5TreeOpExecutor::PreloadPictureSource(const nsAString
& aSrcset
,
1273 const nsAString
& aSizes
,
1274 const nsAString
& aType
,
1275 const nsAString
& aMedia
) {
1276 mDocument
->PreloadPictureImageSource(aSrcset
, aSizes
, aType
, aMedia
);
1279 void nsHtml5TreeOpExecutor::PreloadFont(const nsAString
& aURL
,
1280 const nsAString
& aCrossOrigin
,
1281 const nsAString
& aMedia
,
1282 const nsAString
& aReferrerPolicy
) {
1283 nsCOMPtr
<nsIURI
> uri
= ConvertIfNotPreloadedYetAndMediaApplies(aURL
, aMedia
);
1288 mDocument
->Preloads().PreloadFont(uri
, aCrossOrigin
, aReferrerPolicy
, 0);
1291 void nsHtml5TreeOpExecutor::PreloadFetch(const nsAString
& aURL
,
1292 const nsAString
& aCrossOrigin
,
1293 const nsAString
& aMedia
,
1294 const nsAString
& aReferrerPolicy
) {
1295 nsCOMPtr
<nsIURI
> uri
= ConvertIfNotPreloadedYetAndMediaApplies(aURL
, aMedia
);
1300 mDocument
->Preloads().PreloadFetch(uri
, aCrossOrigin
, aReferrerPolicy
, 0);
1303 void nsHtml5TreeOpExecutor::PreloadOpenPicture() {
1304 mDocument
->PreloadPictureOpened();
1307 void nsHtml5TreeOpExecutor::PreloadEndPicture() {
1308 mDocument
->PreloadPictureClosed();
1311 void nsHtml5TreeOpExecutor::AddBase(const nsAString
& aURL
) {
1312 auto encoding
= mDocument
->GetDocumentCharacterSet();
1313 nsresult rv
= NS_NewURI(getter_AddRefs(mViewSourceBaseURI
), aURL
, encoding
,
1314 GetViewSourceBaseURI());
1315 if (NS_FAILED(rv
)) {
1316 mViewSourceBaseURI
= nullptr;
1319 void nsHtml5TreeOpExecutor::SetSpeculationBase(const nsAString
& aURL
) {
1320 if (mSpeculationBaseURI
) {
1321 // the first one wins
1325 auto encoding
= mDocument
->GetDocumentCharacterSet();
1326 nsCOMPtr
<nsIURI
> newBaseURI
;
1327 DebugOnly
<nsresult
> rv
= NS_NewURI(getter_AddRefs(newBaseURI
), aURL
, encoding
,
1328 mDocument
->GetDocumentURI());
1329 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Failed to create a URI");
1334 // Check the document's CSP usually delivered via the CSP header.
1335 if (nsCOMPtr
<nsIContentSecurityPolicy
> csp
= mDocument
->GetCsp()) {
1336 // base-uri should not fallback to the default-src and preloads should not
1337 // trigger violation reports.
1338 bool cspPermitsBaseURI
= true;
1339 nsresult rv
= csp
->Permits(
1340 nullptr, nullptr, newBaseURI
,
1341 nsIContentSecurityPolicy::BASE_URI_DIRECTIVE
, true /* aSpecific */,
1342 false /* aSendViolationReports */, &cspPermitsBaseURI
);
1343 if (NS_FAILED(rv
) || !cspPermitsBaseURI
) {
1348 // Also check the CSP discovered from the <meta> tag during speculative
1350 if (nsCOMPtr
<nsIContentSecurityPolicy
> csp
= mDocument
->GetPreloadCsp()) {
1351 bool cspPermitsBaseURI
= true;
1352 nsresult rv
= csp
->Permits(
1353 nullptr, nullptr, newBaseURI
,
1354 nsIContentSecurityPolicy::BASE_URI_DIRECTIVE
, true /* aSpecific */,
1355 false /* aSendViolationReports */, &cspPermitsBaseURI
);
1356 if (NS_FAILED(rv
) || !cspPermitsBaseURI
) {
1361 mSpeculationBaseURI
= newBaseURI
;
1362 mDocument
->Preloads().SetSpeculationBase(mSpeculationBaseURI
);
1365 void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(
1366 const nsAString
& aMetaReferrer
) {
1367 mDocument
->UpdateReferrerInfoFromMeta(aMetaReferrer
, true);
1370 void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString
& aCSP
) {
1371 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1373 nsresult rv
= NS_OK
;
1374 nsCOMPtr
<nsIContentSecurityPolicy
> preloadCsp
= mDocument
->GetPreloadCsp();
1376 RefPtr
<nsCSPContext
> csp
= new nsCSPContext();
1377 csp
->SuppressParserLogMessages();
1379 rv
= preloadCsp
->SetRequestContextWithDocument(mDocument
);
1380 NS_ENSURE_SUCCESS_VOID(rv
);
1383 // Please note that multiple meta CSPs need to be joined together.
1384 rv
= preloadCsp
->AppendPolicy(
1386 false, // csp via meta tag can not be report only
1387 true); // delivered through the meta tag
1388 NS_ENSURE_SUCCESS_VOID(rv
);
1390 nsPIDOMWindowInner
* inner
= mDocument
->GetInnerWindow();
1392 inner
->SetPreloadCsp(preloadCsp
);
1394 mDocument
->ApplySettingsFromCSP(true);
1397 #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
1398 uint32_t nsHtml5TreeOpExecutor::sAppendBatchMaxSize
= 0;
1399 uint32_t nsHtml5TreeOpExecutor::sAppendBatchSlotsExamined
= 0;
1400 uint32_t nsHtml5TreeOpExecutor::sAppendBatchExaminations
= 0;
1401 uint32_t nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop
= 0;
1402 uint32_t nsHtml5TreeOpExecutor::sTimesFlushLoopInterrupted
= 0;