| Index: third_party/WebKit/Source/core/dom/ScriptLoader.cpp
|
| diff --git a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
|
| index a02e2ca2086a275cb4d1cf3ef5647e11e0236ce3..400c7167c3dabdd04366eb0519e13fb6ac939064 100644
|
| --- a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
|
| +++ b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
|
| @@ -24,6 +24,7 @@
|
|
|
| #include "core/dom/ScriptLoader.h"
|
|
|
| +#include "bindings/core/v8/CompiledScript.h"
|
| #include "bindings/core/v8/ScriptController.h"
|
| #include "bindings/core/v8/ScriptSourceCode.h"
|
| #include "core/HTMLNames.h"
|
| @@ -34,6 +35,7 @@
|
| #include "core/dom/ScriptLoaderClient.h"
|
| #include "core/dom/ScriptRunner.h"
|
| #include "core/dom/ScriptableDocumentParser.h"
|
| +#include "core/dom/TaskRunnerHelper.h"
|
| #include "core/dom/Text.h"
|
| #include "core/events/Event.h"
|
| #include "core/fetch/AccessControlStatus.h"
|
| @@ -53,6 +55,7 @@
|
| #include "platform/WebFrameScheduler.h"
|
| #include "platform/network/mime/MIMETypeRegistry.h"
|
| #include "platform/weborigin/SecurityOrigin.h"
|
| +#include "public/platform/Platform.h"
|
| #include "public/platform/WebCachePolicy.h"
|
| #include "wtf/StdLibExtras.h"
|
| #include "wtf/text/StringBuilder.h"
|
| @@ -430,32 +433,107 @@ void ScriptLoader::logScriptMIMEType(LocalFrame* frame,
|
|
|
| bool ScriptLoader::executeScript(const ScriptSourceCode& sourceCode) {
|
| double scriptExecStartTime = monotonicallyIncreasingTime();
|
| - bool result = doExecuteScript(sourceCode);
|
|
|
| - // NOTE: we do not check m_willBeParserExecuted here, since
|
| - // m_willBeParserExecuted is false for inline scripts, and we want to
|
| - // include inline script execution time as part of parser blocked script
|
| - // execution time.
|
| - if (m_asyncExecType == ScriptRunner::None)
|
| - DocumentParserTiming::from(m_element->document())
|
| - .recordParserBlockedOnScriptExecutionDuration(
|
| - monotonicallyIncreasingTime() - scriptExecStartTime,
|
| - wasCreatedDuringDocumentWrite());
|
| - return result;
|
| + CheckScriptResult result = checkScript(sourceCode);
|
| + if (result == CheckScriptResult::Proceed) {
|
| + AccessControlStatus accessControlStatus =
|
| + computeAccessControlStatus(sourceCode);
|
| + asCurrentScript(
|
| + [&sourceCode, accessControlStatus](ScriptController& scriptController) {
|
| + scriptController.executeScriptInMainWorld(sourceCode,
|
| + accessControlStatus);
|
| + });
|
| + }
|
| +
|
| + recordParserBlockedOnScriptExecutionSince(scriptExecStartTime);
|
| + return result != CheckScriptResult::Abort;
|
| +}
|
| +
|
| +void ScriptLoader::executeScriptAsync(
|
| + const ScriptSourceCode& sourceCode,
|
| + std::unique_ptr<ExecuteScriptAsyncCallback> completionCallback) {
|
| + DCHECK(completionCallback);
|
| + double scriptCompileStartTime = monotonicallyIncreasingTime();
|
| +
|
| + AccessControlStatus accessControlStatus =
|
| + computeAccessControlStatus(sourceCode);
|
| + Document* elementDocument = &(m_element->document());
|
| + Document* contextDocument = elementDocument->contextDocument();
|
| + LocalFrame* frame = contextDocument->frame();
|
| +
|
| + // TODO(jbroman): Could call checkScript here first.
|
| + // This would arguably be redundant, especially if we call it synchronously.
|
| + // On the other hand, it could also avoid compiling something that cannot
|
| + // execute.
|
| +
|
| + CompiledScript* compiled =
|
| + frame->script().compileScriptInMainWorld(sourceCode, accessControlStatus);
|
| + recordParserBlockedOnScriptExecutionSince(scriptCompileStartTime);
|
| +
|
| + if (!compiled) {
|
| + (*completionCallback)(true);
|
| + return;
|
| + }
|
| +
|
| + static const unsigned kSmallScriptSize = 30 * 1024;
|
| + const bool shouldYield = sourceCode.source().length() >= kSmallScriptSize &&
|
| + Platform::current()
|
| + ->mainThread()
|
| + ->scheduler()
|
| + ->shouldYieldForHighPriorityWork();
|
| + if (shouldYield) {
|
| + std::unique_ptr<WTF::Closure> continuation = WTF::bind(
|
| + &ScriptLoader::continueExecuteScriptAsync, wrapPersistent(this),
|
| + wrapPersistent(compiled), wrapPersistent(elementDocument),
|
| + WTF::passed(std::move(completionCallback)));
|
| + TaskRunnerHelper::get(TaskType::Networking, frame)
|
| + ->postTask(BLINK_FROM_HERE, std::move(continuation));
|
| + } else {
|
| + continueExecuteScriptAsync(compiled, elementDocument,
|
| + std::move(completionCallback));
|
| + }
|
| }
|
|
|
| -bool ScriptLoader::doExecuteScript(const ScriptSourceCode& sourceCode) {
|
| +void ScriptLoader::continueExecuteScriptAsync(
|
| + CompiledScript* compiled,
|
| + Document* elementDocumentWhenCompiled,
|
| + std::unique_ptr<ExecuteScriptAsyncCallback> completionCallback) {
|
| + double scriptExecStartTime = monotonicallyIncreasingTime();
|
| +
|
| + CheckScriptResult result = checkScript(compiled->sourceCode());
|
| + if (result == CheckScriptResult::Proceed) {
|
| + asCurrentScript([this, compiled, elementDocumentWhenCompiled](
|
| + ScriptController& scriptController) {
|
| + if (m_element->document() == elementDocumentWhenCompiled) {
|
| + scriptController.executeScriptInMainWorld(*compiled);
|
| + } else {
|
| + // This will recompile the source code. But it only happens when the
|
| + // element moves to a new document at an inopportune time, which
|
| + // should be rare.
|
| + scriptController.executeScriptInMainWorld(compiled->sourceCode());
|
| + }
|
| + });
|
| + }
|
| +
|
| + recordParserBlockedOnScriptExecutionSince(scriptExecStartTime);
|
| + (*completionCallback)(result != CheckScriptResult::Abort);
|
| +}
|
| +
|
| +ScriptLoader::CheckScriptResult ScriptLoader::checkScript(
|
| + const ScriptSourceCode& sourceCode) {
|
| DCHECK(m_alreadyStarted);
|
|
|
| if (sourceCode.isEmpty())
|
| - return true;
|
| + return CheckScriptResult::NothingToDo;
|
|
|
| Document* elementDocument = &(m_element->document());
|
| Document* contextDocument = elementDocument->contextDocument();
|
| if (!contextDocument)
|
| - return true;
|
| + return CheckScriptResult::NothingToDo;
|
|
|
| LocalFrame* frame = contextDocument->frame();
|
| + if (!frame)
|
| + return CheckScriptResult::NothingToDo;
|
|
|
| const ContentSecurityPolicy* csp = elementDocument->contentSecurityPolicy();
|
| bool shouldBypassMainWorldCSP =
|
| @@ -471,7 +549,7 @@ bool ScriptLoader::doExecuteScript(const ScriptSourceCode& sourceCode) {
|
| (!shouldBypassMainWorldCSP &&
|
| !csp->allowInlineScript(m_element, elementDocument->url(), nonce,
|
| m_startLineNumber, sourceCode.source()))) {
|
| - return false;
|
| + return CheckScriptResult::Abort;
|
| }
|
|
|
| if (m_isExternalScript) {
|
| @@ -486,7 +564,7 @@ bool ScriptLoader::doExecuteScript(const ScriptSourceCode& sourceCode) {
|
| resource->httpContentType() + "') is not executable, and "
|
| "strict MIME type checking is "
|
| "enabled."));
|
| - return false;
|
| + return CheckScriptResult::Abort;
|
| }
|
|
|
| String mimeType = resource->httpContentType();
|
| @@ -505,18 +583,18 @@ bool ScriptLoader::doExecuteScript(const ScriptSourceCode& sourceCode) {
|
| UseCounter::count(frame, UseCounter::BlockedSniffingVideoToScript);
|
| else if (mimeType == "text/csv")
|
| UseCounter::count(frame, UseCounter::BlockedSniffingCSVToScript);
|
| - return false;
|
| + return CheckScriptResult::Abort;
|
| }
|
|
|
| logScriptMIMEType(frame, resource, mimeType);
|
| }
|
| }
|
|
|
| - // FIXME: Can this be moved earlier in the function?
|
| - // Why are we ever attempting to execute scripts without a frame?
|
| - if (!frame)
|
| - return true;
|
| + return CheckScriptResult::Proceed;
|
| +}
|
|
|
| +AccessControlStatus ScriptLoader::computeAccessControlStatus(
|
| + const ScriptSourceCode& sourceCode) {
|
| AccessControlStatus accessControlStatus = NotSharableCrossOrigin;
|
| if (!m_isExternalScript) {
|
| accessControlStatus = SharableCrossOrigin;
|
| @@ -532,27 +610,45 @@ bool ScriptLoader::doExecuteScript(const ScriptSourceCode& sourceCode) {
|
| accessControlStatus = SharableCrossOrigin;
|
| }
|
| }
|
| + return accessControlStatus;
|
| +}
|
| +
|
| +template <typename Functor>
|
| +void ScriptLoader::asCurrentScript(const Functor& functor) {
|
| + Document* elementDocument = &m_element->document();
|
| + Document* contextDocument = elementDocument->contextDocument();
|
| + LocalFrame* frame = contextDocument->frame();
|
| + if (!frame)
|
| + return;
|
|
|
| - const bool isImportedScript = contextDocument != elementDocument;
|
| // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
|
| // step 2.3 with additional support for HTML imports.
|
| + const bool isImportedScript = contextDocument != elementDocument;
|
| IgnoreDestructiveWriteCountIncrementer ignoreDestructiveWriteCountIncrementer(
|
| m_isExternalScript || isImportedScript ? contextDocument : 0);
|
|
|
| if (isHTMLScriptLoader(m_element) || isSVGScriptLoader(m_element))
|
| contextDocument->pushCurrentScript(m_element);
|
|
|
| - // Create a script from the script element node, using the script
|
| - // block's source and the script block's type.
|
| - // Note: This is where the script is compiled and actually executed.
|
| - frame->script().executeScriptInMainWorld(sourceCode, accessControlStatus);
|
| + functor(frame->script());
|
|
|
| if (isHTMLScriptLoader(m_element) || isSVGScriptLoader(m_element)) {
|
| DCHECK(contextDocument->currentScript() == m_element);
|
| contextDocument->popCurrentScript();
|
| }
|
| +}
|
|
|
| - return true;
|
| +void ScriptLoader::recordParserBlockedOnScriptExecutionSince(double startTime) {
|
| + // NOTE: we do not check m_willBeParserExecuted here, since
|
| + // m_willBeParserExecuted is false for inline scripts, and we want to
|
| + // include inline script execution time as part of parser blocked script
|
| + // execution time.
|
| + if (m_asyncExecType == ScriptRunner::None) {
|
| + DocumentParserTiming::from(m_element->document())
|
| + .recordParserBlockedOnScriptExecutionDuration(
|
| + monotonicallyIncreasingTime() - startTime,
|
| + wasCreatedDuringDocumentWrite());
|
| + }
|
| }
|
|
|
| void ScriptLoader::execute() {
|
|
|