Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/parser/html/nsHtml5TreeOpExecutor.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set sw=2 ts=2 et tw=79: */
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
#include "mozilla/DebugOnly.h"
8
#include "mozilla/Likely.h"
9
#include "mozilla/dom/ScriptLoader.h"
10
#include "mozilla/dom/nsCSPService.h"
11
12
#include "GeckoProfiler.h"
13
#include "mozAutoDocUpdate.h"
14
#include "mozilla/IdleTaskRunner.h"
15
#include "mozilla/Preferences.h"
16
#include "mozilla/StaticPrefs.h"
17
#include "mozilla/css/Loader.h"
18
#include "nsContentUtils.h"
19
#include "nsDocShell.h"
20
#include "nsError.h"
21
#include "nsHtml5AutoPauseUpdate.h"
22
#include "nsHtml5Parser.h"
23
#include "nsHtml5StreamParser.h"
24
#include "nsHtml5Tokenizer.h"
25
#include "nsHtml5TreeBuilder.h"
26
#include "nsHtml5TreeOpExecutor.h"
27
#include "nsIContentSecurityPolicy.h"
28
#include "nsIContentViewer.h"
29
#include "nsIDocShell.h"
30
#include "nsIDocShellTreeItem.h"
31
#include "nsIHTMLDocument.h"
32
#include "nsINestedURI.h"
33
#include "nsIScriptContext.h"
34
#include "nsIScriptError.h"
35
#include "nsIScriptGlobalObject.h"
36
#include "nsIViewSourceChannel.h"
37
#include "nsNetUtil.h"
38
#include "xpcpublic.h"
39
40
using namespace mozilla;
41
42
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(nsHtml5TreeOpExecutor,
43
                                             nsHtml5DocumentBuilder,
44
                                             nsIContentSink)
45
46
class nsHtml5ExecutorReflusher : public Runnable
47
{
48
private:
49
  RefPtr<nsHtml5TreeOpExecutor> mExecutor;
50
51
public:
52
  explicit nsHtml5ExecutorReflusher(nsHtml5TreeOpExecutor* aExecutor)
53
    : mozilla::Runnable("nsHtml5ExecutorReflusher")
54
    , mExecutor(aExecutor)
55
0
  {
56
0
  }
57
  NS_IMETHOD Run() override
58
0
  {
59
0
    mExecutor->RunFlushLoop();
60
0
    return NS_OK;
61
0
  }
62
};
63
64
class MOZ_RAII nsHtml5AutoFlush final
65
{
66
private:
67
  RefPtr<nsHtml5TreeOpExecutor> mExecutor;
68
  size_t mOpsToRemove;
69
70
public:
71
  explicit nsHtml5AutoFlush(nsHtml5TreeOpExecutor* aExecutor)
72
    : mExecutor(aExecutor)
73
    , mOpsToRemove(aExecutor->OpQueueLength())
74
0
  {
75
0
    mExecutor->BeginFlush();
76
0
    mExecutor->BeginDocUpdate();
77
0
  }
78
  ~nsHtml5AutoFlush()
79
0
  {
80
0
    if (mExecutor->IsInDocUpdate()) {
81
0
      mExecutor->EndDocUpdate();
82
0
    } else {
83
0
      // We aren't in an update if nsHtml5AutoPauseUpdate
84
0
      // caused something to terminate the parser.
85
0
      MOZ_RELEASE_ASSERT(
86
0
        mExecutor->IsComplete(),
87
0
        "How do we have mParser but the doc update isn't open?");
88
0
    }
89
0
    mExecutor->EndFlush();
90
0
    mExecutor->RemoveFromStartOfOpQueue(mOpsToRemove);
91
0
  }
92
  void SetNumberOfOpsToRemove(size_t aOpsToRemove)
93
0
  {
94
0
    MOZ_ASSERT(aOpsToRemove < mOpsToRemove,
95
0
               "Requested partial clearing of op queue but the number to clear "
96
0
               "wasn't less than the length of the queue.");
97
0
    mOpsToRemove = aOpsToRemove;
98
0
  }
99
};
100
101
static mozilla::LinkedList<nsHtml5TreeOpExecutor>* gBackgroundFlushList =
102
  nullptr;
103
StaticRefPtr<IdleTaskRunner> gBackgroundFlushRunner;
104
105
nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
106
  : nsHtml5DocumentBuilder(false)
107
  , mSuppressEOF(false)
108
  , mReadingFromStage(false)
109
  , mStreamParser(nullptr)
110
  , mPreloadedURLs(23) // Mean # of preloadable resources per page on dmoz
111
  , mSpeculationReferrerPolicy(mozilla::net::RP_Unset)
112
  , mStarted(false)
113
  , mRunFlushLoopOnStack(false)
114
  , mCallContinueInterruptedParsingIfEnabled(false)
115
  , mAlreadyComplainedAboutCharset(false)
116
0
{
117
0
}
118
119
nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
120
0
{
121
0
  if (gBackgroundFlushList && isInList()) {
122
0
    ClearOpQueue();
123
0
    removeFrom(*gBackgroundFlushList);
124
0
    if (gBackgroundFlushList->isEmpty()) {
125
0
      delete gBackgroundFlushList;
126
0
      gBackgroundFlushList = nullptr;
127
0
      if (gBackgroundFlushRunner) {
128
0
        gBackgroundFlushRunner->Cancel();
129
0
        gBackgroundFlushRunner = nullptr;
130
0
      }
131
0
    }
132
0
  }
133
0
  NS_ASSERTION(mOpQueue.IsEmpty(), "Somehow there's stuff in the op queue.");
134
0
}
135
136
// nsIContentSink
137
NS_IMETHODIMP
138
nsHtml5TreeOpExecutor::WillParse()
139
0
{
140
0
  MOZ_ASSERT_UNREACHABLE("No one should call this");
141
0
  return NS_ERROR_NOT_IMPLEMENTED;
142
0
}
143
144
NS_IMETHODIMP
145
nsHtml5TreeOpExecutor::WillBuildModel(nsDTDMode aDTDMode)
146
0
{
147
0
  mDocument->AddObserver(this);
148
0
  WillBuildModelImpl();
149
0
  GetDocument()->BeginLoad();
150
0
  if (mDocShell && !GetDocument()->GetWindow() && !IsExternalViewSource()) {
151
0
    // Not loading as data but script global object not ready
152
0
    return MarkAsBroken(NS_ERROR_DOM_INVALID_STATE_ERR);
153
0
  }
154
0
  return NS_OK;
155
0
}
156
157
// This is called when the tree construction has ended
158
NS_IMETHODIMP
159
nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated)
160
0
{
161
0
  if (mRunsToCompletion) {
162
0
    return NS_OK;
163
0
  }
164
0
165
0
  MOZ_RELEASE_ASSERT(!IsInDocUpdate(),
166
0
                     "DidBuildModel from inside a doc update.");
167
0
168
0
  // This comes from nsXMLContentSink and nsHTMLContentSink
169
0
  // If this parser has been marked as broken, treat the end of parse as
170
0
  // forced termination.
171
0
  DidBuildModelImpl(aTerminated || NS_FAILED(IsBroken()));
172
0
173
0
  if (!mLayoutStarted) {
174
0
    // We never saw the body, and layout never got started. Force
175
0
    // layout *now*, to get an initial reflow.
176
0
177
0
    // NOTE: only force the layout if we are NOT destroying the
178
0
    // docshell. If we are destroying it, then starting layout will
179
0
    // likely cause us to crash, or at best waste a lot of time as we
180
0
    // are just going to tear it down anyway.
181
0
    bool destroying = true;
182
0
    if (mDocShell) {
183
0
      mDocShell->IsBeingDestroyed(&destroying);
184
0
    }
185
0
186
0
    if (!destroying) {
187
0
      nsContentSink::StartLayout(false);
188
0
    }
189
0
  }
190
0
191
0
  ScrollToRef();
192
0
  mDocument->RemoveObserver(this);
193
0
  if (!mParser) {
194
0
    // DidBuildModelImpl may cause mParser to be nulled out
195
0
    // Return early to avoid unblocking the onload event too many times.
196
0
    return NS_OK;
197
0
  }
198
0
199
0
  // We may not have called BeginLoad() if loading is terminated before
200
0
  // OnStartRequest call.
201
0
  if (mStarted) {
202
0
    mDocument->EndLoad();
203
0
  }
204
0
205
0
  GetParser()->DropStreamParser();
206
0
  DropParserAndPerfHint();
207
#ifdef GATHER_DOCWRITE_STATISTICS
208
  printf("UNSAFE SCRIPTS: %d\n", sUnsafeDocWrites);
209
  printf("TOKENIZER-SAFE SCRIPTS: %d\n", sTokenSafeDocWrites);
210
  printf("TREEBUILDER-SAFE SCRIPTS: %d\n", sTreeSafeDocWrites);
211
#endif
212
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
213
  printf("MAX NOTIFICATION BATCH LEN: %d\n", sAppendBatchMaxSize);
214
  if (sAppendBatchExaminations != 0) {
215
    printf("AVERAGE SLOTS EXAMINED: %d\n",
216
           sAppendBatchSlotsExamined / sAppendBatchExaminations);
217
  }
218
#endif
219
  return NS_OK;
220
0
}
221
222
NS_IMETHODIMP
223
nsHtml5TreeOpExecutor::WillInterrupt()
224
0
{
225
0
  MOZ_ASSERT_UNREACHABLE("Don't call. For interface compat only.");
226
0
  return NS_ERROR_NOT_IMPLEMENTED;
227
0
}
228
229
NS_IMETHODIMP
230
nsHtml5TreeOpExecutor::WillResume()
231
0
{
232
0
  MOZ_ASSERT_UNREACHABLE("Don't call. For interface compat only.");
233
0
  return NS_ERROR_NOT_IMPLEMENTED;
234
0
}
235
236
NS_IMETHODIMP
237
nsHtml5TreeOpExecutor::SetParser(nsParserBase* aParser)
238
0
{
239
0
  mParser = aParser;
240
0
  return NS_OK;
241
0
}
242
243
void
244
nsHtml5TreeOpExecutor::FlushPendingNotifications(FlushType aType)
245
0
{
246
0
  if (aType >= FlushType::EnsurePresShellInitAndFrames) {
247
0
    // Bug 577508 / 253951
248
0
    nsContentSink::StartLayout(true);
249
0
  }
250
0
}
251
252
nsISupports*
253
nsHtml5TreeOpExecutor::GetTarget()
254
0
{
255
0
  return mDocument;
256
0
}
257
258
nsresult
259
nsHtml5TreeOpExecutor::MarkAsBroken(nsresult aReason)
260
0
{
261
0
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
262
0
  mBroken = aReason;
263
0
  if (mStreamParser) {
264
0
    mStreamParser->Terminate();
265
0
  }
266
0
  // We are under memory pressure, but let's hope the following allocation
267
0
  // works out so that we get to terminate and clean up the parser from
268
0
  // a safer point.
269
0
  if (mParser && mDocument) { // can mParser ever be null here?
270
0
    nsCOMPtr<nsIRunnable> terminator = NewRunnableMethod(
271
0
      "nsHtml5Parser::Terminate", GetParser(), &nsHtml5Parser::Terminate);
272
0
    if (NS_FAILED(
273
0
          mDocument->Dispatch(TaskCategory::Network, terminator.forget()))) {
274
0
      NS_WARNING("failed to dispatch executor flush event");
275
0
    }
276
0
  }
277
0
  return aReason;
278
0
}
279
280
static bool BackgroundFlushCallback(TimeStamp /*aDeadline*/)
281
0
{
282
0
  RefPtr<nsHtml5TreeOpExecutor> ex = gBackgroundFlushList->popFirst();
283
0
  if (ex) {
284
0
    ex->RunFlushLoop();
285
0
  }
286
0
  if (gBackgroundFlushList && gBackgroundFlushList->isEmpty()) {
287
0
    delete gBackgroundFlushList;
288
0
    gBackgroundFlushList = nullptr;
289
0
    gBackgroundFlushRunner->Cancel();
290
0
    gBackgroundFlushRunner = nullptr;
291
0
    return true;
292
0
  }
293
0
  return true;
294
0
}
295
296
void
297
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync()
298
0
{
299
0
  if (!mDocument || !mDocument->IsInBackgroundWindow()) {
300
0
    nsCOMPtr<nsIRunnable> flusher = new nsHtml5ExecutorReflusher(this);
301
0
    if (NS_FAILED(
302
0
          mDocument->Dispatch(TaskCategory::Network, flusher.forget()))) {
303
0
      NS_WARNING("failed to dispatch executor flush event");
304
0
    }
305
0
  } else {
306
0
    if (!gBackgroundFlushList) {
307
0
      gBackgroundFlushList = new mozilla::LinkedList<nsHtml5TreeOpExecutor>();
308
0
    }
309
0
    if (!isInList()) {
310
0
      gBackgroundFlushList->insertBack(this);
311
0
    }
312
0
    if (gBackgroundFlushRunner) {
313
0
      return;
314
0
    }
315
0
    // Now we set up a repetitive idle scheduler for flushing background list.
316
0
    gBackgroundFlushRunner = IdleTaskRunner::Create(
317
0
      &BackgroundFlushCallback,
318
0
      "nsHtml5TreeOpExecutor::BackgroundFlushCallback",
319
0
      250,                                         // The hard deadline: 250ms.
320
0
      nsContentSink::sInteractiveParseTime / 1000, // Required budget.
321
0
      true,                                        // repeating
322
0
      [] { return false; });                       // MayStopProcessing
323
0
  }
324
0
}
325
326
void
327
nsHtml5TreeOpExecutor::FlushSpeculativeLoads()
328
0
{
329
0
  nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
330
0
  mStage.MoveSpeculativeLoadsTo(speculativeLoadQueue);
331
0
  nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
332
0
  nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
333
0
  for (nsHtml5SpeculativeLoad* iter = start; iter < end; ++iter) {
334
0
    if (MOZ_UNLIKELY(!mParser)) {
335
0
      // An extension terminated the parser from a HTTP observer.
336
0
      return;
337
0
    }
338
0
    iter->Perform(this);
339
0
  }
340
0
}
341
342
class nsHtml5FlushLoopGuard
343
{
344
private:
345
  RefPtr<nsHtml5TreeOpExecutor> mExecutor;
346
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
347
  uint32_t mStartTime;
348
#endif
349
public:
350
  explicit nsHtml5FlushLoopGuard(nsHtml5TreeOpExecutor* aExecutor)
351
    : mExecutor(aExecutor)
352
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
353
    , mStartTime(PR_IntervalToMilliseconds(PR_IntervalNow()))
354
#endif
355
0
  {
356
0
    mExecutor->mRunFlushLoopOnStack = true;
357
0
  }
358
  ~nsHtml5FlushLoopGuard()
359
0
  {
360
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
361
    uint32_t timeOffTheEventLoop =
362
      PR_IntervalToMilliseconds(PR_IntervalNow()) - mStartTime;
363
    if (timeOffTheEventLoop >
364
        nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop) {
365
      nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop = timeOffTheEventLoop;
366
    }
367
    printf("Longest time off the event loop: %d\n",
368
           nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop);
369
#endif
370
371
0
    mExecutor->mRunFlushLoopOnStack = false;
372
0
  }
373
};
374
375
/**
376
 * The purpose of the loop here is to avoid returning to the main event loop
377
 */
378
void
379
nsHtml5TreeOpExecutor::RunFlushLoop()
380
0
{
381
0
  AUTO_PROFILER_LABEL("nsHtml5TreeOpExecutor::RunFlushLoop", OTHER);
382
0
383
0
  if (mRunFlushLoopOnStack) {
384
0
    // There's already a RunFlushLoop() on the call stack.
385
0
    return;
386
0
  }
387
0
388
0
  nsHtml5FlushLoopGuard guard(this); // this is also the self-kungfu!
389
0
390
0
  RefPtr<nsParserBase> parserKungFuDeathGrip(mParser);
391
0
392
0
  // Remember the entry time
393
0
  (void)nsContentSink::WillParseImpl();
394
0
395
0
  for (;;) {
396
0
    if (!mParser) {
397
0
      // Parse has terminated.
398
0
      ClearOpQueue(); // clear in order to be able to assert in destructor
399
0
      return;
400
0
    }
401
0
402
0
    if (NS_FAILED(IsBroken())) {
403
0
      return;
404
0
    }
405
0
406
0
    if (!parserKungFuDeathGrip->IsParserEnabled()) {
407
0
      // The parser is blocked.
408
0
      return;
409
0
    }
410
0
411
0
    if (mFlushState != eNotFlushing) {
412
0
      // XXX Can this happen? In case it can, let's avoid crashing.
413
0
      return;
414
0
    }
415
0
416
0
    // If there are scripts executing, then the content sink is jumping the gun
417
0
    // (probably due to a synchronous XMLHttpRequest) and will re-enable us
418
0
    // later, see bug 460706.
419
0
    if (IsScriptExecuting()) {
420
0
      return;
421
0
    }
422
0
423
0
    if (mReadingFromStage) {
424
0
      nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
425
0
      MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
426
0
                         "mOpQueue modified during flush.");
427
0
      mStage.MoveOpsAndSpeculativeLoadsTo(mOpQueue, speculativeLoadQueue);
428
0
      // Make sure speculative loads never start after the corresponding
429
0
      // normal loads for the same URLs.
430
0
      nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
431
0
      nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
432
0
      for (nsHtml5SpeculativeLoad* iter = start; iter < end; ++iter) {
433
0
        iter->Perform(this);
434
0
        if (MOZ_UNLIKELY(!mParser)) {
435
0
          // An extension terminated the parser from a HTTP observer.
436
0
          ClearOpQueue(); // clear in order to be able to assert in destructor
437
0
          return;
438
0
        }
439
0
      }
440
0
    } else {
441
0
      FlushSpeculativeLoads(); // Make sure speculative loads never start after
442
0
                               // the corresponding normal loads for the same
443
0
                               // URLs.
444
0
      if (MOZ_UNLIKELY(!mParser)) {
445
0
        // An extension terminated the parser from a HTTP observer.
446
0
        ClearOpQueue(); // clear in order to be able to assert in destructor
447
0
        return;
448
0
      }
449
0
      // Not sure if this grip is still needed, but previously, the code
450
0
      // gripped before calling ParseUntilBlocked();
451
0
      RefPtr<nsHtml5StreamParser> streamKungFuDeathGrip =
452
0
        GetParser()->GetStreamParser();
453
0
      mozilla::Unused << streamKungFuDeathGrip; // Not used within function
454
0
      // Now parse content left in the document.write() buffer queue if any.
455
0
      // This may generate tree ops on its own or dequeue a speculation.
456
0
      nsresult rv = GetParser()->ParseUntilBlocked();
457
0
      if (NS_FAILED(rv)) {
458
0
        MarkAsBroken(rv);
459
0
        return;
460
0
      }
461
0
    }
462
0
463
0
    if (mOpQueue.IsEmpty()) {
464
0
      // Avoid bothering the rest of the engine with a doc update if there's
465
0
      // nothing to do.
466
0
      return;
467
0
    }
468
0
469
0
    nsIContent* scriptElement = nullptr;
470
0
    bool interrupted = false;
471
0
    bool streamEnded = false;
472
0
473
0
    {
474
0
      // autoFlush clears mOpQueue in its destructor unless
475
0
      // SetNumberOfOpsToRemove is called first, in which case only
476
0
      // some ops from the start of the queue are cleared.
477
0
      nsHtml5AutoFlush autoFlush(this);
478
0
479
0
      nsHtml5TreeOperation* first = mOpQueue.Elements();
480
0
      nsHtml5TreeOperation* last = first + mOpQueue.Length() - 1;
481
0
      for (nsHtml5TreeOperation* iter = first;; ++iter) {
482
0
        if (MOZ_UNLIKELY(!mParser)) {
483
0
          // The previous tree op caused a call to nsIParser::Terminate().
484
0
          return;
485
0
        }
486
0
        MOZ_ASSERT(IsInDocUpdate(),
487
0
                   "Tried to perform tree op outside update batch.");
488
0
        nsresult rv =
489
0
          iter->Perform(this, &scriptElement, &interrupted, &streamEnded);
490
0
        if (NS_FAILED(rv)) {
491
0
          MarkAsBroken(rv);
492
0
          break;
493
0
        }
494
0
495
0
        // Be sure not to check the deadline if the last op was just performed.
496
0
        if (MOZ_UNLIKELY(iter == last)) {
497
0
          break;
498
0
        } else if (MOZ_UNLIKELY(interrupted) ||
499
0
                   MOZ_UNLIKELY(nsContentSink::DidProcessATokenImpl() ==
500
0
                                NS_ERROR_HTMLPARSER_INTERRUPTED)) {
501
0
502
0
          autoFlush.SetNumberOfOpsToRemove((iter - first) + 1);
503
0
504
0
          nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
505
0
          return;
506
0
        }
507
0
      }
508
0
509
0
      if (MOZ_UNLIKELY(!mParser)) {
510
0
        // The parse ended during an update pause.
511
0
        return;
512
0
      }
513
0
      if (streamEnded) {
514
0
        GetParser()->PermanentlyUndefineInsertionPoint();
515
0
      }
516
0
    } // end autoFlush
517
0
518
0
    if (MOZ_UNLIKELY(!mParser)) {
519
0
      // Ending the doc update caused a call to nsIParser::Terminate().
520
0
      return;
521
0
    }
522
0
523
0
    if (streamEnded) {
524
0
      DidBuildModel(false);
525
#ifdef DEBUG
526
      if (scriptElement) {
527
        nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(scriptElement);
528
        if (!sele) {
529
          MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,
530
                     "Node didn't QI to script, but SVG wasn't disabled.");
531
        }
532
        MOZ_ASSERT(sele->IsMalformed(), "Script wasn't marked as malformed.");
533
      }
534
#endif
535
0
    } else if (scriptElement) {
536
0
      // must be tail call when mFlushState is eNotFlushing
537
0
      RunScript(scriptElement);
538
0
539
0
      // Always check the clock in nsContentSink right after a script
540
0
      StopDeflecting();
541
0
      if (nsContentSink::DidProcessATokenImpl() ==
542
0
          NS_ERROR_HTMLPARSER_INTERRUPTED) {
543
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
544
        printf("REFLUSH SCHEDULED (after script): %d\n",
545
               ++sTimesFlushLoopInterrupted);
546
#endif
547
        nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
548
0
        return;
549
0
      }
550
0
    }
551
0
  }
552
0
}
553
554
nsresult
555
nsHtml5TreeOpExecutor::FlushDocumentWrite()
556
0
{
557
0
  nsresult rv = IsBroken();
558
0
  NS_ENSURE_SUCCESS(rv, rv);
559
0
560
0
  FlushSpeculativeLoads(); // Make sure speculative loads never start after the
561
0
                           // corresponding normal loads for the same URLs.
562
0
563
0
  if (MOZ_UNLIKELY(!mParser)) {
564
0
    // The parse has ended.
565
0
    ClearOpQueue(); // clear in order to be able to assert in destructor
566
0
    return rv;
567
0
  }
568
0
569
0
  if (mFlushState != eNotFlushing) {
570
0
    // XXX Can this happen? In case it can, let's avoid crashing.
571
0
    return rv;
572
0
  }
573
0
574
0
  // avoid crashing near EOF
575
0
  RefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this);
576
0
  RefPtr<nsParserBase> parserKungFuDeathGrip(mParser);
577
0
  mozilla::Unused
578
0
    << parserKungFuDeathGrip; // Intentionally not used within function
579
0
580
0
  MOZ_RELEASE_ASSERT(!mReadingFromStage,
581
0
                     "Got doc write flush when reading from stage");
582
0
583
#ifdef DEBUG
584
  mStage.AssertEmpty();
585
#endif
586
587
0
  nsIContent* scriptElement = nullptr;
588
0
  bool interrupted = false;
589
0
  bool streamEnded = false;
590
0
591
0
  {
592
0
    // autoFlush clears mOpQueue in its destructor.
593
0
    nsHtml5AutoFlush autoFlush(this);
594
0
595
0
    nsHtml5TreeOperation* start = mOpQueue.Elements();
596
0
    nsHtml5TreeOperation* end = start + mOpQueue.Length();
597
0
    for (nsHtml5TreeOperation* iter = start; iter < end; ++iter) {
598
0
      if (MOZ_UNLIKELY(!mParser)) {
599
0
        // The previous tree op caused a call to nsIParser::Terminate().
600
0
        return rv;
601
0
      }
602
0
      NS_ASSERTION(IsInDocUpdate(),
603
0
                   "Tried to perform tree op outside update batch.");
604
0
      rv = iter->Perform(this, &scriptElement, &interrupted, &streamEnded);
605
0
      if (NS_FAILED(rv)) {
606
0
        MarkAsBroken(rv);
607
0
        break;
608
0
      }
609
0
    }
610
0
611
0
    if (MOZ_UNLIKELY(!mParser)) {
612
0
      // The parse ended during an update pause.
613
0
      return rv;
614
0
    }
615
0
    if (streamEnded) {
616
0
      // This should be redundant but let's do it just in case.
617
0
      GetParser()->PermanentlyUndefineInsertionPoint();
618
0
    }
619
0
  } // autoFlush
620
0
621
0
  if (MOZ_UNLIKELY(!mParser)) {
622
0
    // Ending the doc update caused a call to nsIParser::Terminate().
623
0
    return rv;
624
0
  }
625
0
626
0
  if (streamEnded) {
627
0
    DidBuildModel(false);
628
#ifdef DEBUG
629
    if (scriptElement) {
630
      nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(scriptElement);
631
      if (!sele) {
632
        MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,
633
                   "Node didn't QI to script, but SVG wasn't disabled.");
634
      }
635
      MOZ_ASSERT(sele->IsMalformed(), "Script wasn't marked as malformed.");
636
    }
637
#endif
638
0
  } else if (scriptElement) {
639
0
    // must be tail call when mFlushState is eNotFlushing
640
0
    RunScript(scriptElement);
641
0
  }
642
0
  return rv;
643
0
}
644
645
// copied from HTML content sink
646
bool
647
nsHtml5TreeOpExecutor::IsScriptEnabled()
648
0
{
649
0
  // Note that if we have no document or no docshell or no global or whatnot we
650
0
  // want to claim script _is_ enabled, so we don't parse the contents of
651
0
  // <noscript> tags!
652
0
  if (!mDocument || !mDocShell) {
653
0
    return true;
654
0
  }
655
0
656
0
  return mDocument->IsScriptEnabled();
657
0
}
658
659
void
660
nsHtml5TreeOpExecutor::StartLayout(bool* aInterrupted)
661
0
{
662
0
  if (mLayoutStarted || !mDocument) {
663
0
    return;
664
0
  }
665
0
666
0
  nsHtml5AutoPauseUpdate autoPause(this);
667
0
668
0
  if (MOZ_UNLIKELY(!mParser)) {
669
0
    // got terminate
670
0
    return;
671
0
  }
672
0
673
0
  nsContentSink::StartLayout(false);
674
0
675
0
  if (mParser) {
676
0
    *aInterrupted = !GetParser()->IsParserEnabled();
677
0
  }
678
0
}
679
680
void
681
nsHtml5TreeOpExecutor::PauseDocUpdate(bool* aInterrupted)
682
0
{
683
0
  // Pausing the document update allows JS to run, and potentially block
684
0
  // further parsing.
685
0
  nsHtml5AutoPauseUpdate autoPause(this);
686
0
687
0
  if (MOZ_LIKELY(mParser)) {
688
0
    *aInterrupted = !GetParser()->IsParserEnabled();
689
0
  }
690
0
}
691
692
/**
693
 * The reason why this code is here and not in the tree builder even in the
694
 * main-thread case is to allow the control to return from the tokenizer
695
 * before scripts run. This way, the tokenizer is not invoked re-entrantly
696
 * although the parser is.
697
 *
698
 * The reason why this is called as a tail call when mFlushState is set to
699
 * eNotFlushing is to allow re-entry to Flush() but only after the current
700
 * Flush() has cleared the op queue and is otherwise done cleaning up after
701
 * itself.
702
 */
703
void
704
nsHtml5TreeOpExecutor::RunScript(nsIContent* aScriptElement)
705
0
{
706
0
  if (mRunsToCompletion) {
707
0
    // We are in createContextualFragment() or in the upcoming document.parse().
708
0
    // Do nothing. Let's not even mark scripts malformed here, because that
709
0
    // could cause serialization weirdness later.
710
0
    return;
711
0
  }
712
0
713
0
  MOZ_ASSERT(mParser, "Trying to run script with a terminated parser.");
714
0
  MOZ_ASSERT(aScriptElement, "No script to run");
715
0
  nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aScriptElement);
716
0
  if (!sele) {
717
0
    MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,
718
0
               "Node didn't QI to script, but SVG wasn't disabled.");
719
0
    return;
720
0
  }
721
0
722
0
  if (sele->GetScriptDeferred() || sele->GetScriptAsync()) {
723
0
    DebugOnly<bool> block = sele->AttemptToExecute();
724
0
    NS_ASSERTION(!block, "Defer or async script tried to block.");
725
0
    return;
726
0
  }
727
0
728
0
  MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
729
0
                     "Tried to run script while flushing.");
730
0
731
0
  mReadingFromStage = false;
732
0
733
0
  sele->SetCreatorParser(GetParser());
734
0
735
0
  // Copied from nsXMLContentSink
736
0
  // Now tell the script that it's ready to go. This may execute the script
737
0
  // or return true, or neither if the script doesn't need executing.
738
0
  bool block = sele->AttemptToExecute();
739
0
740
0
  // If the act of insertion evaluated the script, we're fine.
741
0
  // Else, block the parser till the script has loaded.
742
0
  if (block) {
743
0
    if (mParser) {
744
0
      GetParser()->BlockParser();
745
0
    }
746
0
  } else {
747
0
    // mParser may have been nulled out by now, but the flusher deals
748
0
749
0
    // If this event isn't needed, it doesn't do anything. It is sometimes
750
0
    // necessary for the parse to continue after complex situations.
751
0
    nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
752
0
  }
753
0
}
754
755
void
756
nsHtml5TreeOpExecutor::Start()
757
0
{
758
0
  MOZ_ASSERT(!mStarted, "Tried to start when already started.");
759
0
  mStarted = true;
760
0
}
761
762
void
763
nsHtml5TreeOpExecutor::NeedsCharsetSwitchTo(NotNull<const Encoding*> aEncoding,
764
                                            int32_t aSource,
765
                                            uint32_t aLineNumber)
766
0
{
767
0
  nsHtml5AutoPauseUpdate autoPause(this);
768
0
  if (MOZ_UNLIKELY(!mParser)) {
769
0
    // got terminate
770
0
    return;
771
0
  }
772
0
773
0
  if (!mDocShell) {
774
0
    return;
775
0
  }
776
0
777
0
  nsDocShell* docShell = static_cast<nsDocShell*>(mDocShell.get());
778
0
779
0
  if (NS_SUCCEEDED(docShell->CharsetChangeStopDocumentLoad())) {
780
0
    nsAutoCString charset;
781
0
    aEncoding->Name(charset);
782
0
    docShell->CharsetChangeReloadDocument(charset.get(), aSource);
783
0
  }
784
0
  // if the charset switch was accepted, mDocShell has called Terminate() on the
785
0
  // parser by now
786
0
787
0
  if (!mParser) {
788
0
    // success
789
0
    if (aSource == kCharsetFromMetaTag) {
790
0
      MaybeComplainAboutCharset("EncLateMetaReload", false, aLineNumber);
791
0
    }
792
0
    return;
793
0
  }
794
0
795
0
  if (aSource == kCharsetFromMetaTag) {
796
0
    MaybeComplainAboutCharset("EncLateMetaTooLate", true, aLineNumber);
797
0
  }
798
0
799
0
  GetParser()->ContinueAfterFailedCharsetSwitch();
800
0
}
801
802
void
803
nsHtml5TreeOpExecutor::MaybeComplainAboutCharset(const char* aMsgId,
804
                                                 bool aError,
805
                                                 uint32_t aLineNumber)
806
0
{
807
0
  if (mAlreadyComplainedAboutCharset) {
808
0
    return;
809
0
  }
810
0
  // The EncNoDeclaration case for advertising iframes is so common that it
811
0
  // would result is way too many errors. The iframe case doesn't matter
812
0
  // when the ad is an image or a Flash animation anyway. When the ad is
813
0
  // textual, a misrendered ad probably isn't a huge loss for users.
814
0
  // Let's suppress the message in this case.
815
0
  // This means that errors about other different-origin iframes in mashups
816
0
  // are lost as well, but generally, the site author isn't in control of
817
0
  // the embedded different-origin pages anyway and can't fix problems even
818
0
  // if alerted about them.
819
0
  if (!strcmp(aMsgId, "EncNoDeclaration") && mDocShell) {
820
0
    nsCOMPtr<nsIDocShellTreeItem> parent;
821
0
    mDocShell->GetSameTypeParent(getter_AddRefs(parent));
822
0
    if (parent) {
823
0
      return;
824
0
    }
825
0
  }
826
0
  mAlreadyComplainedAboutCharset = true;
827
0
  nsContentUtils::ReportToConsole(aError ? nsIScriptError::errorFlag
828
0
                                         : nsIScriptError::warningFlag,
829
0
                                  NS_LITERAL_CSTRING("HTML parser"),
830
0
                                  mDocument,
831
0
                                  nsContentUtils::eHTMLPARSER_PROPERTIES,
832
0
                                  aMsgId,
833
0
                                  nullptr,
834
0
                                  0,
835
0
                                  nullptr,
836
0
                                  EmptyString(),
837
0
                                  aLineNumber);
838
0
}
839
840
void
841
nsHtml5TreeOpExecutor::ComplainAboutBogusProtocolCharset(nsIDocument* aDoc)
842
0
{
843
0
  NS_ASSERTION(!mAlreadyComplainedAboutCharset,
844
0
               "How come we already managed to complain?");
845
0
  mAlreadyComplainedAboutCharset = true;
846
0
  nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
847
0
                                  NS_LITERAL_CSTRING("HTML parser"),
848
0
                                  aDoc,
849
0
                                  nsContentUtils::eHTMLPARSER_PROPERTIES,
850
0
                                  "EncProtocolUnsupported");
851
0
}
852
853
nsHtml5Parser*
854
nsHtml5TreeOpExecutor::GetParser()
855
0
{
856
0
  MOZ_ASSERT(!mRunsToCompletion);
857
0
  return static_cast<nsHtml5Parser*>(mParser.get());
858
0
}
859
860
void
861
nsHtml5TreeOpExecutor::MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue)
862
0
{
863
0
  MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
864
0
                     "Ops added to mOpQueue during tree op execution.");
865
0
  mOpQueue.AppendElements(std::move(aOpQueue));
866
0
}
867
868
void
869
nsHtml5TreeOpExecutor::ClearOpQueue()
870
0
{
871
0
  MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
872
0
                     "mOpQueue cleared during tree op execution.");
873
0
  mOpQueue.Clear();
874
0
}
875
876
void
877
nsHtml5TreeOpExecutor::RemoveFromStartOfOpQueue(size_t aNumberOfOpsToRemove)
878
0
{
879
0
  MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
880
0
                     "Ops removed from mOpQueue during tree op execution.");
881
0
  mOpQueue.RemoveElementsAt(0, aNumberOfOpsToRemove);
882
0
}
883
884
void
885
nsHtml5TreeOpExecutor::InitializeDocWriteParserState(
886
  nsAHtml5TreeBuilderState* aState,
887
  int32_t aLine)
888
0
{
889
0
  GetParser()->InitializeDocWriteParserState(aState, aLine);
890
0
}
891
892
nsIURI*
893
nsHtml5TreeOpExecutor::GetViewSourceBaseURI()
894
0
{
895
0
  if (!mViewSourceBaseURI) {
896
0
897
0
    // We query the channel for the baseURI because in certain situations it
898
0
    // cannot otherwise be determined. If this process fails, fall back to the
899
0
    // standard method.
900
0
    nsCOMPtr<nsIViewSourceChannel> vsc =
901
0
      do_QueryInterface(mDocument->GetChannel());
902
0
    if (vsc) {
903
0
      nsresult rv = vsc->GetBaseURI(getter_AddRefs(mViewSourceBaseURI));
904
0
      if (NS_SUCCEEDED(rv) && mViewSourceBaseURI) {
905
0
        return mViewSourceBaseURI;
906
0
      }
907
0
    }
908
0
909
0
    nsCOMPtr<nsIURI> orig = mDocument->GetOriginalURI();
910
0
    bool isViewSource;
911
0
    orig->SchemeIs("view-source", &isViewSource);
912
0
    if (isViewSource) {
913
0
      nsCOMPtr<nsINestedURI> nested = do_QueryInterface(orig);
914
0
      NS_ASSERTION(nested, "URI with scheme view-source didn't QI to nested!");
915
0
      nested->GetInnerURI(getter_AddRefs(mViewSourceBaseURI));
916
0
    } else {
917
0
      // Fail gracefully if the base URL isn't a view-source: URL.
918
0
      // Not sure if this can ever happen.
919
0
      mViewSourceBaseURI = orig;
920
0
    }
921
0
  }
922
0
  return mViewSourceBaseURI;
923
0
}
924
925
bool
926
nsHtml5TreeOpExecutor::IsExternalViewSource()
927
0
{
928
0
  if (!StaticPrefs::view_source_editor_external()) {
929
0
    return false;
930
0
  }
931
0
  bool isViewSource = false;
932
0
  if (mDocumentURI) {
933
0
    mDocumentURI->SchemeIs("view-source", &isViewSource);
934
0
  }
935
0
  return isViewSource;
936
0
}
937
938
// Speculative loading
939
940
nsIURI*
941
nsHtml5TreeOpExecutor::BaseURIForPreload()
942
0
{
943
0
  // The URL of the document without <base>
944
0
  nsIURI* documentURI = mDocument->GetDocumentURI();
945
0
  // The URL of the document with non-speculative <base>
946
0
  nsIURI* documentBaseURI = mDocument->GetDocBaseURI();
947
0
948
0
  // If the two above are different, use documentBaseURI. If they are the same,
949
0
  // the document object isn't aware of a <base>, so attempt to use the
950
0
  // mSpeculationBaseURI or, failing, that, documentURI.
951
0
  return (documentURI == documentBaseURI)
952
0
           ? (mSpeculationBaseURI ? mSpeculationBaseURI.get() : documentURI)
953
0
           : documentBaseURI;
954
0
}
955
956
already_AddRefed<nsIURI>
957
nsHtml5TreeOpExecutor::ConvertIfNotPreloadedYet(const nsAString& aURL)
958
0
{
959
0
  if (aURL.IsEmpty()) {
960
0
    return nullptr;
961
0
  }
962
0
963
0
  nsIURI* base = BaseURIForPreload();
964
0
  auto encoding = mDocument->GetDocumentCharacterSet();
965
0
  nsCOMPtr<nsIURI> uri;
966
0
  nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, encoding, base);
967
0
  if (NS_FAILED(rv)) {
968
0
    NS_WARNING("Failed to create a URI");
969
0
    return nullptr;
970
0
  }
971
0
972
0
  if (ShouldPreloadURI(uri)) {
973
0
    return uri.forget();
974
0
  }
975
0
976
0
  return nullptr;
977
0
}
978
979
bool
980
nsHtml5TreeOpExecutor::ShouldPreloadURI(nsIURI* aURI)
981
0
{
982
0
  nsAutoCString spec;
983
0
  nsresult rv = aURI->GetSpec(spec);
984
0
  NS_ENSURE_SUCCESS(rv, false);
985
0
  return mPreloadedURLs.EnsureInserted(spec);
986
0
}
987
988
void
989
nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL,
990
                                     const nsAString& aCharset,
991
                                     const nsAString& aType,
992
                                     const nsAString& aCrossOrigin,
993
                                     const nsAString& aIntegrity,
994
                                     bool aScriptFromHead,
995
                                     bool aAsync,
996
                                     bool aDefer,
997
                                     bool aNoModule)
998
0
{
999
0
  nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
1000
0
  if (!uri) {
1001
0
    return;
1002
0
  }
1003
0
  mDocument->ScriptLoader()->PreloadURI(uri,
1004
0
                                        aCharset,
1005
0
                                        aType,
1006
0
                                        aCrossOrigin,
1007
0
                                        aIntegrity,
1008
0
                                        aScriptFromHead,
1009
0
                                        aAsync,
1010
0
                                        aDefer,
1011
0
                                        aNoModule,
1012
0
                                        mSpeculationReferrerPolicy);
1013
0
}
1014
1015
void
1016
nsHtml5TreeOpExecutor::PreloadStyle(const nsAString& aURL,
1017
                                    const nsAString& aCharset,
1018
                                    const nsAString& aCrossOrigin,
1019
                                    const nsAString& aReferrerPolicy,
1020
                                    const nsAString& aIntegrity)
1021
0
{
1022
0
  nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
1023
0
  if (!uri) {
1024
0
    return;
1025
0
  }
1026
0
1027
0
  mozilla::net::ReferrerPolicy referrerPolicy = mSpeculationReferrerPolicy;
1028
0
  mozilla::net::ReferrerPolicy styleReferrerPolicy =
1029
0
    mozilla::net::AttributeReferrerPolicyFromString(aReferrerPolicy);
1030
0
  if (styleReferrerPolicy != mozilla::net::RP_Unset) {
1031
0
    referrerPolicy = styleReferrerPolicy;
1032
0
  }
1033
0
1034
0
  mDocument->PreloadStyle(uri,
1035
0
                          Encoding::ForLabel(aCharset),
1036
0
                          aCrossOrigin,
1037
0
                          referrerPolicy,
1038
0
                          aIntegrity);
1039
0
}
1040
1041
void
1042
nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL,
1043
                                    const nsAString& aCrossOrigin,
1044
                                    const nsAString& aSrcset,
1045
                                    const nsAString& aSizes,
1046
                                    const nsAString& aImageReferrerPolicy)
1047
0
{
1048
0
  nsCOMPtr<nsIURI> baseURI = BaseURIForPreload();
1049
0
  bool isImgSet = false;
1050
0
  nsCOMPtr<nsIURI> uri =
1051
0
    mDocument->ResolvePreloadImage(baseURI, aURL, aSrcset, aSizes, &isImgSet);
1052
0
  if (uri && ShouldPreloadURI(uri)) {
1053
0
    // use document wide referrer policy
1054
0
    mozilla::net::ReferrerPolicy referrerPolicy = mSpeculationReferrerPolicy;
1055
0
    mozilla::net::ReferrerPolicy imageReferrerPolicy =
1056
0
      mozilla::net::AttributeReferrerPolicyFromString(aImageReferrerPolicy);
1057
0
    if (imageReferrerPolicy != mozilla::net::RP_Unset) {
1058
0
      referrerPolicy = imageReferrerPolicy;
1059
0
    }
1060
0
1061
0
    mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy, isImgSet);
1062
0
  }
1063
0
}
1064
1065
// These calls inform the document of picture state and seen sources, such that
1066
// it can use them to inform ResolvePreLoadImage as necessary
1067
void
1068
nsHtml5TreeOpExecutor::PreloadPictureSource(const nsAString& aSrcset,
1069
                                            const nsAString& aSizes,
1070
                                            const nsAString& aType,
1071
                                            const nsAString& aMedia)
1072
0
{
1073
0
  mDocument->PreloadPictureImageSource(aSrcset, aSizes, aType, aMedia);
1074
0
}
1075
1076
void
1077
nsHtml5TreeOpExecutor::PreloadOpenPicture()
1078
0
{
1079
0
  mDocument->PreloadPictureOpened();
1080
0
}
1081
1082
void
1083
nsHtml5TreeOpExecutor::PreloadEndPicture()
1084
0
{
1085
0
  mDocument->PreloadPictureClosed();
1086
0
}
1087
1088
void
1089
nsHtml5TreeOpExecutor::AddBase(const nsAString& aURL)
1090
0
{
1091
0
  auto encoding = mDocument->GetDocumentCharacterSet();
1092
0
  nsresult rv = NS_NewURI(
1093
0
    getter_AddRefs(mViewSourceBaseURI), aURL, encoding, GetViewSourceBaseURI());
1094
0
  if (NS_FAILED(rv)) {
1095
0
    mViewSourceBaseURI = nullptr;
1096
0
  }
1097
0
}
1098
void
1099
nsHtml5TreeOpExecutor::SetSpeculationBase(const nsAString& aURL)
1100
0
{
1101
0
  if (mSpeculationBaseURI) {
1102
0
    // the first one wins
1103
0
    return;
1104
0
  }
1105
0
  auto encoding = mDocument->GetDocumentCharacterSet();
1106
0
  DebugOnly<nsresult> rv = NS_NewURI(getter_AddRefs(mSpeculationBaseURI),
1107
0
                                     aURL,
1108
0
                                     encoding,
1109
0
                                     mDocument->GetDocumentURI());
1110
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to create a URI");
1111
0
}
1112
1113
void
1114
nsHtml5TreeOpExecutor::SetSpeculationReferrerPolicy(
1115
  const nsAString& aReferrerPolicy)
1116
0
{
1117
0
  // Specs says:
1118
0
  // - Let value be the result of stripping leading and trailing whitespace from
1119
0
  // the value of element's content attribute.
1120
0
  // - If value is not the empty string, then:
1121
0
  if (aReferrerPolicy.IsEmpty()) {
1122
0
    return;
1123
0
  }
1124
0
1125
0
  ReferrerPolicy policy =
1126
0
    mozilla::net::ReferrerPolicyFromString(aReferrerPolicy);
1127
0
  // Specs says:
1128
0
  // - If policy is not the empty string, then set element's node document's
1129
0
  // referrer policy to policy
1130
0
  if (policy != mozilla::net::RP_Unset) {
1131
0
    SetSpeculationReferrerPolicy(policy);
1132
0
  }
1133
0
}
1134
1135
void
1136
nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP)
1137
0
{
1138
0
  if (!StaticPrefs::security_csp_enable()) {
1139
0
    return;
1140
0
  }
1141
0
1142
0
  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1143
0
1144
0
  nsIPrincipal* principal = mDocument->NodePrincipal();
1145
0
  nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
1146
0
  nsresult rv = principal->EnsurePreloadCSP(mDocument, getter_AddRefs(preloadCsp));
1147
0
  NS_ENSURE_SUCCESS_VOID(rv);
1148
0
1149
0
  // please note that meta CSPs and CSPs delivered through a header need
1150
0
  // to be joined together.
1151
0
  rv =
1152
0
    preloadCsp->AppendPolicy(aCSP,
1153
0
                             false, // csp via meta tag can not be report only
1154
0
                             true); // delivered through the meta tag
1155
0
  NS_ENSURE_SUCCESS_VOID(rv);
1156
0
1157
0
  mDocument->ApplySettingsFromCSP(true);
1158
0
}
1159
1160
void
1161
nsHtml5TreeOpExecutor::SetSpeculationReferrerPolicy(
1162
  ReferrerPolicy aReferrerPolicy)
1163
0
{
1164
0
  // Record "speculated" referrer policy locally and thread through the
1165
0
  // speculation phase.  The actual referrer policy will be set by
1166
0
  // HTMLMetaElement::BindToTree().
1167
0
  mSpeculationReferrerPolicy = aReferrerPolicy;
1168
0
}
1169
1170
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
1171
uint32_t nsHtml5TreeOpExecutor::sAppendBatchMaxSize = 0;
1172
uint32_t nsHtml5TreeOpExecutor::sAppendBatchSlotsExamined = 0;
1173
uint32_t nsHtml5TreeOpExecutor::sAppendBatchExaminations = 0;
1174
uint32_t nsHtml5TreeOpExecutor::sLongestTimeOffTheEventLoop = 0;
1175
uint32_t nsHtml5TreeOpExecutor::sTimesFlushLoopInterrupted = 0;
1176
#endif