summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/ScriptElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/dom/ScriptElement.cpp')
-rw-r--r--Source/WebCore/dom/ScriptElement.cpp425
1 files changed, 251 insertions, 174 deletions
diff --git a/Source/WebCore/dom/ScriptElement.cpp b/Source/WebCore/dom/ScriptElement.cpp
index 651cc5806..cf51d40d5 100644
--- a/Source/WebCore/dom/ScriptElement.cpp
+++ b/Source/WebCore/dom/ScriptElement.cpp
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2017 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org>
*
* This library is free software; you can redistribute it and/or
@@ -24,43 +24,39 @@
#include "config.h"
#include "ScriptElement.h"
-#include "CachedScript.h"
#include "CachedResourceLoader.h"
#include "CachedResourceRequest.h"
+#include "CachedScript.h"
#include "ContentSecurityPolicy.h"
#include "CrossOriginAccessControl.h"
#include "CurrentScriptIncrementer.h"
#include "Event.h"
+#include "EventNames.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "HTMLNames.h"
#include "HTMLParserIdioms.h"
#include "IgnoreDestructiveWriteCountIncrementer.h"
+#include "InlineClassicScript.h"
+#include "LoadableClassicScript.h"
+#include "LoadableModuleScript.h"
#include "MIMETypeRegistry.h"
-#include "Page.h"
-#include "ScriptCallStack.h"
+#include "NoEventDispatchAssertion.h"
+#include "PendingScript.h"
+#include "SVGScriptElement.h"
#include "ScriptController.h"
#include "ScriptRunner.h"
#include "ScriptSourceCode.h"
#include "ScriptableDocumentParser.h"
-#include "SecurityOrigin.h"
-#include "Settings.h"
#include "TextNodeTraversal.h"
-#include <bindings/ScriptValue.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/StringHash.h>
-#if ENABLE(SVG)
-#include "SVGNames.h"
-#include "SVGScriptElement.h"
-#endif
-
namespace WebCore {
-ScriptElement::ScriptElement(Element* element, bool parserInserted, bool alreadyStarted)
+ScriptElement::ScriptElement(Element& element, bool parserInserted, bool alreadyStarted)
: m_element(element)
- , m_cachedScript(0)
, m_startLineNumber(WTF::OrdinalNumber::beforeFirst())
, m_parserInserted(parserInserted)
, m_isExternalScript(false)
@@ -71,38 +67,32 @@ ScriptElement::ScriptElement(Element* element, bool parserInserted, bool already
, m_willExecuteWhenDocumentFinishedParsing(false)
, m_forceAsync(!parserInserted)
, m_willExecuteInOrder(false)
- , m_requestUsesAccessControl(false)
+ , m_isModuleScript(false)
{
- ASSERT(m_element);
- if (parserInserted && m_element->document().scriptableDocumentParser() && !m_element->document().isInDocumentWrite())
- m_startLineNumber = m_element->document().scriptableDocumentParser()->textPosition().m_line;
+ if (parserInserted && m_element.document().scriptableDocumentParser() && !m_element.document().isInDocumentWrite())
+ m_startLineNumber = m_element.document().scriptableDocumentParser()->textPosition().m_line;
}
-ScriptElement::~ScriptElement()
+bool ScriptElement::shouldCallFinishedInsertingSubtree(ContainerNode& insertionPoint)
{
- stopLoadRequest();
+ return insertionPoint.isConnected() && !m_parserInserted;
}
-bool ScriptElement::shouldNotifySubtreeInsertions(ContainerNode& insertionPoint)
-{
- return insertionPoint.inDocument() && !m_parserInserted;
-}
-
-void ScriptElement::didNotifySubtreeInsertions(ContainerNode*)
+void ScriptElement::finishedInsertingSubtree()
{
ASSERT(!m_parserInserted);
prepareScript(); // FIXME: Provide a real starting line number here.
}
-void ScriptElement::childrenChanged()
+void ScriptElement::childrenChanged(const ContainerNode::ChildChange& childChange)
{
- if (!m_parserInserted && m_element->inDocument())
+ if (!m_parserInserted && childChange.isInsertion() && m_element.isConnected())
prepareScript(); // FIXME: Provide a real starting line number here.
}
-void ScriptElement::handleSourceAttribute(const String& sourceUrl)
+void ScriptElement::handleSourceAttribute(const String& sourceURL)
{
- if (ignoresLoadRequest() || sourceUrl.isEmpty())
+ if (ignoresLoadRequest() || sourceURL.isEmpty())
return;
prepareScript(); // FIXME: Provide a real starting line number here.
@@ -123,49 +113,65 @@ static bool isLegacySupportedJavaScriptLanguage(const String& language)
// We want to accept all the values that either of these browsers accept, but not other values.
// FIXME: This function is not HTML5 compliant. These belong in the MIME registry as "text/javascript<version>" entries.
- typedef HashSet<String, CaseFoldingHash> LanguageSet;
- DEFINE_STATIC_LOCAL(LanguageSet, languages, ());
- if (languages.isEmpty()) {
- languages.add("javascript");
- languages.add("javascript");
- languages.add("javascript1.0");
- languages.add("javascript1.1");
- languages.add("javascript1.2");
- languages.add("javascript1.3");
- languages.add("javascript1.4");
- languages.add("javascript1.5");
- languages.add("javascript1.6");
- languages.add("javascript1.7");
- languages.add("livescript");
- languages.add("ecmascript");
- languages.add("jscript");
+ typedef HashSet<String, ASCIICaseInsensitiveHash> LanguageSet;
+ static NeverDestroyed<LanguageSet> languages;
+ if (languages.get().isEmpty()) {
+ languages.get().add("javascript");
+ languages.get().add("javascript");
+ languages.get().add("javascript1.0");
+ languages.get().add("javascript1.1");
+ languages.get().add("javascript1.2");
+ languages.get().add("javascript1.3");
+ languages.get().add("javascript1.4");
+ languages.get().add("javascript1.5");
+ languages.get().add("javascript1.6");
+ languages.get().add("javascript1.7");
+ languages.get().add("livescript");
+ languages.get().add("ecmascript");
+ languages.get().add("jscript");
}
- return languages.contains(language);
+ return languages.get().contains(language);
}
void ScriptElement::dispatchErrorEvent()
{
- m_element->dispatchEvent(Event::create(eventNames().errorEvent, false, false));
+ m_element.dispatchEvent(Event::create(eventNames().errorEvent, false, false));
}
-bool ScriptElement::isScriptTypeSupported(LegacyTypeSupport supportLegacyTypes) const
+std::optional<ScriptElement::ScriptType> ScriptElement::determineScriptType(LegacyTypeSupport supportLegacyTypes) const
{
// FIXME: isLegacySupportedJavaScriptLanguage() is not valid HTML5. It is used here to maintain backwards compatibility with existing layout tests. The specific violations are:
// - Allowing type=javascript. type= should only support MIME types, such as text/javascript.
// - Allowing a different set of languages for language= and type=. language= supports Javascript 1.1 and 1.4-1.6, but type= does not.
-
String type = typeAttributeValue();
String language = languageAttributeValue();
- if (type.isEmpty() && language.isEmpty())
- return true; // Assume text/javascript.
if (type.isEmpty()) {
- type = "text/" + language.lower();
- if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type) || isLegacySupportedJavaScriptLanguage(language))
- return true;
- } else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace().lower()) || (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type)))
- return true;
- return false;
+ if (language.isEmpty())
+ return ScriptType::Classic; // Assume text/javascript.
+ if (MIMETypeRegistry::isSupportedJavaScriptMIMEType("text/" + language))
+ return ScriptType::Classic;
+ if (isLegacySupportedJavaScriptLanguage(language))
+ return ScriptType::Classic;
+ return std::nullopt;
+ }
+ if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace()))
+ return ScriptType::Classic;
+ if (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type))
+ return ScriptType::Classic;
+
+ // FIXME: XHTML spec defines "defer" attribute. But WebKit does not implement it for a long time.
+ // And module tag also uses defer attribute semantics. We disable script type="module" for non HTML document.
+ // Once "defer" is implemented, we can reconsider enabling modules in XHTML.
+ // https://bugs.webkit.org/show_bug.cgi?id=123387
+ if (!m_element.document().isHTMLDocument())
+ return std::nullopt;
+
+ // https://html.spec.whatwg.org/multipage/scripting.html#attr-script-type
+ // Setting the attribute to an ASCII case-insensitive match for the string "module" means that the script is a module script.
+ if (equalLettersIgnoringASCIICase(type, "module"))
+ return ScriptType::Module;
+ return std::nullopt;
}
// http://dev.w3.org/html5/spec/Overview.html#prepare-a-script
@@ -181,18 +187,22 @@ bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition, Legac
} else
wasParserInserted = false;
- if (wasParserInserted && !asyncAttributeValue())
+ if (wasParserInserted && !hasAsyncAttribute())
m_forceAsync = true;
// FIXME: HTML5 spec says we should check that all children are either comments or empty text nodes.
- if (!hasSourceAttribute() && !m_element->firstChild())
+ if (!hasSourceAttribute() && !m_element.firstChild())
return false;
- if (!m_element->inDocument())
+ if (!m_element.isConnected())
return false;
- if (!isScriptTypeSupported(supportLegacyTypes))
+ ScriptType scriptType = ScriptType::Classic;
+ if (std::optional<ScriptType> result = determineScriptType(supportLegacyTypes))
+ scriptType = result.value();
+ else
return false;
+ m_isModuleScript = scriptType == ScriptType::Module;
if (wasParserInserted) {
m_parserInserted = true;
@@ -202,7 +212,7 @@ bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition, Legac
m_alreadyStarted = true;
// FIXME: If script is parser inserted, verify it's still in the original document.
- Document& document = m_element->document();
+ Document& document = m_element.document();
// FIXME: Eventually we'd like to evaluate scripts which are inserted into a
// viewless document but this'll do for now.
@@ -210,180 +220,235 @@ bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition, Legac
if (!document.frame())
return false;
+ if (scriptType == ScriptType::Classic && hasNoModuleAttribute())
+ return false;
+
if (!document.frame()->script().canExecuteScripts(AboutToExecuteScript))
return false;
- if (!isScriptForEventSupported())
+ if (scriptType == ScriptType::Classic && !isScriptForEventSupported())
return false;
+ // According to the spec, the module tag ignores the "charset" attribute as the same to the worker's
+ // importScript. But WebKit supports the "charset" for importScript intentionally. So to be consistent,
+ // even for the module tags, we handle the "charset" attribute.
if (!charsetAttributeValue().isEmpty())
m_characterEncoding = charsetAttributeValue();
else
m_characterEncoding = document.charset();
- if (hasSourceAttribute())
- if (!requestScript(sourceAttributeValue()))
+ if (scriptType == ScriptType::Classic) {
+ if (hasSourceAttribute()) {
+ if (!requestClassicScript(sourceAttributeValue()))
+ return false;
+ }
+ } else {
+ ASSERT(scriptType == ScriptType::Module);
+ if (!requestModuleScript(scriptStartPosition))
return false;
+ }
+
+ // All the inlined module script is handled by requestModuleScript. It produces LoadableModuleScript and inlined module script
+ // is handled as the same to the external module script.
- if (hasSourceAttribute() && deferAttributeValue() && m_parserInserted && !asyncAttributeValue()) {
+ bool isClassicExternalScript = scriptType == ScriptType::Classic && hasSourceAttribute();
+ bool isParserInsertedDeferredScript = ((isClassicExternalScript && hasDeferAttribute()) || scriptType == ScriptType::Module)
+ && m_parserInserted && !hasAsyncAttribute();
+ if (isParserInsertedDeferredScript) {
m_willExecuteWhenDocumentFinishedParsing = true;
m_willBeParserExecuted = true;
- } else if (hasSourceAttribute() && m_parserInserted && !asyncAttributeValue())
+ } else if (isClassicExternalScript && m_parserInserted && !hasAsyncAttribute()) {
+ ASSERT(scriptType == ScriptType::Classic);
m_willBeParserExecuted = true;
- else if (!hasSourceAttribute() && m_parserInserted && !document.haveStylesheetsLoaded()) {
+ } else if ((isClassicExternalScript || scriptType == ScriptType::Module) && !hasAsyncAttribute() && !m_forceAsync) {
+ m_willExecuteInOrder = true;
+ ASSERT(m_loadableScript);
+ document.scriptRunner()->queueScriptForExecution(*this, *m_loadableScript, ScriptRunner::IN_ORDER_EXECUTION);
+ } else if (hasSourceAttribute() || scriptType == ScriptType::Module) {
+ ASSERT(m_loadableScript);
+ ASSERT(hasAsyncAttribute() || m_forceAsync);
+ document.scriptRunner()->queueScriptForExecution(*this, *m_loadableScript, ScriptRunner::ASYNC_EXECUTION);
+ } else if (!hasSourceAttribute() && m_parserInserted && !document.haveStylesheetsLoaded()) {
+ ASSERT(scriptType == ScriptType::Classic);
m_willBeParserExecuted = true;
m_readyToBeParserExecuted = true;
- } else if (hasSourceAttribute() && !asyncAttributeValue() && !m_forceAsync) {
- m_willExecuteInOrder = true;
- document.scriptRunner()->queueScriptForExecution(this, m_cachedScript, ScriptRunner::IN_ORDER_EXECUTION);
- m_cachedScript->addClient(this);
- } else if (hasSourceAttribute()) {
- m_element->document().scriptRunner()->queueScriptForExecution(this, m_cachedScript, ScriptRunner::ASYNC_EXECUTION);
- m_cachedScript->addClient(this);
} else {
- // Reset line numbering for nested writes.
+ ASSERT(scriptType == ScriptType::Classic);
TextPosition position = document.isInDocumentWrite() ? TextPosition() : scriptStartPosition;
- executeScript(ScriptSourceCode(scriptContent(), document.url(), position));
+ executeClassicScript(ScriptSourceCode(scriptContent(), document.url(), position, JSC::SourceProviderSourceType::Program, InlineClassicScript::create(*this)));
}
return true;
}
-bool ScriptElement::requestScript(const String& sourceUrl)
+bool ScriptElement::requestClassicScript(const String& sourceURL)
{
- Ref<Document> originalDocument(m_element->document());
- if (!m_element->dispatchBeforeLoadEvent(sourceUrl))
- return false;
- if (!m_element->inDocument() || &m_element->document() != &originalDocument.get())
+ Ref<Document> originalDocument(m_element.document());
+ if (!m_element.dispatchBeforeLoadEvent(sourceURL))
return false;
- if (!m_element->document().contentSecurityPolicy()->allowScriptNonce(m_element->fastGetAttribute(HTMLNames::nonceAttr), m_element->document().url(), m_startLineNumber, m_element->document().completeURL(sourceUrl)))
+ bool didEventListenerDisconnectThisElement = !m_element.isConnected() || &m_element.document() != originalDocument.ptr();
+ if (didEventListenerDisconnectThisElement)
return false;
- ASSERT(!m_cachedScript);
- if (!stripLeadingAndTrailingHTMLSpaces(sourceUrl).isEmpty()) {
- CachedResourceRequest request(ResourceRequest(m_element->document().completeURL(sourceUrl)));
+ ASSERT(!m_loadableScript);
+ if (!stripLeadingAndTrailingHTMLSpaces(sourceURL).isEmpty()) {
+ auto script = LoadableClassicScript::create(
+ m_element.attributeWithoutSynchronization(HTMLNames::nonceAttr),
+ m_element.attributeWithoutSynchronization(HTMLNames::crossoriginAttr),
+ scriptCharset(),
+ m_element.localName(),
+ m_element.isInUserAgentShadowTree());
+ if (script->load(m_element.document(), m_element.document().completeURL(sourceURL))) {
+ m_loadableScript = WTFMove(script);
+ m_isExternalScript = true;
+ }
+ }
+
+ if (m_loadableScript)
+ return true;
- String crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr);
- if (!crossOriginMode.isNull()) {
- m_requestUsesAccessControl = true;
- StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials;
- updateRequestForAccessControl(request.mutableResourceRequest(), m_element->document().securityOrigin(), allowCredentials);
+ callOnMainThread([this, element = Ref<Element>(m_element)] {
+ dispatchErrorEvent();
+ });
+ return false;
+}
+
+bool ScriptElement::requestModuleScript(const TextPosition& scriptStartPosition)
+{
+ String nonce = m_element.attributeWithoutSynchronization(HTMLNames::nonceAttr);
+ String crossOriginMode = m_element.attributeWithoutSynchronization(HTMLNames::crossoriginAttr);
+ if (crossOriginMode.isNull())
+ crossOriginMode = ASCIILiteral("omit");
+
+ if (hasSourceAttribute()) {
+ String sourceURL = sourceAttributeValue();
+ Ref<Document> originalDocument(m_element.document());
+ if (!m_element.dispatchBeforeLoadEvent(sourceURL))
+ return false;
+
+ bool didEventListenerDisconnectThisElement = !m_element.isConnected() || &m_element.document() != originalDocument.ptr();
+ if (didEventListenerDisconnectThisElement)
+ return false;
+
+ if (stripLeadingAndTrailingHTMLSpaces(sourceURL).isEmpty()) {
+ dispatchErrorEvent();
+ return false;
}
- request.setCharset(scriptCharset());
- request.setInitiator(element());
- m_cachedScript = m_element->document().cachedResourceLoader()->requestScript(request);
- m_isExternalScript = true;
- }
+ auto moduleScriptRootURL = m_element.document().completeURL(sourceURL);
+ if (!moduleScriptRootURL.isValid()) {
+ dispatchErrorEvent();
+ return false;
+ }
- if (m_cachedScript) {
+ m_isExternalScript = true;
+ auto script = LoadableModuleScript::create(nonce, crossOriginMode, scriptCharset(), m_element.localName(), m_element.isInUserAgentShadowTree());
+ script->load(m_element.document(), moduleScriptRootURL);
+ m_loadableScript = WTFMove(script);
return true;
}
- dispatchErrorEvent();
- return false;
+ auto script = LoadableModuleScript::create(nonce, crossOriginMode, scriptCharset(), m_element.localName(), m_element.isInUserAgentShadowTree());
+
+ TextPosition position = m_element.document().isInDocumentWrite() ? TextPosition() : scriptStartPosition;
+ ScriptSourceCode sourceCode(scriptContent(), m_element.document().url(), position, JSC::SourceProviderSourceType::Module, script.copyRef());
+
+ ASSERT(m_element.document().contentSecurityPolicy());
+ const auto& contentSecurityPolicy = *m_element.document().contentSecurityPolicy();
+ bool hasKnownNonce = contentSecurityPolicy.allowScriptWithNonce(nonce, m_element.isInUserAgentShadowTree());
+ if (!contentSecurityPolicy.allowInlineScript(m_element.document().url(), m_startLineNumber, sourceCode.source().toStringWithoutCopying(), hasKnownNonce))
+ return false;
+
+ script->load(m_element.document(), sourceCode);
+ m_loadableScript = WTFMove(script);
+ return true;
}
-void ScriptElement::executeScript(const ScriptSourceCode& sourceCode)
+void ScriptElement::executeClassicScript(const ScriptSourceCode& sourceCode)
{
+ ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread());
ASSERT(m_alreadyStarted);
if (sourceCode.isEmpty())
return;
- if (!m_element->document().contentSecurityPolicy()->allowScriptNonce(m_element->fastGetAttribute(HTMLNames::nonceAttr), m_element->document().url(), m_startLineNumber))
- return;
+ if (!m_isExternalScript) {
+ ASSERT(m_element.document().contentSecurityPolicy());
+ const ContentSecurityPolicy& contentSecurityPolicy = *m_element.document().contentSecurityPolicy();
+ bool hasKnownNonce = contentSecurityPolicy.allowScriptWithNonce(m_element.attributeWithoutSynchronization(HTMLNames::nonceAttr), m_element.isInUserAgentShadowTree());
+ if (!contentSecurityPolicy.allowInlineScript(m_element.document().url(), m_startLineNumber, sourceCode.source().toStringWithoutCopying(), hasKnownNonce))
+ return;
+ }
- if (!m_isExternalScript && !m_element->document().contentSecurityPolicy()->allowInlineScript(m_element->document().url(), m_startLineNumber))
+ auto& document = m_element.document();
+ auto* frame = document.frame();
+ if (!frame)
return;
-#if ENABLE(NOSNIFF)
- if (m_isExternalScript && m_cachedScript && !m_cachedScript->mimeTypeAllowedByNosniff()) {
- m_element->document().addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Refused to execute script from '" + m_cachedScript->url().stringCenterEllipsizedToLength() + "' because its MIME type ('" + m_cachedScript->mimeType() + "') is not executable, and strict MIME type checking is enabled.");
- return;
- }
-#endif
+ IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(m_isExternalScript ? &document : nullptr);
+ CurrentScriptIncrementer currentScriptIncrementer(document, m_element);
- Ref<Document> document(m_element->document());
- if (Frame* frame = document->frame()) {
- IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(m_isExternalScript ? &document.get() : 0);
- CurrentScriptIncrementer currentScriptIncrementer(&document.get(), 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().evaluate(sourceCode);
- }
+ frame->script().evaluate(sourceCode);
}
-void ScriptElement::stopLoadRequest()
+void ScriptElement::executeModuleScript(LoadableModuleScript& loadableModuleScript)
{
- if (m_cachedScript) {
- if (!m_willBeParserExecuted)
- m_cachedScript->removeClient(this);
- m_cachedScript = 0;
- }
+ // https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
+
+ ASSERT(!loadableModuleScript.error());
+
+ auto& document = m_element.document();
+ auto* frame = document.frame();
+ if (!frame)
+ return;
+
+ IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(&document);
+ CurrentScriptIncrementer currentScriptIncrementer(document, m_element);
+
+ frame->script().linkAndEvaluateModuleScript(loadableModuleScript);
}
-void ScriptElement::execute(CachedScript* cachedScript)
+void ScriptElement::executeScriptAndDispatchEvent(LoadableScript& loadableScript)
{
- ASSERT(!m_willBeParserExecuted);
- ASSERT(cachedScript);
- if (cachedScript->errorOccurred())
+ if (std::optional<LoadableScript::Error> error = loadableScript.error()) {
+ if (std::optional<LoadableScript::ConsoleMessage> message = error->consoleMessage)
+ m_element.document().addConsoleMessage(message->source, message->level, message->message);
dispatchErrorEvent();
- else if (!cachedScript->wasCanceled()) {
- executeScript(ScriptSourceCode(cachedScript));
+ } else if (!loadableScript.wasCanceled()) {
+ ASSERT(!loadableScript.error());
+ loadableScript.execute(*this);
dispatchLoadEvent();
}
- cachedScript->removeClient(this);
}
-void ScriptElement::notifyFinished(CachedResource* resource)
+void ScriptElement::executePendingScript(PendingScript& pendingScript)
{
- ASSERT(!m_willBeParserExecuted);
-
- // CachedResource possibly invokes this notifyFinished() more than
- // once because ScriptElement doesn't unsubscribe itself from
- // CachedResource here and does it in execute() instead.
- // We use m_cachedScript to check if this function is already called.
- ASSERT_UNUSED(resource, resource == m_cachedScript);
- if (!m_cachedScript)
- return;
-
- if (m_requestUsesAccessControl
- && !m_element->document().securityOrigin()->canRequest(m_cachedScript->response().url())
- && !m_cachedScript->passesAccessControlCheck(m_element->document().securityOrigin())) {
-
- dispatchErrorEvent();
- DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Cross-origin script load denied by Cross-Origin Resource Sharing policy.")));
- m_element->document().addConsoleMessage(JSMessageSource, ErrorMessageLevel, consoleMessage);
- return;
+ if (auto* loadableScript = pendingScript.loadableScript())
+ executeScriptAndDispatchEvent(*loadableScript);
+ else {
+ ASSERT(!pendingScript.error());
+ ASSERT_WITH_MESSAGE(scriptType() == ScriptType::Classic, "Module script always have a loadableScript pointer.");
+ executeClassicScript(ScriptSourceCode(scriptContent(), m_element.document().url(), pendingScript.startingPosition(), JSC::SourceProviderSourceType::Program, InlineClassicScript::create(*this)));
+ dispatchLoadEvent();
}
-
- if (m_willExecuteInOrder)
- m_element->document().scriptRunner()->notifyScriptReady(this, ScriptRunner::IN_ORDER_EXECUTION);
- else
- m_element->document().scriptRunner()->notifyScriptReady(this, ScriptRunner::ASYNC_EXECUTION);
-
- m_cachedScript = 0;
}
bool ScriptElement::ignoresLoadRequest() const
{
- return m_alreadyStarted || m_isExternalScript || m_parserInserted || !m_element->inDocument();
+ return m_alreadyStarted || m_isExternalScript || m_parserInserted || !m_element.isConnected();
}
bool ScriptElement::isScriptForEventSupported() const
{
String eventAttribute = eventAttributeValue();
String forAttribute = forAttributeValue();
- if (!eventAttribute.isEmpty() && !forAttribute.isEmpty()) {
- forAttribute = forAttribute.stripWhiteSpace();
- if (!equalIgnoringCase(forAttribute, "window"))
+ if (!eventAttribute.isNull() && !forAttribute.isNull()) {
+ forAttribute = stripLeadingAndTrailingHTMLSpaces(forAttribute);
+ if (!equalLettersIgnoringASCIICase(forAttribute, "window"))
return false;
- eventAttribute = eventAttribute.stripWhiteSpace();
- if (!equalIgnoringCase(eventAttribute, "onload") && !equalIgnoringCase(eventAttribute, "onload()"))
+ eventAttribute = stripLeadingAndTrailingHTMLSpaces(eventAttribute);
+ if (!equalLettersIgnoringASCIICase(eventAttribute, "onload") && !equalLettersIgnoringASCIICase(eventAttribute, "onload()"))
return false;
}
return true;
@@ -391,20 +456,32 @@ bool ScriptElement::isScriptForEventSupported() const
String ScriptElement::scriptContent() const
{
- return TextNodeTraversal::contentsAsString(m_element);
+ StringBuilder result;
+ for (auto* text = TextNodeTraversal::firstChild(m_element); text; text = TextNodeTraversal::nextSibling(*text))
+ result.append(text->data());
+ return result.toString();
}
-ScriptElement* toScriptElementIfPossible(Element* element)
+void ScriptElement::ref()
{
- if (isHTMLScriptElement(element))
- return toHTMLScriptElement(element);
+ m_element.ref();
+}
-#if ENABLE(SVG)
- if (isSVGScriptElement(element))
- return toSVGScriptElement(element);
-#endif
+void ScriptElement::deref()
+{
+ m_element.deref();
+}
- return 0;
+bool isScriptElement(Element& element)
+{
+ return is<HTMLScriptElement>(element) || is<SVGScriptElement>(element);
+}
+
+ScriptElement& downcastScriptElement(Element& element)
+{
+ if (is<HTMLScriptElement>(element))
+ return downcast<HTMLScriptElement>(element);
+ return downcast<SVGScriptElement>(element);
}
}