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
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.
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"
41 ScriptRunner::ScriptRunner(Document
* document
)
42 : m_document(document
)
43 , m_executeScriptsTaskFactory(CancellableTaskFactory::create(this, &ScriptRunner::executeScripts
))
48 ScriptRunner::~ScriptRunner()
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();
61 void ScriptRunner::addPendingAsyncScript(ScriptLoader
* scriptLoader
)
63 m_document
->incrementLoadEventDelayCount();
64 m_pendingAsyncScripts
.add(scriptLoader
);
67 void ScriptRunner::queueScriptForExecution(ScriptLoader
* scriptLoader
, ExecutionType executionType
)
71 switch (executionType
) {
73 addPendingAsyncScript(scriptLoader
);
76 case IN_ORDER_EXECUTION
:
77 m_document
->incrementLoadEventDelayCount();
78 m_scriptsToExecuteInOrder
.append(scriptLoader
);
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
) {
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
103 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(m_pendingAsyncScripts
.contains(scriptLoader
));
104 m_scriptsToExecuteSoon
.append(scriptLoader
);
105 m_pendingAsyncScripts
.remove(scriptLoader
);
108 case IN_ORDER_EXECUTION
:
109 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_scriptsToExecuteInOrder
.isEmpty());
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
123 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(m_pendingAsyncScripts
.contains(scriptLoader
));
124 m_pendingAsyncScripts
.remove(scriptLoader
);
125 scriptLoader
->detach();
126 m_document
->decrementLoadEventDelayCount();
129 case IN_ORDER_EXECUTION
:
130 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_scriptsToExecuteInOrder
.isEmpty());
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())
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
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
);
202 m_scriptsToExecuteSoon
.prepend(script
);
206 bool ScriptRunner::yieldForHighPriorityWork()
208 if (!Platform::current()->currentThread()->scheduler()->shouldYieldForHighPriorityWork())
211 postTaskIfOneIsNotAlreadyInFlight();
215 void ScriptRunner::postTaskIfOneIsNotAlreadyInFlight()
217 if (m_executeScriptsTaskFactory
->isPending())
220 Platform::current()->currentThread()->scheduler()->loadingTaskRunner()->postTask(FROM_HERE
, m_executeScriptsTaskFactory
->cancelAndCreate());
223 DEFINE_TRACE(ScriptRunner
)
226 visitor
->trace(m_document
);
227 visitor
->trace(m_scriptsToExecuteInOrder
);
228 visitor
->trace(m_scriptsToExecuteSoon
);
229 visitor
->trace(m_pendingAsyncScripts
);