Upon load failure, remove sync script from execution queue.

If a script element is to be loaded synchronously and executed in order,
it's queued for execution before loading. Should that load fail, the
immediate execution of the script,

  https://html.spec.whatwg.org/#execute-the-script-block

should only result in an error event being dispatched.

Implementation-wise, along with signalling error, the failed script must also
be removed from the internal in-order execution queue. We're done with
(not) executing the script and failure to remove it will cause subsequent
processing of the script execution queue to see the script as having failed
to load and re-dispatch an error event.

R=haraken
BUG=503077

Review URL: https://codereview.chromium.org/1263743002

git-svn-id: svn://svn.chromium.org/blink/trunk@199656 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/LayoutTests/fast/dom/HTMLScriptElement/script-sync-onerror-not-repeated-expected.txt b/LayoutTests/fast/dom/HTMLScriptElement/script-sync-onerror-not-repeated-expected.txt
new file mode 100644
index 0000000..392a70d
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLScriptElement/script-sync-onerror-not-repeated-expected.txt
@@ -0,0 +1,10 @@
+Script that fails to load should not dispatch multiple error events
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS numberOfTimesOnErrorHandlerHasRun is 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/HTMLScriptElement/script-sync-onerror-not-repeated.html b/LayoutTests/fast/dom/HTMLScriptElement/script-sync-onerror-not-repeated.html
new file mode 100644
index 0000000..d0a65dd
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLScriptElement/script-sync-onerror-not-repeated.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../resources/js-test.js"></script>
+<script>
+function loadScript(url, async, onload, onerror) {
+    var script = document.createElement("script");
+    script.async = async;
+    script.onload = onload;
+    if (onerror)
+        script.onerror = onerror;
+    script.src = url;
+    document.head.appendChild(script);
+}
+</script>
+</head>
+<body>
+<script>
+description("Script that fails to load should not dispatch multiple error events");
+
+window.jsTestIsAsync = true;
+
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+var numberOfTimesOnErrorHandlerHasRun = 0;
+function step2() {
+    shouldBeZero("numberOfTimesOnErrorHandlerHasRun");
+    numberOfTimesOnErrorHandlerHasRun++;
+    // Issue another script load so as to have the script runner
+    // revisit its script queue. It must not include the sync script
+    // that triggered the dispatch of an error event and the running of
+    // this handler.
+    loadScript("resources/script-load.js", true, finishJSTest);
+}
+
+function unexpectedLoad() {
+    testFailed("Script should not have loaded");
+    finishJSTest();
+}
+
+loadScript("non-existing.js", false, unexpectedLoad, step2);
+</script>
+</body>
+</html>
diff --git a/Source/core/dom/ScriptRunner.cpp b/Source/core/dom/ScriptRunner.cpp
index bf1d254..a79adbc 100644
--- a/Source/core/dom/ScriptRunner.cpp
+++ b/Source/core/dom/ScriptRunner.cpp
@@ -132,14 +132,17 @@
         // to detach).
         RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(m_pendingAsyncScripts.contains(scriptLoader));
         m_pendingAsyncScripts.remove(scriptLoader);
-        scriptLoader->detach();
-        m_document->decrementLoadEventDelayCount();
         break;
 
     case IN_ORDER_EXECUTION:
         RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_scriptsToExecuteInOrder.isEmpty());
+        // Remove; script failed to load and error event has been dispatched.
+        ScriptLoader* script = m_scriptsToExecuteInOrder.takeFirst();
+        ASSERT_UNUSED(script, script == scriptLoader);
         break;
     }
+    scriptLoader->detach();
+    m_document->decrementLoadEventDelayCount();
 }
 
 void ScriptRunner::movePendingAsyncScript(Document& oldDocument, Document& newDocument, ScriptLoader* scriptLoader)