Changeset 79114 in webkit


Ignore:
Timestamp:
Feb 19, 2011 1:39:06 AM (13 years ago)
Author:
simonjam@chromium.org
Message:

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:
Location:
trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r79111 r79114  
     12011-02-19  James Simonsen  <simonjam@chromium.org>
     2
     3        Reviewed by Adam Barth.
     4
     5        Make ScriptElement match the HTML5 spec
     6        https://bugs.webkit.org/show_bug.cgi?id=54676
     7
     8        This implements the "prepare a script" section of the HTML5 spec in ScriptElement::prepareScript().
     9        http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#prepare-a-script
     10
     11        There are a couple of things missing from the spec that would be new functionality. These will be added later.
     12        - Support for async=false
     13        - Empty src attribute should dispatch an error.
     14
     15        There are a couple of slight behavioral changes to match the spec.
     16        - An XHTML script that is loaded then copied will not fire load on the copy.
     17        - If onbeforeload moves the script to a different document, then it will not try to execute again because wasAlreadyStarted is true.
     18
     19        The parsers were updated to use the new API, but not rewritten to look like the spec. That will be done separately.
     20
     21        * 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.
     22        * 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.
     23
    1242011-02-19  Andrew Wilson  <atwilson@chromium.org>
    225
  • trunk/LayoutTests/fast/dom/HTMLScriptElement/move-in-beforeload.html

    r74752 r79114  
    99<script>
    1010if (window.layoutTestController) {
    11     layoutTestController.waitUntilDone();
    1211    layoutTestController.dumpAsText();
    1312}
     
    1918        var s = document.body.removeChild(document.getElementsByTagName("script")[2]);
    2019        document.getElementsByTagName("iframe")[0].contentDocument.body.appendChild(s);
     20        testPassed("");
    2121    }, false);
    2222    s.addEventListener("error", function() {
    23         testFailed("error event should not fire.")
     23        testFailed("error event should not fire.");
    2424    }, false);
    2525    s.addEventListener("load", function() {
    26         testPassed("");
    27         if (window.layoutTestController)
    28             layoutTestController.notifyDone();
     26        testFailed("script should not be loaded");
    2927    }, false);
    3028    s.src = "external.js";
  • trunk/LayoutTests/fast/dom/script-clone-rerun-src.xhtml

    r72638 r79114  
    1212    layoutTestController.waitUntilDone();
    1313}
    14 
    15 var firstLoad = true;
    16 
    17 function loaded() {
    18     if (firstLoad) {
    19         firstLoad = false;
    20         return;
    21     }
    22 
    23     result = 'PASS';
    24     if (i == 2)
    25       result = 'FAIL: script ran twice';
    26     else if (i > 2)
    27       result = 'FAIL: script ran ' + i + ' times';
    28     document.body.appendChild(document.createTextNode(result));
    29 
    30     layoutTestController.notifyDone();
    31 }
    32 
    3314</script>
    3415</head>
    3516<body>
    3617<p>This test runs a script that clones and inserts its script element. The script should run exactly once and print PASS:</p>
    37 <div><script src="resources/script-clone-rerun-src.js" onload="loaded()"></script></div>
     18<div><script src="resources/script-clone-rerun-src.js"></script></div>
    3819<script>
    3920
     
    4122var div = script.parentNode;
    4223div.removeChild(script);
    43 div.appendChild(script.cloneNode(true));
     24var clone = script.cloneNode(true);
     25clone.addEventListener("load", function() { document.body.appendChild(document.createTextNode("FAIL: load event fired on clone")); }, false);
     26div.appendChild(clone);
     27
     28setTimeout(function() {
     29    result = 'FAIL: script never ran';
     30    if (i == 1)
     31      result = 'PASS';
     32    else if (i > 1)
     33      result = 'FAIL: script ran ' + i + ' times';
     34    document.body.appendChild(document.createTextNode(result));
     35    if (window.layoutTestController)
     36        layoutTestController.notifyDone();
     37    }, 0);
    4438
    4539</script>
  • trunk/Source/WebCore/ChangeLog

    r79112 r79114  
     12011-02-19  James Simonsen  <simonjam@chromium.org>
     2
     3        Reviewed by Adam Barth.
     4
     5        Make ScriptElement match the HTML5 spec
     6        https://bugs.webkit.org/show_bug.cgi?id=54676
     7
     8        This implements the "prepare a script" section of the HTML5 spec in ScriptElement::prepareScript().
     9        http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#prepare-a-script
     10
     11        There are a couple of things missing from the spec that would be new functionality. These will be added later.
     12        - Support for async=false
     13        - Empty src attribute should dispatch an error.
     14
     15        There are a couple of slight behavioral changes to match the spec.
     16        - An XHTML script that is loaded then copied will not fire load on the copy.
     17        - If onbeforeload moves the script to a different document, then it will not try to execute again because wasAlreadyStarted is true.
     18
     19        The parsers were updated to use the new API, but not rewritten to look like the spec. That will be done separately.
     20
     21        Test: All existing tests.
     22
     23        * dom/ScriptElement.cpp: Rewritten to match HTML5 spec.
     24        (WebCore::ScriptElement::ScriptElement):
     25        (WebCore::ScriptElement::insertedIntoDocument): Logic moved to prepareScript.
     26        (WebCore::ScriptElement::childrenChanged): Logic moved to prepareScript.
     27        (WebCore::ScriptElement::handleSourceAttribute): Logic moved to prepareScript.
     28        (WebCore::isLegacySupportedJavaScriptLanguage): Added to support old script types in layout tests.
     29        (WebCore::ScriptElement::isScriptTypeSupported): Derived from old shouldExecuteAsJavaScript().
     30        (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.
     31        (WebCore::ScriptElement::requestScript): Most logic moved to prepareScript. Check security settings here.
     32        (WebCore::ScriptElement::executeScript): Combined evaluateScript() and executeScript() from old code. Logic moved to prepareScript.
     33        (WebCore::ScriptElement::stopLoadRequest): Ignore parser executed scripts.
     34        (WebCore::ScriptElement::execute): Renamed executeScript.
     35        (WebCore::ScriptElement::notifyFinished): We should only listen for non-parser executed scripts.
     36        (WebCore::ScriptElement::ignoresLoadRequest): New variable names.
     37        (WebCore::ScriptElement::childrenAreCommentsOrEmptyText): Added for HTML5 compliance.
     38        (WebCore::ScriptElement::scriptCharset): Use HTML5 variables.
     39        * dom/ScriptElement.h:
     40        (WebCore::ScriptElement::willBeParserExecuted): Added.
     41        (WebCore::ScriptElement::readyToBeParserExecuted): Added.
     42        (WebCore::ScriptElement::willExecuteWhenDocumentFinishedParsing): Added.
     43        (WebCore::ScriptElement::cachedScript): prepareScript() is the only place that should load scripts. This accessor lets the parsers listen for when loads finish.
     44        (WebCore::ScriptElement::isParserInserted): Added.
     45        * dom/XMLDocumentParserLibxml2.cpp:
     46        (WebCore::XMLDocumentParser::endElementNs): Should behave the same. Offloads much of its work to prepareScript().
     47        * dom/XMLDocumentParserQt.cpp:
     48        (WebCore::XMLDocumentParser::parseEndElement): Identical to libxml2 changes.
     49        * html/HTMLScriptElement.cpp:
     50        (WebCore::HTMLScriptElement::insertedIntoDocument): No longer needs url.
     51        (WebCore::HTMLScriptElement::hasSourceAttribute): Added.
     52        * html/HTMLScriptElement.h:
     53        * html/parser/HTMLScriptRunner.cpp:
     54        (WebCore::HTMLScriptRunner::requestPendingScript): Requesting scripts offloaded to ScriptElement.
     55        (WebCore::HTMLScriptRunner::runScript): Should behave the same. Offloads much of its work to prepareScript().
     56        * svg/SVGScriptElement.cpp:
     57        (WebCore::SVGScriptElement::svgAttributeChanged): New ScriptElement function names.
     58        (WebCore::SVGScriptElement::insertedIntoDocument): No longer needs url.
     59        (WebCore::SVGScriptElement::finishParsingChildren): ScriptElement::finishParsingChildren is gone.
     60        (WebCore::SVGScriptElement::hasSourceAttribute): Added.
     61        (WebCore::SVGScriptElement::dispatchLoadEvent): New ScriptElement function names.
     62        * svg/SVGScriptElement.h:
     63
    1642011-02-19  Marc-Antoine Ruel  <maruel@chromium.org>
    265
  • trunk/Source/WebCore/dom/ScriptElement.cpp

    r77750 r79114  
    5252namespace WebCore {
    5353
    54 ScriptElement::ScriptElement(Element* element, bool wasInsertedByParser, bool wasAlreadyStarted)
     54ScriptElement::ScriptElement(Element* element, bool parserInserted, bool alreadyStarted)
    5555    : m_element(element)
    5656    , m_cachedScript(0)
    57     , m_wasInsertedByParser(wasInsertedByParser)
     57    , m_parserInserted(parserInserted)
    5858    , m_isExternalScript(false)
    59     , m_wasAlreadyStarted(wasAlreadyStarted)
     59    , m_alreadyStarted(alreadyStarted)
    6060    , m_haveFiredLoad(false)
     61    , m_willBeParserExecuted(false)
     62    , m_readyToBeParserExecuted(false)
     63    , m_willExecuteWhenDocumentFinishedParsing(false)
    6164{
    6265    ASSERT(m_element);
     
    6871}
    6972
    70 void ScriptElement::insertedIntoDocument(const String& sourceUrl)
    71 {
    72     if (wasInsertedByParser() && !isAsynchronous())
    73         return;
    74 
    75     // http://www.whatwg.org/specs/web-apps/current-work/#script
    76 
    77     if (!sourceUrl.isEmpty()) {
    78         requestScript(sourceUrl);
    79         return;
    80     }
    81 
    82     // If there's an empty script node, we shouldn't evaluate the script
    83     // because if a script is inserted afterwards (by setting text or innerText)
    84     // it should be evaluated, and evaluateScript only evaluates a script once.
    85     evaluateScript(ScriptSourceCode(scriptContent(), element()->document()->url())); // FIXME: Provide a real starting line number here.
     73void ScriptElement::insertedIntoDocument()
     74{
     75    if (!m_parserInserted)
     76        prepareScript(); // FIXME: Provide a real starting line number here.
    8677}
    8778
     
    9485void ScriptElement::childrenChanged()
    9586{
    96     if (wasInsertedByParser())
    97         return;
    98 
    99     // If a node is inserted as a child of the script element
    100     // and the script element has been inserted in the document
    101     // we evaluate the script.
    102     if (m_element->inDocument() && m_element->firstChild())
    103         evaluateScript(ScriptSourceCode(scriptContent(), m_element->document()->url())); // FIXME: Provide a real starting line number here
    104 }
    105 
    106 void ScriptElement::finishParsingChildren(const String& sourceUrl)
    107 {
    108     // The parser just reached </script>. If we have no src and no text,
    109     // allow dynamic loading later.
    110     if (sourceUrl.isEmpty() && scriptContent().isEmpty())
    111         m_wasInsertedByParser = false;
     87    if (!m_parserInserted && m_element->inDocument())
     88        prepareScript(); // FIXME: Provide a real starting line number here.
    11289}
    11390
     
    11794        return;
    11895
    119     requestScript(sourceUrl);
     96    prepareScript(); // FIXME: Provide a real starting line number here.
    12097}
    12198
    12299// Helper function
    123 static bool isSupportedJavaScriptLanguage(const String& language)
    124 {
     100static bool isLegacySupportedJavaScriptLanguage(const String& language)
     101{
     102    // Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only javascript1.1 - javascript1.3.
     103    // Mozilla 1.8 and WinIE 7 both accept javascript and livescript.
     104    // WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't.
     105    // Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace.
     106    // We want to accept all the values that either of these browsers accept, but not other values.
     107
     108    // FIXME: This function is not HTML5 compliant. These belong in the MIME registry as "text/javascript<version>" entries.
    125109    typedef HashSet<String, CaseFoldingHash> LanguageSet;
    126110    DEFINE_STATIC_LOCAL(LanguageSet, languages, ());
     
    138122        languages.add("livescript");
    139123        languages.add("ecmascript");
    140         languages.add("jscript");               
     124        languages.add("jscript");
    141125    }
    142126
     
    144128}
    145129
    146 void ScriptElement::requestScript(const String& sourceUrl)
    147 {
    148     // FIXME: Eventually we'd like to evaluate scripts which are inserted into a
     130bool ScriptElement::isScriptTypeSupported(LegacyTypeSupport supportLegacyTypes) const
     131{
     132    // FIXME: isLegacySupportedJavaScriptLanguage() is not valid HTML5. It is used here to maintain backwards compatibility with existing layout tests. The specific violations are:
     133    // - Allowing type=javascript. type= should only support MIME types, such as text/javascript.
     134    // - Allowing a different set of languages for language= and type=. language= supports Javascript 1.1 and 1.4-1.6, but type= does not.
     135
     136    String type = typeAttributeValue();
     137    String language = languageAttributeValue();
     138    if (type.isEmpty() && language.isEmpty())
     139        return true; // Assume text/javascript.
     140    if (type.isEmpty()) {
     141        type = "text/" + language.lower();
     142        if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type) || isLegacySupportedJavaScriptLanguage(language))
     143            return true;
     144    } else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace().lower()) || (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type)))
     145        return true;
     146    return false;
     147}
     148
     149// http://dev.w3.org/html5/spec/Overview.html#prepare-a-script
     150bool ScriptElement::prepareScript(const TextPosition1& scriptStartPosition, LegacyTypeSupport supportLegacyTypes)
     151{
     152    if (m_alreadyStarted)
     153        return false;
     154
     155    bool wasParserInserted;
     156    if (m_parserInserted) {
     157        wasParserInserted = true;
     158        m_parserInserted = false;
     159    } else
     160        wasParserInserted = false;
     161
     162    // FIXME: HTML5 spec says we should set forceAsync.
     163
     164    // FIXME: HTML5 spec says we should check that all children are either comments or empty text nodes.
     165    if (!hasSourceAttribute() && !m_element->firstChild())
     166        return false;
     167
     168    if (!m_element->inDocument())
     169        return false;
     170
     171    if (!isScriptTypeSupported(supportLegacyTypes))
     172        return false;
     173
     174    if (wasParserInserted)
     175        m_parserInserted = true;
     176
     177    m_alreadyStarted = true;
     178
     179    // FIXME: If script is parser inserted, verify it's still in the original document.
     180
     181    // FIXME: Eventually we'd like to evaluate scripts which are inserted into a
    149182    // viewless document but this'll do for now.
    150183    // See http://bugs.webkit.org/show_bug.cgi?id=5727
    151184    if (!m_element->document()->frame())
    152         return;
     185        return false;
     186
     187    if (!m_element->document()->frame()->script()->canExecuteScripts(AboutToExecuteScript))
     188        return false;
     189
     190    if (!isScriptForEventSupported())
     191        return false;
     192
     193    if (!charsetAttributeValue().isEmpty())
     194        m_characterEncoding = charsetAttributeValue();
     195    else
     196        m_characterEncoding = m_element->document()->charset();
     197
     198    if (hasSourceAttribute())
     199        if (!requestScript(sourceAttributeValue()))
     200            return false;
     201
     202    if (hasSourceAttribute() && deferAttributeValue() && m_parserInserted && !asyncAttributeValue()) {
     203        m_willExecuteWhenDocumentFinishedParsing = true;
     204        m_willBeParserExecuted = true;
     205    } else if (hasSourceAttribute() && m_parserInserted && !asyncAttributeValue())
     206        m_willBeParserExecuted = true;
     207    else if (!hasSourceAttribute() && m_parserInserted && !m_element->document()->haveStylesheetsLoaded()) {
     208        m_willBeParserExecuted = true;
     209        m_readyToBeParserExecuted = true;
     210    } else if (hasSourceAttribute())
     211        m_cachedScript->addClient(this);
     212    else
     213        executeScript(ScriptSourceCode(scriptContent(), m_element->document()->url(), scriptStartPosition));
     214
     215    return true;
     216}
     217
     218bool ScriptElement::requestScript(const String& sourceUrl)
     219{
     220    if (!m_element->document()->contentSecurityPolicy()->canLoadExternalScriptFromSrc(sourceUrl))
     221        return false;
    153222
    154223    RefPtr<Document> originalDocument = m_element->document();
    155224    if (!m_element->dispatchBeforeLoadEvent(sourceUrl))
    156         return;
     225        return false;
    157226    if (!m_element->inDocument() || m_element->document() != originalDocument)
    158         return;
     227        return false;
    159228
    160229    ASSERT(!m_cachedScript);
     230    // FIXME: If sourceUrl is empty, we should dispatchErrorEvent().
    161231    m_cachedScript = m_element->document()->cachedResourceLoader()->requestScript(sourceUrl, scriptCharset());
    162232    m_isExternalScript = true;
    163233
    164     // m_wasInsertedByParser is never reset - always resied at the initial value set while parsing.
    165     // m_wasAlreadyStarted is left untouched as well to avoid script reexecution, if a <script> element
    166     // is removed and reappended to the document.
    167     m_haveFiredLoad = false;
    168 
    169     if (m_cachedScript) {
    170         m_cachedScript->addClient(this);
    171         return;
    172     }
     234    if (m_cachedScript)
     235        return true;
    173236
    174237    dispatchErrorEvent();
    175 }
    176 
    177 void ScriptElement::evaluateScript(const ScriptSourceCode& sourceCode)
    178 {
    179     if (wasAlreadyStarted() || sourceCode.isEmpty() || !shouldExecuteAsJavaScript())
     238    return false;
     239}
     240
     241void ScriptElement::executeScript(const ScriptSourceCode& sourceCode)
     242{
     243    ASSERT(m_alreadyStarted);
     244
     245    if (sourceCode.isEmpty())
    180246        return;
    181247
     
    183249    ASSERT(document);
    184250    if (Frame* frame = document->frame()) {
    185         if (!frame->script()->canExecuteScripts(AboutToExecuteScript))
    186             return;
    187 
    188         m_wasAlreadyStarted = true;
    189 
    190         // http://www.whatwg.org/specs/web-apps/current-work/#script
    191 
    192251        {
    193252            IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(m_isExternalScript ? document.get() : 0);
     
    202261}
    203262
    204 void ScriptElement::executeScript(const ScriptSourceCode& sourceCode)
    205 {
    206     if (wasAlreadyStarted() || sourceCode.isEmpty())
    207         return;
    208     RefPtr<Document> document = m_element->document();
    209     ASSERT(document);
    210     Frame* frame = document->frame();
    211     if (!frame)
    212         return;
    213 
    214     m_wasAlreadyStarted = true;
    215 
    216     frame->script()->executeScript(sourceCode);
    217 }
    218 
    219263void ScriptElement::stopLoadRequest()
    220264{
    221265    if (m_cachedScript) {
    222         m_cachedScript->removeClient(this);
     266        if (!m_willBeParserExecuted)
     267            m_cachedScript->removeClient(this);
    223268        m_cachedScript = 0;
    224269    }
     
    227272void ScriptElement::execute(CachedScript* cachedScript)
    228273{
     274    ASSERT(!m_willBeParserExecuted);
    229275    ASSERT(cachedScript);
    230276    if (cachedScript->errorOccurred())
    231277        dispatchErrorEvent();
    232278    else {
    233         evaluateScript(ScriptSourceCode(cachedScript));
     279        executeScript(ScriptSourceCode(cachedScript));
    234280        dispatchLoadEvent();
    235281    }
     
    239285void ScriptElement::notifyFinished(CachedResource* o)
    240286{
     287    ASSERT(!m_willBeParserExecuted);
    241288    ASSERT_UNUSED(o, o == m_cachedScript);
    242289    m_element->document()->asyncScriptRunner()->executeScriptSoon(this, m_cachedScript);
     
    246293bool ScriptElement::ignoresLoadRequest() const
    247294{
    248     return wasAlreadyStarted() || m_isExternalScript || wasInsertedByParser() || !m_element->inDocument();
    249 }
    250 
    251 bool ScriptElement::shouldExecuteAsJavaScript() const
    252 {
    253     /*
    254          Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only javascript1.1 - javascript1.3.
    255          Mozilla 1.8 and WinIE 7 both accept javascript and livescript.
    256          WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't.
    257          Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace.
    258          We want to accept all the values that either of these browsers accept, but not other values.
    259      
    260          FIXME: Is this HTML5 compliant?
    261      */
    262     String type = typeAttributeValue();
    263     if (!type.isEmpty()) {
    264         if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace().lower()))
    265             return false;
    266     } else {
    267         String language = languageAttributeValue();
    268         if (!language.isEmpty() && !isSupportedJavaScriptLanguage(language))
    269             return false;
    270     }   
    271 
    272     // No type or language is specified, so we assume the script to be JavaScript.
    273 
     295    return m_alreadyStarted || m_isExternalScript || m_parserInserted || !m_element->inDocument();
     296}
     297
     298bool ScriptElement::isScriptForEventSupported() const
     299{
     300    String eventAttribute = eventAttributeValue();
    274301    String forAttribute = forAttributeValue();
    275     String eventAttribute = eventAttributeValue();
    276     if (!forAttribute.isEmpty() && !eventAttribute.isEmpty()) {
     302    if (!eventAttribute.isEmpty() && !forAttribute.isEmpty()) {
    277303        forAttribute = forAttribute.stripWhiteSpace();
    278304        if (!equalIgnoringCase(forAttribute, "window"))
    279305            return false;
    280            
     306
    281307        eventAttribute = eventAttribute.stripWhiteSpace();
    282308        if (!equalIgnoringCase(eventAttribute, "onload") && !equalIgnoringCase(eventAttribute, "onload()"))
    283309            return false;
    284310    }
    285    
    286311    return true;
    287 }
    288 
    289 String ScriptElement::scriptCharset() const
    290 {
    291     // First we try to get encoding from charset attribute.
    292     String charset = charsetAttributeValue().stripWhiteSpace();
    293 
    294     // If charset has not been declared in script tag, fall back to frame encoding.
    295     if (charset.isEmpty())
    296         charset = m_element->document()->charset();
    297 
    298     return charset;
    299312}
    300313
     
    326339}
    327340
    328 bool ScriptElement::isAsynchronous() const
    329 {
    330     // Only external scripts may be asynchronous.
    331     // See: http://dev.w3.org/html5/spec/Overview.html#attr-script-async
    332     return !sourceAttributeValue().isEmpty() && asyncAttributeValue();
    333 }
    334 
    335 bool ScriptElement::isDeferred() const
    336 {
    337     // Only external scripts may be deferred and async trumps defer to allow for backward compatibility.
    338     // See: http://dev.w3.org/html5/spec/Overview.html#attr-script-defer
    339     return !sourceAttributeValue().isEmpty() && !asyncAttributeValue() && deferAttributeValue();
    340 }
    341 
    342341ScriptElement* toScriptElement(Element* element)
    343342{
  • trunk/Source/WebCore/dom/ScriptElement.h

    r76248 r79114  
    2424#include "CachedResourceClient.h"
    2525#include "CachedResourceHandle.h"
     26#include <wtf/text/TextPosition.h>
    2627
    2728namespace WebCore {
     
    3738    ScriptElement(Element*, bool createdByParser, bool isEvaluated);
    3839    virtual ~ScriptElement();
    39    
     40
    4041    Element* element() const { return m_element; }
    4142
    42     // A charset for loading the script (may be overridden by HTTP headers or a BOM).
    43     String scriptCharset() const;
     43    enum LegacyTypeSupport { DisallowLegacyTypeInTypeAttribute, AllowLegacyTypeInTypeAttribute };
     44    bool prepareScript(const TextPosition1& scriptStartPosition = TextPosition1::minimumPosition(), LegacyTypeSupport = DisallowLegacyTypeInTypeAttribute);
    4445
     46    String scriptCharset() const { return m_characterEncoding; }
    4547    String scriptContent() const;
    46     bool shouldExecuteAsJavaScript() const;
    4748    void executeScript(const ScriptSourceCode&);
    4849    void execute(CachedScript*);
    4950
    5051    // XML parser calls these
    51     virtual String sourceAttributeValue() const = 0;
    5252    virtual void dispatchLoadEvent() = 0;
    5353    virtual void dispatchErrorEvent() = 0;
     54    bool isScriptTypeSupported(LegacyTypeSupport) const;
    5455
    5556    bool haveFiredLoadEvent() const { return m_haveFiredLoad; }
     57    bool willBeParserExecuted() const { return m_willBeParserExecuted; }
     58    bool readyToBeParserExecuted() const { return m_readyToBeParserExecuted; }
     59    bool willExecuteWhenDocumentFinishedParsing() const { return m_willExecuteWhenDocumentFinishedParsing; }
     60    CachedResourceHandle<CachedScript> cachedScript() { return m_cachedScript; }
    5661
    5762protected:
    5863    void setHaveFiredLoadEvent(bool haveFiredLoad) { m_haveFiredLoad = haveFiredLoad; }
    59     bool wasInsertedByParser() const { return m_wasInsertedByParser; }
    60     bool wasAlreadyStarted() const { return m_wasAlreadyStarted; }
     64    bool isParserInserted() const { return m_parserInserted; }
     65    bool alreadyStarted() const { return m_alreadyStarted; }
    6166
    6267    // Helper functions used by our parent classes.
    63     void insertedIntoDocument(const String& sourceUrl);
     68    void insertedIntoDocument();
    6469    void removedFromDocument();
    6570    void childrenChanged();
    66     void finishParsingChildren(const String& sourceUrl);
    6771    void handleSourceAttribute(const String& sourceUrl);
    6872
    6973private:
    7074    bool ignoresLoadRequest() const;
    71     bool isAsynchronous() const;
    72     bool isDeferred() const;
     75    bool isScriptForEventSupported() const;
    7376
    74     void requestScript(const String& sourceUrl);
    75     void evaluateScript(const ScriptSourceCode&);
     77    bool requestScript(const String& sourceUrl);
    7678    void stopLoadRequest();
    7779
    7880    virtual void notifyFinished(CachedResource*);
    7981
     82    virtual String sourceAttributeValue() const = 0;
    8083    virtual String charsetAttributeValue() const = 0;
    8184    virtual String typeAttributeValue() const = 0;
     
    8588    virtual bool asyncAttributeValue() const = 0;
    8689    virtual bool deferAttributeValue() const = 0;
     90    virtual bool hasSourceAttribute() const = 0;
    8791
    8892    Element* m_element;
    8993    CachedResourceHandle<CachedScript> m_cachedScript;
    90     bool m_wasInsertedByParser;
    91     bool m_isExternalScript;
    92     bool m_wasAlreadyStarted;
    93     bool m_haveFiredLoad;
     94    bool m_parserInserted : 1;
     95    bool m_isExternalScript : 1;
     96    bool m_alreadyStarted : 1;
     97    bool m_haveFiredLoad : 1;
     98    bool m_willBeParserExecuted : 1; // Same as "The parser will handle executing the script."
     99    bool m_readyToBeParserExecuted : 1;
     100    bool m_willExecuteWhenDocumentFinishedParsing : 1;
     101    String m_characterEncoding;
     102    String m_fallbackCharacterEncoding;
    94103};
    95104
  • trunk/Source/WebCore/dom/XMLDocumentParserLibxml2.cpp

    r76248 r79114  
    881881    m_requestingScript = true;
    882882
     883    bool successfullyPrepared = scriptElement->prepareScript(m_scriptStartPosition, ScriptElement::AllowLegacyTypeInTypeAttribute);
     884    if (!successfullyPrepared) {
    883885#if ENABLE(XHTMLMP)
    884     if (!scriptElement->shouldExecuteAsJavaScript())
    885         document()->setShouldProcessNoscriptElement(true);
    886     else
    887 #endif
    888     {
    889         // FIXME: Script execution should be shared should be shared between
     886        if (!scriptElement->isScriptTypeSupported(ScriptElement::AllowLegacyTypeInTypeAttribute))
     887            document()->setShouldProcessNoscriptElement(true);
     888#endif
     889    } else {
     890        // FIXME: Script execution should be shared between
    890891        // the libxml2 and Qt XMLDocumentParser implementations.
    891892
     
    894895        RefPtr<XMLDocumentParser> protect(this);
    895896
    896         String scriptHref = scriptElement->sourceAttributeValue();
    897         if (!scriptHref.isEmpty()) {
    898             // we have a src attribute
    899             String scriptCharset = scriptElement->scriptCharset();
    900             if (element->dispatchBeforeLoadEvent(scriptHref) &&
    901                 (m_pendingScript = document()->cachedResourceLoader()->requestScript(scriptHref, scriptCharset))) {
    902                 m_scriptElement = element;
    903                 m_pendingScript->addClient(this);
    904 
    905                 // m_pendingScript will be 0 if script was already loaded and ref() executed it
    906                 if (m_pendingScript)
    907                     pauseParsing();
    908             } else
    909                 m_scriptElement = 0;
     897        if (scriptElement->readyToBeParserExecuted())
     898            scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition));
     899        else if (scriptElement->willBeParserExecuted()) {
     900            m_pendingScript = scriptElement->cachedScript();
     901            m_scriptElement = element;
     902            m_pendingScript->addClient(this);
     903
     904            // m_pendingScript will be 0 if script was already loaded and addClient() executed it.
     905            if (m_pendingScript)
     906                pauseParsing();
    910907        } else
    911             scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition));
     908            m_scriptElement = 0;
    912909
    913910        // JavaScript may have detached the parser
  • trunk/Source/WebCore/dom/XMLDocumentParserQt.cpp

    r75188 r79114  
    589589    m_requestingScript = true;
    590590
    591 #if ENABLE(XHTMLMP)
    592     if (!scriptElement->shouldExecuteAsJavaScript())
    593         document()->setShouldProcessNoscriptElement(true);
    594     else
    595 #endif
    596     {
    597         String scriptHref = scriptElement->sourceAttributeValue();
    598         if (!scriptHref.isEmpty()) {
    599             // we have a src attribute
    600             String scriptCharset = scriptElement->scriptCharset();
    601             if (element->dispatchBeforeLoadEvent(scriptHref) &&
    602                 (m_pendingScript = document()->cachedResourceLoader()->requestScript(scriptHref, scriptCharset))) {
    603                 m_scriptElement = element;
    604                 m_pendingScript->addClient(this);
    605 
    606                 // m_pendingScript will be 0 if script was already loaded and ref() executed it
    607                 if (m_pendingScript)
    608                     pauseParsing();
    609             } else
    610                 m_scriptElement = 0;
     591    bool successfullyPrepared = scriptElement->prepareScript(m_scriptStartPosition, ScriptElement::AllowLegacyTypeInTypeAttribute);
     592    if (!successfullyPrepared) {
     593#if ENABLE(XHTMLMP)
     594        if (!scriptElement->isScriptTypeSupported(ScriptElement::AllowLegacyTypeInTypeAttribute))
     595            document()->setShouldProcessNoscriptElement(true);
     596#endif
     597    } else {
     598        if (scriptElement->readyToBeParserExecuted())
     599            scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition));
     600        else if (scriptElement->willBeParserExecuted()) {
     601            m_pendingScript = scriptElement->cachedScript();
     602            m_scriptElement = element;
     603            m_pendingScript->addClient(this);
     604
     605            // m_pendingScript will be 0 if script was already loaded and addClient() executed it.
     606            if (m_pendingScript)
     607                pauseParsing();
    611608        } else
    612             scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition));
     609            m_scriptElement = 0;
    613610    }
    614611    m_requestingScript = false;
  • trunk/Source/WebCore/html/HTMLScriptElement.cpp

    r73589 r79114  
    3636using namespace HTMLNames;
    3737
    38 inline HTMLScriptElement::HTMLScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool wasAlreadyStarted)
     38inline HTMLScriptElement::HTMLScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool alreadyStarted)
    3939    : HTMLElement(tagName, document)
    40     , ScriptElement(this, wasInsertedByParser, wasAlreadyStarted)
     40    , ScriptElement(this, wasInsertedByParser, alreadyStarted)
    4141{
    4242    ASSERT(hasTagName(scriptTag));
     
    7575}
    7676
    77 void HTMLScriptElement::finishParsingChildren()
    78 {
    79     ScriptElement::finishParsingChildren(sourceAttributeValue());
    80     HTMLElement::finishParsingChildren();
    81 }
    82 
    8377void HTMLScriptElement::insertedIntoDocument()
    8478{
    8579    HTMLElement::insertedIntoDocument();
    86     ScriptElement::insertedIntoDocument(sourceAttributeValue());
     80    ScriptElement::insertedIntoDocument();
    8781}
    8882
     
    161155}
    162156
     157bool HTMLScriptElement::hasSourceAttribute() const
     158{
     159    return fastHasAttribute(srcAttr);
     160}
     161
    163162void HTMLScriptElement::dispatchLoadEvent()
    164163{
     
    176175PassRefPtr<Element> HTMLScriptElement::cloneElementWithoutAttributesAndChildren() const
    177176{
    178     return adoptRef(new HTMLScriptElement(tagQName(), document(), false, wasAlreadyStarted()));
     177    return adoptRef(new HTMLScriptElement(tagQName(), document(), false, alreadyStarted()));
    179178}
    180179
  • trunk/Source/WebCore/html/HTMLScriptElement.h

    r72995 r79114  
    4040
    4141private:
    42     HTMLScriptElement(const QualifiedName&, Document*, bool wasInsertedByParser, bool wasAlreadyStarted);
     42    HTMLScriptElement(const QualifiedName&, Document*, bool wasInsertedByParser, bool alreadyStarted);
    4343
    4444    virtual void parseMappedAttribute(Attribute*);
     
    4848
    4949    virtual bool isURLAttribute(Attribute*) const;
    50     virtual void finishParsingChildren();
    5150
    5251    virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const;
     
    6059    virtual bool asyncAttributeValue() const;
    6160    virtual bool deferAttributeValue() const;
     61    virtual bool hasSourceAttribute() const;
    6262
    6363    virtual void dispatchLoadEvent();
  • trunk/Source/WebCore/html/parser/HTMLScriptRunner.cpp

    r78058 r79114  
    263263{
    264264    ASSERT(!pendingScript.element());
    265     const AtomicString& srcValue = script->getAttribute(srcAttr);
    266     // Allow the host to disllow script loads (using the XSSAuditor, etc.)
    267     // FIXME: this check should be performed on the final URL in a redirect chain.
    268     if (!m_host->shouldLoadExternalScriptFromSrc(srcValue))
    269         return false;
    270     // FIXME: We need to resolve the url relative to the element.
    271     if (!script->dispatchBeforeLoadEvent(srcValue))
    272         return false;
    273265    pendingScript.setElement(script);
    274266    // This should correctly return 0 for empty or invalid srcValues.
    275     CachedScript* cachedScript = m_document->cachedResourceLoader()->requestScript(srcValue, toScriptElement(script)->scriptCharset());
     267    CachedScript* cachedScript = toScriptElement(script)->cachedScript().get();
    276268    if (!cachedScript) {
    277269        notImplemented(); // Dispatch error event.
     
    294286        ScriptElement* scriptElement = toScriptElement(script);
    295287        ASSERT(scriptElement);
    296         if (!scriptElement->shouldExecuteAsJavaScript())
     288
     289        scriptElement->prepareScript(scriptStartPosition);
     290
     291        if (!scriptElement->willBeParserExecuted())
    297292            return;
    298        
    299         if (script->hasAttribute(srcAttr)) {
    300             if (script->hasAttribute(asyncAttr)) // Async takes precendence over defer.
    301                 return; // Asynchronous scripts handle themselves.
    302 
    303             if (script->hasAttribute(deferAttr))
    304                 requestDeferredScript(script);
    305             else
    306                 requestParsingBlockingScript(script);
    307         } else if (!m_document->haveStylesheetsLoaded() && m_scriptNestingLevel == 1) {
    308             // Block inline script execution on stylesheet load, unless we are in document.write().
    309             // The latter case can only happen if a script both triggers a stylesheet load
    310             // and writes an inline script. Since write is blocking we have to execute the
    311             // written script immediately, ignoring the pending sheets.
    312             m_parsingBlockingScript.setElement(script);
    313             m_parsingBlockingScript.setStartingPosition(scriptStartPosition);
    314         } else {
    315             ASSERT(isExecutingScript());
    316             ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition);
    317             scriptElement->executeScript(sourceCode);
    318         }
    319     }
    320 }
    321 
    322 }
     293
     294        if (scriptElement->willExecuteWhenDocumentFinishedParsing())
     295            requestDeferredScript(script);
     296        else if (scriptElement->readyToBeParserExecuted()) {
     297            if (m_scriptNestingLevel == 1) {
     298                m_parsingBlockingScript.setElement(script);
     299                m_parsingBlockingScript.setStartingPosition(scriptStartPosition);
     300            } else {
     301                ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition);
     302                scriptElement->executeScript(sourceCode);
     303            }
     304        } else
     305            requestParsingBlockingScript(script);
     306    }
     307}
     308
     309}
  • trunk/Source/WebCore/svg/SVGScriptElement.cpp

    r78249 r79114  
    3636DEFINE_ANIMATED_BOOLEAN(SVGScriptElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
    3737
    38 inline SVGScriptElement::SVGScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool wasAlreadyStarted)
     38inline SVGScriptElement::SVGScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool alreadyStarted)
    3939    : SVGElement(tagName, document)
    40     , ScriptElement(this, wasInsertedByParser, wasAlreadyStarted)
     40    , ScriptElement(this, wasInsertedByParser, alreadyStarted)
    4141{
    4242}
     
    7373        // causes an immediate dispatch of the SVGLoad event. If the attribute value was 'false' before inserting the script element
    7474        // in the document, the SVGLoad event has already been dispatched.
    75         if (!externalResourcesRequiredBaseValue() && !haveFiredLoadEvent() && !wasInsertedByParser()) {
     75        if (!externalResourcesRequiredBaseValue() && !haveFiredLoadEvent() && !isParserInserted()) {
    7676            setHaveFiredLoadEvent(true);
    7777            ASSERT(haveLoadedRequiredResources());
     
    112112{
    113113    SVGElement::insertedIntoDocument();
    114     ScriptElement::insertedIntoDocument(sourceAttributeValue());
    115 
    116     if (wasInsertedByParser())
     114    ScriptElement::insertedIntoDocument();
     115
     116    if (isParserInserted())
    117117        return;
    118118
     
    143143void SVGScriptElement::finishParsingChildren()
    144144{
    145     ScriptElement::finishParsingChildren(sourceAttributeValue());
    146145    SVGElement::finishParsingChildren();
    147146
     
    213212}
    214213
     214bool SVGScriptElement::hasSourceAttribute() const
     215{
     216    return hasAttribute(XLinkNames::hrefAttr);
     217}
     218
    215219void SVGScriptElement::dispatchLoadEvent()
    216220{
    217221    bool externalResourcesRequired = externalResourcesRequiredBaseValue();
    218222
    219     if (wasInsertedByParser())
     223    if (isParserInserted())
    220224        ASSERT(externalResourcesRequired != haveFiredLoadEvent());
    221225    else if (haveFiredLoadEvent()) {
     
    248252PassRefPtr<Element> SVGScriptElement::cloneElementWithoutAttributesAndChildren() const
    249253{
    250     return adoptRef(new SVGScriptElement(tagQName(), document(), false, wasAlreadyStarted()));
     254    return adoptRef(new SVGScriptElement(tagQName(), document(), false, alreadyStarted()));
    251255}
    252256
  • trunk/Source/WebCore/svg/SVGScriptElement.h

    r78249 r79114  
    4343
    4444private:
    45     SVGScriptElement(const QualifiedName&, Document*, bool wasInsertedByParser, bool wasAlreadyStarted);
     45    SVGScriptElement(const QualifiedName&, Document*, bool wasInsertedByParser, bool alreadyStarted);
    4646
    4747    virtual void parseMappedAttribute(Attribute*);
     
    6969    virtual bool asyncAttributeValue() const;
    7070    virtual bool deferAttributeValue() const;
     71    virtual bool hasSourceAttribute() const;
    7172
    7273    virtual void dispatchLoadEvent();
Note: See TracChangeset for help on using the changeset viewer.