Upon load failure, remove sync script from execution queue.
[chromium-blink-merge.git] / third_party / WebKit / Source / core / dom / ScriptRunner.cpp
bloba79adbca74937515055495d9e6da56f0deaee485
1 /*
2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "config.h"
27 #include "core/dom/ScriptRunner.h"
29 #include "core/dom/Document.h"
30 #include "core/dom/Element.h"
31 #include "core/dom/ScriptLoader.h"
32 #include "platform/heap/Handle.h"
33 #include "public/platform/Platform.h"
34 #include "public/platform/WebScheduler.h"
35 #include "public/platform/WebThread.h"
36 #include "wtf/Functional.h"
38 // This bit of magic is needed by oilpan to prevent the ScriptRunner from leaking.
39 namespace WTF {
40 template<>
41 struct ParamStorageTraits<blink::ScriptRunner*> : public PointerParamStorageTraits<blink::ScriptRunner*, false> {
45 namespace blink {
48 ScriptRunner::ScriptRunner(Document* document)
49 : m_document(document)
50 , m_executeScriptsTaskFactory(WTF::bind(&ScriptRunner::executeScripts, this))
52 ASSERT(document);
53 #if ENABLE(LAZY_SWEEPING) && defined(ADDRESS_SANITIZER)
54 m_executeScriptsTaskFactory.setUnpoisonBeforeUpdate();
55 #endif
58 ScriptRunner::~ScriptRunner()
60 #if !ENABLE(OILPAN)
61 // Make sure that ScriptLoaders don't keep their PendingScripts alive.
62 for (ScriptLoader* scriptLoader : m_scriptsToExecuteInOrder)
63 scriptLoader->detach();
64 for (ScriptLoader* scriptLoader : m_scriptsToExecuteSoon)
65 scriptLoader->detach();
66 for (ScriptLoader* scriptLoader : m_pendingAsyncScripts)
67 scriptLoader->detach();
68 #endif
71 void ScriptRunner::addPendingAsyncScript(ScriptLoader* scriptLoader)
73 m_document->incrementLoadEventDelayCount();
74 m_pendingAsyncScripts.add(scriptLoader);
77 void ScriptRunner::queueScriptForExecution(ScriptLoader* scriptLoader, ExecutionType executionType)
79 ASSERT(scriptLoader);
81 switch (executionType) {
82 case ASYNC_EXECUTION:
83 addPendingAsyncScript(scriptLoader);
84 break;
86 case IN_ORDER_EXECUTION:
87 m_document->incrementLoadEventDelayCount();
88 m_scriptsToExecuteInOrder.append(scriptLoader);
89 break;
93 void ScriptRunner::suspend()
95 m_executeScriptsTaskFactory.cancel();
98 void ScriptRunner::resume()
100 if (hasPendingScripts())
101 postTaskIfOneIsNotAlreadyInFlight();
104 void ScriptRunner::notifyScriptReady(ScriptLoader* scriptLoader, ExecutionType executionType)
106 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(scriptLoader);
107 switch (executionType) {
108 case ASYNC_EXECUTION:
109 // RELEASE_ASSERT makes us crash in a controlled way in error cases
110 // where the ScriptLoader is associated with the wrong ScriptRunner
111 // (otherwise we'd cause a use-after-free in ~ScriptRunner when it tries
112 // to detach).
113 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(m_pendingAsyncScripts.contains(scriptLoader));
114 m_scriptsToExecuteSoon.append(scriptLoader);
115 m_pendingAsyncScripts.remove(scriptLoader);
116 break;
118 case IN_ORDER_EXECUTION:
119 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_scriptsToExecuteInOrder.isEmpty());
120 break;
122 postTaskIfOneIsNotAlreadyInFlight();
125 void ScriptRunner::notifyScriptLoadError(ScriptLoader* scriptLoader, ExecutionType executionType)
127 switch (executionType) {
128 case ASYNC_EXECUTION:
129 // RELEASE_ASSERT makes us crash in a controlled way in error cases
130 // where the ScriptLoader is associated with the wrong ScriptRunner
131 // (otherwise we'd cause a use-after-free in ~ScriptRunner when it tries
132 // to detach).
133 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(m_pendingAsyncScripts.contains(scriptLoader));
134 m_pendingAsyncScripts.remove(scriptLoader);
135 break;
137 case IN_ORDER_EXECUTION:
138 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_scriptsToExecuteInOrder.isEmpty());
139 // Remove; script failed to load and error event has been dispatched.
140 ScriptLoader* script = m_scriptsToExecuteInOrder.takeFirst();
141 ASSERT_UNUSED(script, script == scriptLoader);
142 break;
144 scriptLoader->detach();
145 m_document->decrementLoadEventDelayCount();
148 void ScriptRunner::movePendingAsyncScript(Document& oldDocument, Document& newDocument, ScriptLoader* scriptLoader)
150 RefPtrWillBeRawPtr<Document> newContextDocument = newDocument.contextDocument().get();
151 if (!newContextDocument) {
152 // Document's contextDocument() method will return no Document if the
153 // following conditions both hold:
155 // - The Document wasn't created with an explicit context document
156 // and that document is otherwise kept alive.
157 // - The Document itself is detached from its frame.
159 // The script element's loader is in that case moved to document() and
160 // its script runner, which is the non-null Document that contextDocument()
161 // would return if not detached.
162 ASSERT(!newDocument.frame());
163 newContextDocument = &newDocument;
165 RefPtrWillBeRawPtr<Document> oldContextDocument = oldDocument.contextDocument().get();
166 if (!oldContextDocument) {
167 ASSERT(!oldDocument.frame());
168 oldContextDocument = &oldDocument;
170 if (oldContextDocument != newContextDocument)
171 oldContextDocument->scriptRunner()->movePendingAsyncScript(newContextDocument->scriptRunner(), scriptLoader);
174 void ScriptRunner::movePendingAsyncScript(ScriptRunner* newRunner, ScriptLoader* scriptLoader)
176 if (m_pendingAsyncScripts.contains(scriptLoader)) {
177 newRunner->addPendingAsyncScript(scriptLoader);
178 m_pendingAsyncScripts.remove(scriptLoader);
179 m_document->decrementLoadEventDelayCount();
183 void ScriptRunner::executeScripts()
185 RefPtrWillBeRawPtr<Document> protect(m_document.get());
187 WillBeHeapDeque<RawPtrWillBeMember<ScriptLoader>> scriptLoaders;
188 scriptLoaders.swap(m_scriptsToExecuteSoon);
190 WillBeHeapHashSet<RawPtrWillBeMember<ScriptLoader>> inorderSet;
191 while (!m_scriptsToExecuteInOrder.isEmpty() && m_scriptsToExecuteInOrder.first()->isReady()) {
192 ScriptLoader* script = m_scriptsToExecuteInOrder.takeFirst();
193 inorderSet.add(script);
194 scriptLoaders.append(script);
197 while (!scriptLoaders.isEmpty()) {
198 scriptLoaders.takeFirst()->execute();
199 m_document->decrementLoadEventDelayCount();
201 if (yieldForHighPriorityWork())
202 break;
205 // If we have to yield, we must re-enqueue any scriptLoaders back onto the front of
206 // m_scriptsToExecuteInOrder or m_scriptsToExecuteSoon depending on where the script
207 // came from.
208 // NOTE a yield followed by a notifyScriptReady(... ASYNC_EXECUTION) will result in that script executing
209 // before any pre-existing ScriptsToExecuteInOrder.
210 while (!scriptLoaders.isEmpty()) {
211 ScriptLoader* script = scriptLoaders.takeLast();
212 if (inorderSet.contains(script))
213 m_scriptsToExecuteInOrder.prepend(script);
214 else
215 m_scriptsToExecuteSoon.prepend(script);
219 bool ScriptRunner::yieldForHighPriorityWork()
221 if (!Platform::current()->currentThread()->scheduler()->shouldYieldForHighPriorityWork())
222 return false;
224 postTaskIfOneIsNotAlreadyInFlight();
225 return true;
228 void ScriptRunner::postTaskIfOneIsNotAlreadyInFlight()
230 if (m_executeScriptsTaskFactory.isPending())
231 return;
233 // FIXME: Rename task() so that it's obvious it cancels any pending task.
234 Platform::current()->currentThread()->scheduler()->postLoadingTask(FROM_HERE, m_executeScriptsTaskFactory.cancelAndCreate());
237 DEFINE_TRACE(ScriptRunner)
239 #if ENABLE(OILPAN)
240 visitor->trace(m_document);
241 visitor->trace(m_scriptsToExecuteInOrder);
242 visitor->trace(m_scriptsToExecuteSoon);
243 visitor->trace(m_pendingAsyncScripts);
244 #endif