Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Align <script type language> with the HTML Standard
https://bugs.webkit.org/show_bug.cgi?id=257080
rdar://109600797

Reviewed by Alexey Shvayka.

In particular:

- Stop supporting language attribute values in the type attribute value.
- Stop supporting javascript1.6 and javascript1.7 altogether.
- Strip ASCII whitespace rather than some variant of Unicode whitespace.

In terms of interoperability:

- The non-whitespace changes align WebKit with Chromium and Gecko.
- The whitespace change align us with Gecko only.

New test is upstreamed via web-platform-tests/wpt#40112.

* LayoutTests/fast/html/script-allowed-types-languages-expected.txt:
* LayoutTests/fast/html/script-allowed-types-languages.html:
* LayoutTests/fast/tokenizer/004-expected.txt:
* LayoutTests/fast/tokenizer/004.html:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-svg-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-xhtml-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/script-type-whitespace-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/script-type-whitespace.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/w3c-import.log:
* LayoutTests/svg/custom/absolute-sized-content-with-resources.xhtml:
* LayoutTests/svg/custom/relative-sized-content-with-resources.xhtml:
* LayoutTests/svg/custom/relative-sized-content.xhtml:
* LayoutTests/svg/custom/relative-sized-image.xhtml:
* Source/WebCore/dom/ScriptElement.cpp:
(WebCore::ScriptElement::determineScriptType):
(WebCore::ScriptElement::determineScriptType const):
(WebCore::ScriptElement::prepareScript):
(WebCore::isLegacySupportedJavaScriptLanguage): Deleted.
* Source/WebCore/dom/ScriptElement.h:
* Source/WebCore/xml/parser/XMLDocumentParserLibxml2.cpp:
(WebCore::XMLDocumentParser::endElementNs):

Canonical link: https://commits.webkit.org/265977@main
  • Loading branch information
annevk committed Jul 12, 2023
1 parent 243357f commit 724b429
Show file tree
Hide file tree
Showing 17 changed files with 84 additions and 81 deletions.
Expand Up @@ -26,11 +26,10 @@ Type: unspecified, Language: "javascript1.2"
Type: unspecified, Language: "javascript1.3"
Type: unspecified, Language: "javascript1.4"
Type: unspecified, Language: "javascript1.5"
Type: unspecified, Language: "javascript1.6"
Type: unspecified, Language: "javascript1.7"
Type: unspecified, Language: "ecmascript"
Type: unspecified, Language: "livescript"
Type: unspecified, Language: "jscript"
When a type is specified, the language attribute should be ignored.
Type: "", Language: ""
Type: "", Language: "javascript"
Type: "", Language: "javascript1.0"
Expand All @@ -52,7 +51,6 @@ Type: "", Language: " javascript"
Type: "", Language: "javascript "
Type: "", Language: " javascript "
Type: "", Language: "abcdefg"
When a type is specified, the language attribute should be ignored.
Type: "text/javascript", Language: ""
Type: "text/javascript", Language: "javascript"
Type: "text/javascript", Language: "javascript1.0"
Expand Down
2 changes: 1 addition & 1 deletion LayoutTests/fast/html/script-allowed-types-languages.html
Expand Up @@ -104,10 +104,10 @@
for (var i = 0; i < languages.length; ++i)
document.body.appendChild(createScriptElement(null, languages[i]));

log('When a type is specified, the language attribute should be ignored.');
for (var i = 0; i < languages.length; ++i)
document.body.appendChild(createScriptElement("", languages[i]));

log('When a type is specified, the language attribute should be ignored.');
for (var i = 0; i < languages.length; ++i)
document.body.appendChild(createScriptElement("text/javascript", languages[i]));
}
Expand Down
4 changes: 2 additions & 2 deletions LayoutTests/fast/tokenizer/004-expected.txt
Expand Up @@ -108,8 +108,6 @@ JavaScript1.2 executed
JavaScript1.3 executed
JavaScript1.4 executed
JavaScript1.5 executed
JavaScript1.6 executed
JavaScript1.7 executed
These scripts should not execute

one space
Expand All @@ -136,6 +134,8 @@ javascript_
javascript_1.0
javascript 1.0 x
JavaScript1
JavaScript1.6
JavaScript1.7
JavaScript1.8
JavaScript1.9
JavaScript1.4.1
Expand Down
4 changes: 2 additions & 2 deletions LayoutTests/fast/tokenizer/004.html
Expand Up @@ -115,8 +115,6 @@ <h4>These scripts should execute</h4>
<li>JavaScript1.3 <script language="JavaScript1.3">document.write("executed");</script></li>
<li>JavaScript1.4 <script language="JavaScript1.4">document.write("executed");</script></li>
<li>JavaScript1.5 <script language="JavaScript1.5">document.write("executed");</script></li>
<li>JavaScript1.6 <script language="JavaScript1.6">document.write("executed");</script></li>
<li>JavaScript1.7 <script language="JavaScript1.7">document.write("executed");</script></li>
</ol>
<h4>These scripts should not execute</h4>
<ol>
Expand Down Expand Up @@ -144,6 +142,8 @@ <h4>These scripts should not execute</h4>
<li>javascript_1.0 <script language="javascript_1.0">document.write("executed");</script></li>
<li>javascript 1.0 x <script language="javascript 1.0 x">document.write("executed");</script></li>
<li>JavaScript1 <script language="JavaScript1">document.write("executed");</script></li>
<li>JavaScript1.6 <script language="JavaScript1.6">document.write("executed");</script></li>
<li>JavaScript1.7 <script language="JavaScript1.7">document.write("executed");</script></li>
<li>JavaScript1.8 <script language="JavaScript1.8">document.write("executed");</script></li>
<li>JavaScript1.9 <script language="JavaScript1.9">document.write("executed");</script></li>
<li>JavaScript1.4.1 <script language="JavaScript1.4.1">document.write("executed");</script></li>
Expand Down
Expand Up @@ -271,8 +271,8 @@ PASS Script should run with language="JSCRIPT"
PASS Script should run with language="LIVESCRIPT"
PASS Script should run with language="X-ECMASCRIPT"
PASS Script should run with language="X-JAVASCRIPT"
FAIL Script shouldn't run with language="javascript1.6" assert_equals: expected false but got true
FAIL Script shouldn't run with language="javascript1.7" assert_equals: expected false but got true
PASS Script shouldn't run with language="javascript1.6"
PASS Script shouldn't run with language="javascript1.7"
PASS Script shouldn't run with language="javascript1.8"
PASS Script shouldn't run with language="javascript1.9"
PASS Script shouldn't run with language="ecmascript "
Expand Down
Expand Up @@ -443,16 +443,16 @@ PASS Script should run with language="jscript\0foo"
PASS Script should run with language="livescript\0foo"
PASS Script should run with language="x-ecmascript\0foo"
PASS Script should run with language="x-javascript\0foo"
FAIL Script shouldn't run with type=javascript (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.0 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.1 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.2 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.3 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.4 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.5 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.6 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.7 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=livescript (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=ecmascript (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=jscript (parser-inserted) assert_false: expected false got true
PASS Script shouldn't run with type=javascript (parser-inserted)
PASS Script shouldn't run with type=javascript1.0 (parser-inserted)
PASS Script shouldn't run with type=javascript1.1 (parser-inserted)
PASS Script shouldn't run with type=javascript1.2 (parser-inserted)
PASS Script shouldn't run with type=javascript1.3 (parser-inserted)
PASS Script shouldn't run with type=javascript1.4 (parser-inserted)
PASS Script shouldn't run with type=javascript1.5 (parser-inserted)
PASS Script shouldn't run with type=javascript1.6 (parser-inserted)
PASS Script shouldn't run with type=javascript1.7 (parser-inserted)
PASS Script shouldn't run with type=livescript (parser-inserted)
PASS Script shouldn't run with type=ecmascript (parser-inserted)
PASS Script shouldn't run with type=jscript (parser-inserted)

Expand Up @@ -271,8 +271,8 @@ PASS Script should run with language="JSCRIPT"
PASS Script should run with language="LIVESCRIPT"
PASS Script should run with language="X-ECMASCRIPT"
PASS Script should run with language="X-JAVASCRIPT"
FAIL Script shouldn't run with language="javascript1.6" assert_equals: expected false but got true
FAIL Script shouldn't run with language="javascript1.7" assert_equals: expected false but got true
PASS Script shouldn't run with language="javascript1.6"
PASS Script shouldn't run with language="javascript1.7"
PASS Script shouldn't run with language="javascript1.8"
PASS Script shouldn't run with language="javascript1.9"
PASS Script shouldn't run with language="ecmascript "
Expand Down Expand Up @@ -443,16 +443,16 @@ PASS Script shouldn't run with language="jscript\0foo"
PASS Script shouldn't run with language="livescript\0foo"
PASS Script shouldn't run with language="x-ecmascript\0foo"
PASS Script shouldn't run with language="x-javascript\0foo"
FAIL Script shouldn't run with type=javascript (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.0 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.1 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.2 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.3 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.4 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.5 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.6 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=javascript1.7 (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=livescript (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=ecmascript (parser-inserted) assert_false: expected false got true
FAIL Script shouldn't run with type=jscript (parser-inserted) assert_false: expected false got true
PASS Script shouldn't run with type=javascript (parser-inserted)
PASS Script shouldn't run with type=javascript1.0 (parser-inserted)
PASS Script shouldn't run with type=javascript1.1 (parser-inserted)
PASS Script shouldn't run with type=javascript1.2 (parser-inserted)
PASS Script shouldn't run with type=javascript1.3 (parser-inserted)
PASS Script shouldn't run with type=javascript1.4 (parser-inserted)
PASS Script shouldn't run with type=javascript1.5 (parser-inserted)
PASS Script shouldn't run with type=javascript1.6 (parser-inserted)
PASS Script shouldn't run with type=javascript1.7 (parser-inserted)
PASS Script shouldn't run with type=livescript (parser-inserted)
PASS Script shouldn't run with type=ecmascript (parser-inserted)
PASS Script shouldn't run with type=jscript (parser-inserted)

@@ -0,0 +1,7 @@

PASS Script shouldn't run with type="text/javascript&#x000B;" (parser-inserted)
PASS Script shouldn't run with type="text/javascript&#x0085;" (parser-inserted)
PASS Script shouldn't run with type="text/javascript&#x00A0;" (parser-inserted)
PASS Script shouldn't run with type="text/javascript&#x1680;" (parser-inserted)
PASS Script shouldn't run with type="text/javascript&#x3000;" (parser-inserted)

@@ -0,0 +1,27 @@
<!doctype html>
<title>&lt;script type> non-ASCII whitespace handling</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script>
function testParserInsertedDidNotRun(description) {
test(() => assert_false(window.ran),
"Script shouldn't run with " + description + " (parser-inserted)");
window.ran = false;
}
</script>

<script>window.ran = false;</script>
<script type="text/javascript&#x000B;">window.ran = true;</script>
<script>testParserInsertedDidNotRun("type=\"text/javascript&#x000B;\"");</script>

<script type="text/javascript&#x0085;">window.ran = true;</script>
<script>testParserInsertedDidNotRun("type=\"text/javascript&#x0085;\"");</script>

<script type="text/javascript&#x00A0;">window.ran = true;</script>
<script>testParserInsertedDidNotRun("type=\"text/javascript&#x00A0;\"");</script>

<script type="text/javascript&#x1680;">window.ran = true;</script>
<script>testParserInsertedDidNotRun("type=\"text/javascript&#x1680;\"");</script>

<script type="text/javascript&#x3000;">window.ran = true;</script>
<script>testParserInsertedDidNotRun("type=\"text/javascript&#x3000;\"");</script>
Expand Up @@ -79,6 +79,7 @@ List of files:
/LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js-xhtml.xhtml
/LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/script-type-and-language-js.html
/LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/script-type-and-language-with-params.html
/LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/script-type-whitespace.html
/LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/scripting-enabled.html
/LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/serve-json-then-js.py
/LayoutTests/imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/serve-with-content-type.py
Expand Up @@ -25,7 +25,7 @@
</g>
</svg>
</div>
<script type="JavaScript">
<script>
function repaintTest() {
document.getElementById("contentBox").style.setProperty("width", "400px");
}
Expand Down
Expand Up @@ -16,7 +16,7 @@
<circle cx="50%" cy="50%" r="40%" stroke="gray" fill="url(#grad)"/>
</svg>
</div>
<script type="JavaScript">
<script>
function repaintTest() {
document.getElementById("contentBox").style.setProperty("width", "400px");
}
Expand Down
2 changes: 1 addition & 1 deletion LayoutTests/svg/custom/relative-sized-content.xhtml
Expand Up @@ -9,7 +9,7 @@
<rect id="targetRect" width="100%" height="100%" fill="blue"/>
</svg>
</div>
<script type="JavaScript">
<script>
function repaintTest() {
document.getElementById("contentBox").style.setProperty("width", "400px");
}
Expand Down
2 changes: 1 addition & 1 deletion LayoutTests/svg/custom/relative-sized-image.xhtml
Expand Up @@ -9,7 +9,7 @@
<image xlink:href="../W3C-SVG-1.1/resources/smiley.png" width="100%" height="100%" preserveAspectRatio="none"/>
</svg>
</div>
<script type="JavaScript">
<script>
function repaintTest() {
document.getElementById("contentBox").style.setProperty("width", "400px");
}
Expand Down
43 changes: 7 additions & 36 deletions Source/WebCore/dom/ScriptElement.cpp
Expand Up @@ -59,8 +59,6 @@
#include "TextNodeTraversal.h"
#include <JavaScriptCore/ImportMap.h>
#include <wtf/Scope.h>
#include <wtf/SortedArrayMap.h>
#include <wtf/StdLibExtras.h>
#include <wtf/SystemTracing.h>

namespace WebCore {
Expand Down Expand Up @@ -107,52 +105,25 @@ void ScriptElement::handleAsyncAttribute()
m_forceAsync = false;
}

static bool isLegacySupportedJavaScriptLanguage(const String& language)
{
static constexpr ComparableLettersLiteral languageArray[] = {
"ecmascript",
"javascript",
"javascript1.0",
"javascript1.1",
"javascript1.2",
"javascript1.3",
"javascript1.4",
"javascript1.5",
"javascript1.6",
"javascript1.7",
"jscript",
"livescript",
};
static constexpr SortedArraySet languageSet { languageArray };
return languageSet.contains(language);
}

void ScriptElement::dispatchErrorEvent()
{
m_element.dispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
}

// https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script
std::optional<ScriptType> ScriptElement::determineScriptType(const String& type, const String& language, bool isHTMLDocument, LegacyTypeSupport supportLegacyTypes)
std::optional<ScriptType> ScriptElement::determineScriptType(const String& type, const String& language, bool isHTMLDocument)
{
// 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.
if (type.isNull()) {
if (language.isEmpty())
return ScriptType::Classic; // Assume text/javascript.
if (MIMETypeRegistry::isSupportedJavaScriptMIMEType("text/" + language))
return ScriptType::Classic;
if (isLegacySupportedJavaScriptLanguage(language))
if (MIMETypeRegistry::isSupportedJavaScriptMIMEType("text/" + language))
return ScriptType::Classic;
return std::nullopt;
}
if (type.isEmpty())
return ScriptType::Classic; // Assume text/javascript.

if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.trim(deprecatedIsSpaceOrNewline)))
return ScriptType::Classic;
if (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type))
if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.trim(isASCIIWhitespace)))
return ScriptType::Classic;

// FIXME: XHTML spec defines "defer" attribute. But WebKit does not implement it for a long time.
Expand All @@ -175,13 +146,13 @@ std::optional<ScriptType> ScriptElement::determineScriptType(const String& type,
return std::nullopt;
}

std::optional<ScriptType> ScriptElement::determineScriptType(LegacyTypeSupport supportLegacyTypes) const
std::optional<ScriptType> ScriptElement::determineScriptType() const
{
return determineScriptType(typeAttributeValue(), languageAttributeValue(), m_element.document().isHTMLDocument(), supportLegacyTypes);
return determineScriptType(typeAttributeValue(), languageAttributeValue(), m_element.document().isHTMLDocument());
}

// https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element
bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition, LegacyTypeSupport supportLegacyTypes)
bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition)
{
if (m_alreadyStarted)
return false;
Expand All @@ -204,7 +175,7 @@ bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition, Legac
return false;

ScriptType scriptType = ScriptType::Classic;
if (std::optional<ScriptType> result = determineScriptType(supportLegacyTypes))
if (std::optional<ScriptType> result = determineScriptType())
scriptType = result.value();
else
return false;
Expand Down
7 changes: 3 additions & 4 deletions Source/WebCore/dom/ScriptElement.h
Expand Up @@ -48,8 +48,7 @@ class ScriptElement {
Element& element() { return m_element; }
const Element& element() const { return m_element; }

enum LegacyTypeSupport { DisallowLegacyTypeInTypeAttribute, AllowLegacyTypeInTypeAttribute };
bool prepareScript(const TextPosition& scriptStartPosition = TextPosition(), LegacyTypeSupport = DisallowLegacyTypeInTypeAttribute);
bool prepareScript(const TextPosition& scriptStartPosition = TextPosition());

String scriptCharset() const { return m_characterEncoding; }
WEBCORE_EXPORT String scriptContent() const;
Expand Down Expand Up @@ -81,7 +80,7 @@ class ScriptElement {
void ref();
void deref();

static std::optional<ScriptType> determineScriptType(const String& typeAttribute, const String& languageAttribute, bool isHTMLDocument = true, LegacyTypeSupport = DisallowLegacyTypeInTypeAttribute);
static std::optional<ScriptType> determineScriptType(const String& typeAttribute, const String& languageAttribute, bool isHTMLDocument = true);

protected:
ScriptElement(Element&, bool createdByParser, bool isEvaluated);
Expand All @@ -108,7 +107,7 @@ class ScriptElement {
private:
void executeScriptAndDispatchEvent(LoadableScript&);

std::optional<ScriptType> determineScriptType(LegacyTypeSupport) const;
std::optional<ScriptType> determineScriptType() const;
bool ignoresLoadRequest() const;
void dispatchLoadEventRespectingUserGestureIndicator();

Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/xml/parser/XMLDocumentParserLibxml2.cpp
Expand Up @@ -906,7 +906,7 @@ void XMLDocumentParser::endElementNs()
m_requestingScript = true;

auto& scriptElement = downcastScriptElement(element);
if (scriptElement.prepareScript(m_scriptStartPosition, ScriptElement::AllowLegacyTypeInTypeAttribute)) {
if (scriptElement.prepareScript(m_scriptStartPosition)) {
// FIXME: Script execution should be shared between
// the libxml2 and Qt XMLDocumentParser implementations.

Expand Down

0 comments on commit 724b429

Please sign in to comment.