Bug 1938290 - Add a continuation mechanism for the parsers returning early due to there being a current script. r=peterv

Differential Revision: https://phabricator.services.mozilla.com/D233329
This commit is contained in:
Henri Sivonen
2025-03-04 17:34:59 +00:00
parent 1fdd122166
commit a1732ccb1c
9 changed files with 59 additions and 14 deletions

View File

@@ -883,6 +883,12 @@ bool nsContentSink::IsScriptExecutingImpl() {
return !!mScriptLoader->GetCurrentScript();
}
void nsContentSink::ContinueParsingDocumentAfterCurrentScriptImpl() {
if (mScriptLoader) {
mScriptLoader->ContinueParsingDocumentAfterCurrentScript();
}
}
nsresult nsContentSink::WillParseImpl(void) {
if (mRunsToCompletion || !mDocument) {
return NS_OK;

View File

@@ -105,6 +105,7 @@ class nsContentSink : public nsICSSLoaderObserver,
void DidBuildModelImpl(bool aTerminated);
void DropParserAndPerfHint(void);
bool IsScriptExecutingImpl();
void ContinueParsingDocumentAfterCurrentScriptImpl();
void NotifyAppend(nsIContent* aContent, uint32_t aStartIndex);

View File

@@ -207,6 +207,7 @@ ScriptLoader::ScriptLoader(Document* aDocument)
mBlockingDOMContentLoaded(false),
mLoadEventFired(false),
mGiveUpEncoding(false),
mContinueParsingDocumentAfterCurrentScript(false),
mReporter(new ConsoleReportCollector()) {
LOG(("ScriptLoader::ScriptLoader %p", this));
@@ -2721,7 +2722,8 @@ nsresult ScriptLoader::EvaluateScriptElement(ScriptLoadRequest* aRequest) {
Document* ownerDoc =
aRequest->GetScriptLoadContext()->GetScriptOwnerDocument();
if (ownerDoc != mDocument) {
// Willful violation of HTML5 as of 2010-12-01
// https://html.spec.whatwg.org/#prepare-the-script-element step 16
// as of 2025-01-15
return NS_ERROR_FAILURE;
}
@@ -2749,6 +2751,23 @@ nsresult ScriptLoader::EvaluateScriptElement(ScriptLoadRequest* aRequest) {
globalObject = scriptGlobal;
}
// This mechanism is currently only used when the parser returns
// early due to this script loader having a current script. However,
// now that we have this, we could migrate continuing after a
// parser-blocking script to this same mechanism. Not doing it right
// away to reduce risk of introducing bugs.
auto maybeContinueParser = MakeScopeExit([&] {
if (mContinueParsingDocumentAfterCurrentScript) {
mContinueParsingDocumentAfterCurrentScript = false;
if (mDocument) {
nsCOMPtr<nsIParser> parser = mDocument->CreatorParserOrNull();
if (parser) {
parser->ContinueInterruptedParsingAsync();
}
}
}
});
// Update our current script.
// This must be destroyed after destroying nsAutoMicroTask, see:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1620505#c4

View File

@@ -224,6 +224,11 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
*/
nsIScriptElement* GetCurrentScript() { return mCurrentScript; }
void ContinueParsingDocumentAfterCurrentScript() {
MOZ_ASSERT(mCurrentScript);
mContinueParsingDocumentAfterCurrentScript = true;
}
nsIScriptElement* GetCurrentParserInsertedScript() {
return mCurrentParserInsertedScript;
}
@@ -817,6 +822,7 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
bool mBlockingDOMContentLoaded;
bool mLoadEventFired;
bool mGiveUpEncoding;
bool mContinueParsingDocumentAfterCurrentScript;
TimeDuration mMainThreadParseTime;

View File

@@ -792,8 +792,6 @@ void nsXMLContentSink::SetDocumentCharset(NotNull<const Encoding*> aEncoding) {
nsISupports* nsXMLContentSink::GetTarget() { return ToSupports(mDocument); }
bool nsXMLContentSink::IsScriptExecuting() { return IsScriptExecutingImpl(); }
nsresult nsXMLContentSink::FlushText(bool aReleaseTextNode) {
nsresult rv = NS_OK;

View File

@@ -69,7 +69,10 @@ class nsXMLContentSink : public nsContentSink,
virtual void FlushPendingNotifications(mozilla::FlushType aType) override;
virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding) override;
virtual nsISupports* GetTarget() override;
virtual bool IsScriptExecuting() override;
bool IsScriptExecuting() override { return IsScriptExecutingImpl(); }
void ContinueParsingDocumentAfterCurrentScript() override {
ContinueParsingDocumentAfterCurrentScriptImpl();
}
virtual void ContinueInterruptedParsingAsync() override;
bool IsPrettyPrintXML() const override { return mPrettyPrintXML; }
bool IsPrettyPrintHasSpecialRoot() const override {

View File

@@ -516,6 +516,8 @@ void nsHtml5TreeOpExecutor::RunFlushLoop() {
if (mRunFlushLoopOnStack) {
// There's already a RunFlushLoop() on the call stack.
// The instance of RunFlushLoop() already on the stack is
// expected to ensure that we don't stall.
return;
}
@@ -544,18 +546,26 @@ void nsHtml5TreeOpExecutor::RunFlushLoop() {
if (!parserKungFuDeathGrip->IsParserEnabled()) {
// The parser is blocked.
// Whatever blocked the parser is responsible for ensuring that
// we don't stall.
return;
}
if (mFlushState != eNotFlushing) {
// XXX Can this happen? In case it can, let's avoid crashing.
// It's not clear whether this case can ever happen. It's not
// supposed to, but in case this can happen, let's return
// early to avoid an even worse state. In case this happens,
// there in no obvious other mechanism to prevent stalling,
// so let's call `ContinueInterruptedParsingAsync()` to
// make sure we don't stall.
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
return;
}
// If there are scripts executing, then the content sink is jumping the gun
// (probably due to a synchronous XMLHttpRequest) and will re-enable us
// later, see bug 460706.
// If there are scripts executing, this is probably due to a synchronous
// XMLHttpRequest, see bug 460706 and 1938290.
if (IsScriptExecuting()) {
ContinueParsingDocumentAfterCurrentScript();
return;
}

View File

@@ -161,6 +161,10 @@ class nsHtml5TreeOpExecutor final
bool IsScriptExecuting() override { return IsScriptExecutingImpl(); }
void ContinueParsingDocumentAfterCurrentScript() override {
ContinueParsingDocumentAfterCurrentScriptImpl();
}
// Not from interface
void SetStreamParser(nsHtml5StreamParser* aStreamParser) {

View File

@@ -28,12 +28,8 @@ namespace mozilla {
class Encoding;
}
#define NS_ICONTENT_SINK_IID \
{ \
0xcf9a7cbb, 0xfcbc, 0x4e13, { \
0x8e, 0xf5, 0x18, 0xef, 0x2d, 0x3d, 0x58, 0x29 \
} \
}
#define NS_ICONTENT_SINK_IID \
{0xcf9a7cbb, 0xfcbc, 0x4e13, {0x8e, 0xf5, 0x18, 0xef, 0x2d, 0x3d, 0x58, 0x29}}
class nsIContentSink : public nsISupports {
protected:
@@ -130,6 +126,8 @@ class nsIContentSink : public nsISupports {
*/
virtual bool IsScriptExecuting() { return false; }
virtual void ContinueParsingDocumentAfterCurrentScript() {};
/**
* Posts a runnable that continues parsing.
*/