Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / dom / ScriptRunner.cpp
blob748809724c54905f9089c9175b69ee405c8486d2
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 "platform/scheduler/CancellableTaskFactory.h"
34 #include "public/platform/Platform.h"
35 #include "public/platform/WebScheduler.h"
36 #include "public/platform/WebThread.h"
38 namespace blink {
41 ScriptRunner::ScriptRunner(Document* document)
42 : m_document(document)
43 , m_executeScriptsTaskFactory(CancellableTaskFactory::create(this, &ScriptRunner::executeScripts))
45 ASSERT(document);
48 ScriptRunner::~ScriptRunner()
50 #if !ENABLE(OILPAN)
51 // Make sure that ScriptLoaders don't keep their PendingScripts alive.
52 for (ScriptLoader* scriptLoader : m_scriptsToExecuteInOrder)
53 scriptLoader->detach();
54 for (ScriptLoader* scriptLoader : m_scriptsToExecuteSoon)
55 scriptLoader->detach();
56 for (ScriptLoader* scriptLoader : m_pendingAsyncScripts)
57 scriptLoader->detach();
58 #endif
61 void ScriptRunner::addPendingAsyncScript(ScriptLoader* scriptLoader)
63 m_document->incrementLoadEventDelayCount();
64 m_pendingAsyncScripts.add(scriptLoader);
67 void ScriptRunner::queueScriptForExecution(ScriptLoader* scriptLoader, ExecutionType executionType)
69 ASSERT(scriptLoader);
71 switch (executionType) {
72 case ASYNC_EXECUTION:
73 addPendingAsyncScript(scriptLoader);
74 break;
76 case IN_ORDER_EXECUTION:
77 m_document->incrementLoadEventDelayCount();
78 m_scriptsToExecuteInOrder.append(scriptLoader);
79 break;
83 void ScriptRunner::suspend()
85 m_executeScriptsTaskFactory->cancel();
88 void ScriptRunner::resume()
90 if (hasPendingScripts())
91 postTaskIfOneIsNotAlreadyInFlight();
94 void ScriptRunner::notifyScriptReady(ScriptLoader* scriptLoader, ExecutionType executionType)
96 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(scriptLoader);
97 switch (executionType) {
98 case ASYNC_EXECUTION:
99 // RELEASE_ASSERT makes us crash in a controlled way in error cases
100 // where the ScriptLoader is associated with the wrong ScriptRunner
101 // (otherwise we'd cause a use-after-free in ~ScriptRunner when it tries
102 // to detach).
103 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(m_pendingAsyncScripts.contains(scriptLoader));
104 m_scriptsToExecuteSoon.append(scriptLoader);
105 m_pendingAsyncScripts.remove(scriptLoader);
106 break;
108 case IN_ORDER_EXECUTION:
109 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_scriptsToExecuteInOrder.isEmpty());
110 break;
112 postTaskIfOneIsNotAlreadyInFlight();
115 void ScriptRunner::notifyScriptLoadError(ScriptLoader* scriptLoader, ExecutionType executionType)
117 switch (executionType) {
118 case ASYNC_EXECUTION:
119 // RELEASE_ASSERT makes us crash in a controlled way in error cases
120 // where the ScriptLoader is associated with the wrong ScriptRunner
121 // (otherwise we'd cause a use-after-free in ~ScriptRunner when it tries
122 // to detach).
123 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(m_pendingAsyncScripts.contains(scriptLoader));
124 m_pendingAsyncScripts.remove(scriptLoader);
125 scriptLoader->detach();
126 m_document->decrementLoadEventDelayCount();
127 break;
129 case IN_ORDER_EXECUTION:
130 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_scriptsToExecuteInOrder.isEmpty());
131 break;
135 void ScriptRunner::movePendingAsyncScript(Document& oldDocument, Document& newDocument, ScriptLoader* scriptLoader)
137 RefPtrWillBeRawPtr<Document> newContextDocument = newDocument.contextDocument().get();
138 if (!newContextDocument) {
139 // Document's contextDocument() method will return no Document if the
140 // following conditions both hold:
142 // - The Document wasn't created with an explicit context document
143 // and that document is otherwise kept alive.
144 // - The Document itself is detached from its frame.
146 // The script element's loader is in that case moved to document() and
147 // its script runner, which is the non-null Document that contextDocument()
148 // would return if not detached.
149 ASSERT(!newDocument.frame());
150 newContextDocument = &newDocument;
152 RefPtrWillBeRawPtr<Document> oldContextDocument = oldDocument.contextDocument().get();
153 if (!oldContextDocument) {
154 ASSERT(!oldDocument.frame());
155 oldContextDocument = &oldDocument;
157 if (oldContextDocument != newContextDocument)
158 oldContextDocument->scriptRunner()->movePendingAsyncScript(newContextDocument->scriptRunner(), scriptLoader);
161 void ScriptRunner::movePendingAsyncScript(ScriptRunner* newRunner, ScriptLoader* scriptLoader)
163 if (m_pendingAsyncScripts.contains(scriptLoader)) {
164 newRunner->addPendingAsyncScript(scriptLoader);
165 m_pendingAsyncScripts.remove(scriptLoader);
166 m_document->decrementLoadEventDelayCount();
170 void ScriptRunner::executeScripts()
172 RefPtrWillBeRawPtr<Document> protect(m_document.get());
174 WillBeHeapDeque<RawPtrWillBeMember<ScriptLoader>> scriptLoaders;
175 scriptLoaders.swap(m_scriptsToExecuteSoon);
177 WillBeHeapHashSet<RawPtrWillBeMember<ScriptLoader>> inorderSet;
178 while (!m_scriptsToExecuteInOrder.isEmpty() && m_scriptsToExecuteInOrder.first()->isReady()) {
179 ScriptLoader* script = m_scriptsToExecuteInOrder.takeFirst();
180 inorderSet.add(script);
181 scriptLoaders.append(script);
184 while (!scriptLoaders.isEmpty()) {
185 scriptLoaders.takeFirst()->execute();
186 m_document->decrementLoadEventDelayCount();
188 if (yieldForHighPriorityWork())
189 break;
192 // If we have to yield, we must re-enqueue any scriptLoaders back onto the front of
193 // m_scriptsToExecuteInOrder or m_scriptsToExecuteSoon depending on where the script
194 // came from.
195 // NOTE a yield followed by a notifyScriptReady(... ASYNC_EXECUTION) will result in that script executing
196 // before any pre-existing ScriptsToExecuteInOrder.
197 while (!scriptLoaders.isEmpty()) {
198 ScriptLoader* script = scriptLoaders.takeLast();
199 if (inorderSet.contains(script))
200 m_scriptsToExecuteInOrder.prepend(script);
201 else
202 m_scriptsToExecuteSoon.prepend(script);
206 bool ScriptRunner::yieldForHighPriorityWork()
208 if (!Platform::current()->currentThread()->scheduler()->shouldYieldForHighPriorityWork())
209 return false;
211 postTaskIfOneIsNotAlreadyInFlight();
212 return true;
215 void ScriptRunner::postTaskIfOneIsNotAlreadyInFlight()
217 if (m_executeScriptsTaskFactory->isPending())
218 return;
220 Platform::current()->currentThread()->scheduler()->loadingTaskRunner()->postTask(FROM_HERE, m_executeScriptsTaskFactory->cancelAndCreate());
223 DEFINE_TRACE(ScriptRunner)
225 #if ENABLE(OILPAN)
226 visitor->trace(m_document);
227 visitor->trace(m_scriptsToExecuteInOrder);
228 visitor->trace(m_scriptsToExecuteSoon);
229 visitor->trace(m_pendingAsyncScripts);
230 #endif
233 } // namespace blink