Commit 4879ffa3 authored by simonjam@chromium.org's avatar simonjam@chromium.org

2011-02-19 James Simonsen <simonjam@chromium.org>

        Reviewed by Adam Barth.

        Make ScriptElement match the HTML5 spec
        https://bugs.webkit.org/show_bug.cgi?id=54676

        This implements the "prepare a script" section of the HTML5 spec in ScriptElement::prepareScript().
        http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#prepare-a-script

        There are a couple of things missing from the spec that would be new functionality. These will be added later.
        - Support for async=false
        - Empty src attribute should dispatch an error.

        There are a couple of slight behavioral changes to match the spec.
        - An XHTML script that is loaded then copied will not fire load on the copy.
        - If onbeforeload moves the script to a different document, then it will not try to execute again because wasAlreadyStarted is true.

        The parsers were updated to use the new API, but not rewritten to look like the spec. That will be done separately.

        * fast/dom/HTMLScriptElement/move-in-beforeload.html: Original author says test was only meant to check for crashes. Beforeload is not specified by HTML5. Test was modified to assume moved script wouldn't execute because wasAlreadyStarted is set.
        * fast/dom/script-clone-rerun-src.xhtml: According to HTML5 spec, load should only fire after a script executes. Test was modified to match that. The old broken behavior was that load fired on the cloned element, even though it didn't execute.
2011-02-19  James Simonsen  <simonjam@chromium.org>

        Reviewed by Adam Barth.

        Make ScriptElement match the HTML5 spec
        https://bugs.webkit.org/show_bug.cgi?id=54676

        This implements the "prepare a script" section of the HTML5 spec in ScriptElement::prepareScript().
        http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#prepare-a-script

        There are a couple of things missing from the spec that would be new functionality. These will be added later.
        - Support for async=false
        - Empty src attribute should dispatch an error.

        There are a couple of slight behavioral changes to match the spec.
        - An XHTML script that is loaded then copied will not fire load on the copy.
        - If onbeforeload moves the script to a different document, then it will not try to execute again because wasAlreadyStarted is true.

        The parsers were updated to use the new API, but not rewritten to look like the spec. That will be done separately.

        Test: All existing tests.

        * dom/ScriptElement.cpp: Rewritten to match HTML5 spec.
        (WebCore::ScriptElement::ScriptElement):
        (WebCore::ScriptElement::insertedIntoDocument): Logic moved to prepareScript.
        (WebCore::ScriptElement::childrenChanged): Logic moved to prepareScript.
        (WebCore::ScriptElement::handleSourceAttribute): Logic moved to prepareScript.
        (WebCore::isLegacySupportedJavaScriptLanguage): Added to support old script types in layout tests.
        (WebCore::ScriptElement::isScriptTypeSupported): Derived from old shouldExecuteAsJavaScript().
        (WebCore::ScriptElement::prepareScript): START HERE. Main change. Should read exactly like HTML5's "prepare a script." Legacy type support needed for layout tests using XML parser.
        (WebCore::ScriptElement::requestScript): Most logic moved to prepareScript. Check security settings here.
        (WebCore::ScriptElement::executeScript): Combined evaluateScript() and executeScript() from old code. Logic moved to prepareScript.
        (WebCore::ScriptElement::stopLoadRequest): Ignore parser executed scripts.
        (WebCore::ScriptElement::execute): Renamed executeScript.
        (WebCore::ScriptElement::notifyFinished): We should only listen for non-parser executed scripts.
        (WebCore::ScriptElement::ignoresLoadRequest): New variable names.
        (WebCore::ScriptElement::childrenAreCommentsOrEmptyText): Added for HTML5 compliance.
        (WebCore::ScriptElement::scriptCharset): Use HTML5 variables.
        * dom/ScriptElement.h:
        (WebCore::ScriptElement::willBeParserExecuted): Added.
        (WebCore::ScriptElement::readyToBeParserExecuted): Added.
        (WebCore::ScriptElement::willExecuteWhenDocumentFinishedParsing): Added.
        (WebCore::ScriptElement::cachedScript): prepareScript() is the only place that should load scripts. This accessor lets the parsers listen for when loads finish.
        (WebCore::ScriptElement::isParserInserted): Added.
        * dom/XMLDocumentParserLibxml2.cpp:
        (WebCore::XMLDocumentParser::endElementNs): Should behave the same. Offloads much of its work to prepareScript().
        * dom/XMLDocumentParserQt.cpp:
        (WebCore::XMLDocumentParser::parseEndElement): Identical to libxml2 changes.
        * html/HTMLScriptElement.cpp:
        (WebCore::HTMLScriptElement::insertedIntoDocument): No longer needs url.
        (WebCore::HTMLScriptElement::hasSourceAttribute): Added.
        * html/HTMLScriptElement.h:
        * html/parser/HTMLScriptRunner.cpp:
        (WebCore::HTMLScriptRunner::requestPendingScript): Requesting scripts offloaded to ScriptElement.
        (WebCore::HTMLScriptRunner::runScript): Should behave the same. Offloads much of its work to prepareScript().
        * svg/SVGScriptElement.cpp:
        (WebCore::SVGScriptElement::svgAttributeChanged): New ScriptElement function names.
        (WebCore::SVGScriptElement::insertedIntoDocument): No longer needs url.
        (WebCore::SVGScriptElement::finishParsingChildren): ScriptElement::finishParsingChildren is gone.
        (WebCore::SVGScriptElement::hasSourceAttribute): Added.
        (WebCore::SVGScriptElement::dispatchLoadEvent): New ScriptElement function names.
        * svg/SVGScriptElement.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@79114 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 0c1625f5
2011-02-19 James Simonsen <simonjam@chromium.org>
Reviewed by Adam Barth.
Make ScriptElement match the HTML5 spec
https://bugs.webkit.org/show_bug.cgi?id=54676
This implements the "prepare a script" section of the HTML5 spec in ScriptElement::prepareScript().
http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#prepare-a-script
There are a couple of things missing from the spec that would be new functionality. These will be added later.
- Support for async=false
- Empty src attribute should dispatch an error.
There are a couple of slight behavioral changes to match the spec.
- An XHTML script that is loaded then copied will not fire load on the copy.
- If onbeforeload moves the script to a different document, then it will not try to execute again because wasAlreadyStarted is true.
The parsers were updated to use the new API, but not rewritten to look like the spec. That will be done separately.
* fast/dom/HTMLScriptElement/move-in-beforeload.html: Original author says test was only meant to check for crashes. Beforeload is not specified by HTML5. Test was modified to assume moved script wouldn't execute because wasAlreadyStarted is set.
* fast/dom/script-clone-rerun-src.xhtml: According to HTML5 spec, load should only fire after a script executes. Test was modified to match that. The old broken behavior was that load fired on the cloned element, even though it didn't execute.
2011-02-19 Andrew Wilson <atwilson@chromium.org>
Updated test expectations (more fallout from r78846).
......
......@@ -8,7 +8,6 @@
<iframe src="resources/frame.html"></iframe>
<script>
if (window.layoutTestController) {
layoutTestController.waitUntilDone();
layoutTestController.dumpAsText();
}
......@@ -18,14 +17,13 @@ function test()
s.addEventListener("beforeload", function() {
var s = document.body.removeChild(document.getElementsByTagName("script")[2]);
document.getElementsByTagName("iframe")[0].contentDocument.body.appendChild(s);
testPassed("");
}, false);
s.addEventListener("error", function() {
testFailed("error event should not fire.")
testFailed("error event should not fire.");
}, false);
s.addEventListener("load", function() {
testPassed("");
if (window.layoutTestController)
layoutTestController.notifyDone();
testFailed("script should not be loaded");
}, false);
s.src = "external.js";
document.body.appendChild(s);
......
......@@ -11,36 +11,30 @@ if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
var firstLoad = true;
function loaded() {
if (firstLoad) {
firstLoad = false;
return;
}
result = 'PASS';
if (i == 2)
result = 'FAIL: script ran twice';
else if (i > 2)
result = 'FAIL: script ran ' + i + ' times';
document.body.appendChild(document.createTextNode(result));
layoutTestController.notifyDone();
}
</script>
</head>
<body>
<p>This test runs a script that clones and inserts its script element. The script should run exactly once and print PASS:</p>
<div><script src="resources/script-clone-rerun-src.js" onload="loaded()"></script></div>
<div><script src="resources/script-clone-rerun-src.js"></script></div>
<script>
var script = document.getElementsByTagName('script')[1];
var div = script.parentNode;
div.removeChild(script);
div.appendChild(script.cloneNode(true));
var clone = script.cloneNode(true);
clone.addEventListener("load", function() { document.body.appendChild(document.createTextNode("FAIL: load event fired on clone")); }, false);
div.appendChild(clone);
setTimeout(function() {
result = 'FAIL: script never ran';
if (i == 1)
result = 'PASS';
else if (i > 1)
result = 'FAIL: script ran ' + i + ' times';
document.body.appendChild(document.createTextNode(result));
if (window.layoutTestController)
layoutTestController.notifyDone();
}, 0);
</script>
</body>
......
2011-02-19 James Simonsen <simonjam@chromium.org>
Reviewed by Adam Barth.
Make ScriptElement match the HTML5 spec
https://bugs.webkit.org/show_bug.cgi?id=54676
This implements the "prepare a script" section of the HTML5 spec in ScriptElement::prepareScript().
http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#prepare-a-script
There are a couple of things missing from the spec that would be new functionality. These will be added later.
- Support for async=false
- Empty src attribute should dispatch an error.
There are a couple of slight behavioral changes to match the spec.
- An XHTML script that is loaded then copied will not fire load on the copy.
- If onbeforeload moves the script to a different document, then it will not try to execute again because wasAlreadyStarted is true.
The parsers were updated to use the new API, but not rewritten to look like the spec. That will be done separately.
Test: All existing tests.
* dom/ScriptElement.cpp: Rewritten to match HTML5 spec.
(WebCore::ScriptElement::ScriptElement):
(WebCore::ScriptElement::insertedIntoDocument): Logic moved to prepareScript.
(WebCore::ScriptElement::childrenChanged): Logic moved to prepareScript.
(WebCore::ScriptElement::handleSourceAttribute): Logic moved to prepareScript.
(WebCore::isLegacySupportedJavaScriptLanguage): Added to support old script types in layout tests.
(WebCore::ScriptElement::isScriptTypeSupported): Derived from old shouldExecuteAsJavaScript().
(WebCore::ScriptElement::prepareScript): START HERE. Main change. Should read exactly like HTML5's "prepare a script." Legacy type support needed for layout tests using XML parser.
(WebCore::ScriptElement::requestScript): Most logic moved to prepareScript. Check security settings here.
(WebCore::ScriptElement::executeScript): Combined evaluateScript() and executeScript() from old code. Logic moved to prepareScript.
(WebCore::ScriptElement::stopLoadRequest): Ignore parser executed scripts.
(WebCore::ScriptElement::execute): Renamed executeScript.
(WebCore::ScriptElement::notifyFinished): We should only listen for non-parser executed scripts.
(WebCore::ScriptElement::ignoresLoadRequest): New variable names.
(WebCore::ScriptElement::childrenAreCommentsOrEmptyText): Added for HTML5 compliance.
(WebCore::ScriptElement::scriptCharset): Use HTML5 variables.
* dom/ScriptElement.h:
(WebCore::ScriptElement::willBeParserExecuted): Added.
(WebCore::ScriptElement::readyToBeParserExecuted): Added.
(WebCore::ScriptElement::willExecuteWhenDocumentFinishedParsing): Added.
(WebCore::ScriptElement::cachedScript): prepareScript() is the only place that should load scripts. This accessor lets the parsers listen for when loads finish.
(WebCore::ScriptElement::isParserInserted): Added.
* dom/XMLDocumentParserLibxml2.cpp:
(WebCore::XMLDocumentParser::endElementNs): Should behave the same. Offloads much of its work to prepareScript().
* dom/XMLDocumentParserQt.cpp:
(WebCore::XMLDocumentParser::parseEndElement): Identical to libxml2 changes.
* html/HTMLScriptElement.cpp:
(WebCore::HTMLScriptElement::insertedIntoDocument): No longer needs url.
(WebCore::HTMLScriptElement::hasSourceAttribute): Added.
* html/HTMLScriptElement.h:
* html/parser/HTMLScriptRunner.cpp:
(WebCore::HTMLScriptRunner::requestPendingScript): Requesting scripts offloaded to ScriptElement.
(WebCore::HTMLScriptRunner::runScript): Should behave the same. Offloads much of its work to prepareScript().
* svg/SVGScriptElement.cpp:
(WebCore::SVGScriptElement::svgAttributeChanged): New ScriptElement function names.
(WebCore::SVGScriptElement::insertedIntoDocument): No longer needs url.
(WebCore::SVGScriptElement::finishParsingChildren): ScriptElement::finishParsingChildren is gone.
(WebCore::SVGScriptElement::hasSourceAttribute): Added.
(WebCore::SVGScriptElement::dispatchLoadEvent): New ScriptElement function names.
* svg/SVGScriptElement.h:
2011-02-19 Marc-Antoine Ruel <maruel@chromium.org>
Reviewed by James Robinson.
......
......@@ -51,13 +51,16 @@
namespace WebCore {
ScriptElement::ScriptElement(Element* element, bool wasInsertedByParser, bool wasAlreadyStarted)
ScriptElement::ScriptElement(Element* element, bool parserInserted, bool alreadyStarted)
: m_element(element)
, m_cachedScript(0)
, m_wasInsertedByParser(wasInsertedByParser)
, m_parserInserted(parserInserted)
, m_isExternalScript(false)
, m_wasAlreadyStarted(wasAlreadyStarted)
, m_alreadyStarted(alreadyStarted)
, m_haveFiredLoad(false)
, m_willBeParserExecuted(false)
, m_readyToBeParserExecuted(false)
, m_willExecuteWhenDocumentFinishedParsing(false)
{
ASSERT(m_element);
}
......@@ -67,22 +70,10 @@ ScriptElement::~ScriptElement()
stopLoadRequest();
}
void ScriptElement::insertedIntoDocument(const String& sourceUrl)
void ScriptElement::insertedIntoDocument()
{
if (wasInsertedByParser() && !isAsynchronous())
return;
// http://www.whatwg.org/specs/web-apps/current-work/#script
if (!sourceUrl.isEmpty()) {
requestScript(sourceUrl);
return;
}
// If there's an empty script node, we shouldn't evaluate the script
// because if a script is inserted afterwards (by setting text or innerText)
// it should be evaluated, and evaluateScript only evaluates a script once.
evaluateScript(ScriptSourceCode(scriptContent(), element()->document()->url())); // FIXME: Provide a real starting line number here.
if (!m_parserInserted)
prepareScript(); // FIXME: Provide a real starting line number here.
}
void ScriptElement::removedFromDocument()
......@@ -93,22 +84,8 @@ void ScriptElement::removedFromDocument()
void ScriptElement::childrenChanged()
{
if (wasInsertedByParser())
return;
// If a node is inserted as a child of the script element
// and the script element has been inserted in the document
// we evaluate the script.
if (m_element->inDocument() && m_element->firstChild())
evaluateScript(ScriptSourceCode(scriptContent(), m_element->document()->url())); // FIXME: Provide a real starting line number here
}
void ScriptElement::finishParsingChildren(const String& sourceUrl)
{
// The parser just reached </script>. If we have no src and no text,
// allow dynamic loading later.
if (sourceUrl.isEmpty() && scriptContent().isEmpty())
m_wasInsertedByParser = false;
if (!m_parserInserted && m_element->inDocument())
prepareScript(); // FIXME: Provide a real starting line number here.
}
void ScriptElement::handleSourceAttribute(const String& sourceUrl)
......@@ -116,12 +93,19 @@ void ScriptElement::handleSourceAttribute(const String& sourceUrl)
if (ignoresLoadRequest() || sourceUrl.isEmpty())
return;
requestScript(sourceUrl);
prepareScript(); // FIXME: Provide a real starting line number here.
}
// Helper function
static bool isSupportedJavaScriptLanguage(const String& language)
static bool isLegacySupportedJavaScriptLanguage(const String& language)
{
// Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only javascript1.1 - javascript1.3.
// Mozilla 1.8 and WinIE 7 both accept javascript and livescript.
// WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't.
// Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace.
// 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()) {
......@@ -137,58 +121,133 @@ static bool isSupportedJavaScriptLanguage(const String& language)
languages.add("javascript1.7");
languages.add("livescript");
languages.add("ecmascript");
languages.add("jscript");
languages.add("jscript");
}
return languages.contains(language);
}
void ScriptElement::requestScript(const String& sourceUrl)
bool ScriptElement::isScriptTypeSupported(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;
}
// http://dev.w3.org/html5/spec/Overview.html#prepare-a-script
bool ScriptElement::prepareScript(const TextPosition1& scriptStartPosition, LegacyTypeSupport supportLegacyTypes)
{
// FIXME: Eventually we'd like to evaluate scripts which are inserted into a
if (m_alreadyStarted)
return false;
bool wasParserInserted;
if (m_parserInserted) {
wasParserInserted = true;
m_parserInserted = false;
} else
wasParserInserted = false;
// FIXME: HTML5 spec says we should set forceAsync.
// FIXME: HTML5 spec says we should check that all children are either comments or empty text nodes.
if (!hasSourceAttribute() && !m_element->firstChild())
return false;
if (!m_element->inDocument())
return false;
if (!isScriptTypeSupported(supportLegacyTypes))
return false;
if (wasParserInserted)
m_parserInserted = true;
m_alreadyStarted = true;
// FIXME: If script is parser inserted, verify it's still in the original document.
// FIXME: Eventually we'd like to evaluate scripts which are inserted into a
// viewless document but this'll do for now.
// See http://bugs.webkit.org/show_bug.cgi?id=5727
if (!m_element->document()->frame())
return;
return false;
if (!m_element->document()->frame()->script()->canExecuteScripts(AboutToExecuteScript))
return false;
if (!isScriptForEventSupported())
return false;
if (!charsetAttributeValue().isEmpty())
m_characterEncoding = charsetAttributeValue();
else
m_characterEncoding = m_element->document()->charset();
if (hasSourceAttribute())
if (!requestScript(sourceAttributeValue()))
return false;
if (hasSourceAttribute() && deferAttributeValue() && m_parserInserted && !asyncAttributeValue()) {
m_willExecuteWhenDocumentFinishedParsing = true;
m_willBeParserExecuted = true;
} else if (hasSourceAttribute() && m_parserInserted && !asyncAttributeValue())
m_willBeParserExecuted = true;
else if (!hasSourceAttribute() && m_parserInserted && !m_element->document()->haveStylesheetsLoaded()) {
m_willBeParserExecuted = true;
m_readyToBeParserExecuted = true;
} else if (hasSourceAttribute())
m_cachedScript->addClient(this);
else
executeScript(ScriptSourceCode(scriptContent(), m_element->document()->url(), scriptStartPosition));
return true;
}
bool ScriptElement::requestScript(const String& sourceUrl)
{
if (!m_element->document()->contentSecurityPolicy()->canLoadExternalScriptFromSrc(sourceUrl))
return false;
RefPtr<Document> originalDocument = m_element->document();
if (!m_element->dispatchBeforeLoadEvent(sourceUrl))
return;
return false;
if (!m_element->inDocument() || m_element->document() != originalDocument)
return;
return false;
ASSERT(!m_cachedScript);
// FIXME: If sourceUrl is empty, we should dispatchErrorEvent().
m_cachedScript = m_element->document()->cachedResourceLoader()->requestScript(sourceUrl, scriptCharset());
m_isExternalScript = true;
// m_wasInsertedByParser is never reset - always resied at the initial value set while parsing.
// m_wasAlreadyStarted is left untouched as well to avoid script reexecution, if a <script> element
// is removed and reappended to the document.
m_haveFiredLoad = false;
if (m_cachedScript) {
m_cachedScript->addClient(this);
return;
}
if (m_cachedScript)
return true;
dispatchErrorEvent();
return false;
}
void ScriptElement::evaluateScript(const ScriptSourceCode& sourceCode)
void ScriptElement::executeScript(const ScriptSourceCode& sourceCode)
{
if (wasAlreadyStarted() || sourceCode.isEmpty() || !shouldExecuteAsJavaScript())
ASSERT(m_alreadyStarted);
if (sourceCode.isEmpty())
return;
RefPtr<Document> document = m_element->document();
ASSERT(document);
if (Frame* frame = document->frame()) {
if (!frame->script()->canExecuteScripts(AboutToExecuteScript))
return;
m_wasAlreadyStarted = true;
// http://www.whatwg.org/specs/web-apps/current-work/#script
{
IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(m_isExternalScript ? document.get() : 0);
// Create a script from the script element node, using the script
......@@ -201,36 +260,23 @@ void ScriptElement::evaluateScript(const ScriptSourceCode& sourceCode)
}
}
void ScriptElement::executeScript(const ScriptSourceCode& sourceCode)
{
if (wasAlreadyStarted() || sourceCode.isEmpty())
return;
RefPtr<Document> document = m_element->document();
ASSERT(document);
Frame* frame = document->frame();
if (!frame)
return;
m_wasAlreadyStarted = true;
frame->script()->executeScript(sourceCode);
}
void ScriptElement::stopLoadRequest()
{
if (m_cachedScript) {
m_cachedScript->removeClient(this);
if (!m_willBeParserExecuted)
m_cachedScript->removeClient(this);
m_cachedScript = 0;
}
}
void ScriptElement::execute(CachedScript* cachedScript)
{
ASSERT(!m_willBeParserExecuted);
ASSERT(cachedScript);
if (cachedScript->errorOccurred())
dispatchErrorEvent();
else {
evaluateScript(ScriptSourceCode(cachedScript));
executeScript(ScriptSourceCode(cachedScript));
dispatchLoadEvent();
}
cachedScript->removeClient(this);
......@@ -238,6 +284,7 @@ void ScriptElement::execute(CachedScript* cachedScript)
void ScriptElement::notifyFinished(CachedResource* o)
{
ASSERT(!m_willBeParserExecuted);
ASSERT_UNUSED(o, o == m_cachedScript);
m_element->document()->asyncScriptRunner()->executeScriptSoon(this, m_cachedScript);
m_cachedScript = 0;
......@@ -245,59 +292,25 @@ void ScriptElement::notifyFinished(CachedResource* o)
bool ScriptElement::ignoresLoadRequest() const
{
return wasAlreadyStarted() || m_isExternalScript || wasInsertedByParser() || !m_element->inDocument();
return m_alreadyStarted || m_isExternalScript || m_parserInserted || !m_element->inDocument();
}
bool ScriptElement::shouldExecuteAsJavaScript() const
bool ScriptElement::isScriptForEventSupported() const
{
/*
Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only javascript1.1 - javascript1.3.
Mozilla 1.8 and WinIE 7 both accept javascript and livescript.
WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't.
Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace.
We want to accept all the values that either of these browsers accept, but not other values.
FIXME: Is this HTML5 compliant?
*/
String type = typeAttributeValue();
if (!type.isEmpty()) {
if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace().lower()))
return false;
} else {
String language = languageAttributeValue();
if (!language.isEmpty() && !isSupportedJavaScriptLanguage(language))
return false;
}
// No type or language is specified, so we assume the script to be JavaScript.
String forAttribute = forAttributeValue();
String eventAttribute = eventAttributeValue();
if (!forAttribute.isEmpty() && !eventAttribute.isEmpty()) {
String forAttribute = forAttributeValue();
if (!eventAttribute.isEmpty() && !forAttribute.isEmpty()) {
forAttribute = forAttribute.stripWhiteSpace();
if (!equalIgnoringCase(forAttribute, "window"))
return false;
eventAttribute = eventAttribute.stripWhiteSpace();
if (!equalIgnoringCase(eventAttribute, "onload") && !equalIgnoringCase(eventAttribute, "onload()"))
return false;
}
return true;
}
String ScriptElement::scriptCharset() const
{
// First we try to get encoding from charset attribute.
String charset = charsetAttributeValue().stripWhiteSpace();
// If charset has not been declared in script tag, fall back to frame encoding.
if (charset.isEmpty())
charset = m_element->document()->charset();
return charset;
}
String ScriptElement::scriptContent() const
{
Vector<UChar> val;
......@@ -325,20 +338,6 @@ String ScriptElement::scriptContent() const
return String::adopt(val);
}
bool ScriptElement::isAsynchronous() const
{
// Only external scripts may be asynchronous.
// See: http://dev.w3.org/html5/spec/Overview.html#attr-script-async
return !sourceAttributeValue().isEmpty() && asyncAttributeValue();
}
bool ScriptElement::isDeferred() const
{
// Only external scripts may be deferred and async trumps defer to allow for backward compatibility.
// See: http://dev.w3.org/html5/spec/Overview.html#attr-script-defer
return !sourceAttributeValue().isEmpty() && !asyncAttributeValue() && deferAttributeValue();
}
ScriptElement* toScriptElement(Element* element)
{
if (element->isHTMLElement() && element->hasTagName(HTMLNames::scriptTag))
......
......@@ -23,6 +23,7 @@
#include "CachedResourceClient.h"
#include "CachedResourceHandle.h"
#include <wtf/text/TextPosition.h>
namespace WebCore {
......@@ -36,47 +37,49 @@ class ScriptElement : private CachedResourceClient {
public:
ScriptElement(Element*, bool createdByParser, bool isEvaluated);
virtual ~ScriptElement();
Element* element() const { return m_element; }
// A charset for loading the script (may be overridden by HTTP headers or a BOM).
String scriptCharset() const;
enum LegacyTypeSupport { DisallowLegacyTypeInTypeAttribute, AllowLegacyTypeInTypeAttribute };
bool prepareScript(const TextPosition1& scriptStartPosition = TextPosition1::minimumPosition(), LegacyTypeSupport = DisallowLegacyTypeInTypeAttribute);
String scriptCharset() const { return m_characterEncoding; }
String scriptContent() const;
bool shouldExecuteAsJavaScript() const;
void executeScript(const ScriptSourceCode&);
void execute(CachedScript*);
// XML parser calls these
virtual String sourceAttributeValue() const = 0;
virtual void dispatchLoadEvent() = 0;
virtual void dispatchErrorEvent() = 0;
bool isScriptTypeSupported(LegacyTypeSupport) const;
bool haveFiredLoadEvent() const { return m_haveFiredLoad; }
bool willBeParserExecuted() const { return m_willBeParserExecuted; }
bool readyToBeParserExecuted() const { return m_readyToBeParserExecuted; }
bool willExecuteWhenDocumentFinishedParsing() const { return m_willExecuteWhenDocumentFinishedParsing; }
CachedResourceHandle<CachedScript> cachedScript() { return m_cachedScript; }
protected:
void setHaveFiredLoadEvent(bool haveFiredLoad) { m_haveFiredLoad = haveFiredLoad; }
bool wasInsertedByParser() const { return m_wasInsertedByParser; }
bool wasAlreadyStarted() const { return m_wasAlreadyStarted; }
bool isParserInserted() const { return m_parserInserted; }
bool alreadyStarted() const { return m_alreadyStarted; }
// Helper functions used by our parent classes.
void insertedIntoDocument(const String& sourceUrl);
void insertedIntoDocument();
void removedFromDocument();
void childrenChanged();
void finishParsingChildren(const String& sourceUrl);
void handleSourceAttribute(const String& sourceUrl);
private:
bool ignoresLoadRequest() const;
bool isAsynchronous() const;
bool isDeferred() const;
bool isScriptForEventSupported() const;
void requestScript(const String& sourceUrl);
void evaluateScript(const ScriptSourceCode&);
bool requestScript(const String& sourceUrl);
void stopLoadRequest();
virtual void notifyFinished(CachedResource*);
virtual String sourceAttributeValue() const = 0;
virtual String charsetAttributeValue() const = 0;
virtual String typeAttributeValue() const = 0;