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 "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.
41 struct ParamStorageTraits
<blink::ScriptRunner
*> : public PointerParamStorageTraits
<blink::ScriptRunner
*, false> {
48 ScriptRunner::ScriptRunner(Document
* document
)
49 : m_document(document
)
50 , m_executeScriptsTaskFactory(WTF::bind(&ScriptRunner::executeScripts
, this))
53 #if ENABLE(LAZY_SWEEPING) && defined(ADDRESS_SANITIZER)
54 m_executeScriptsTaskFactory
.setUnpoisonBeforeUpdate();
58 ScriptRunner::~ScriptRunner()
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();
71 void ScriptRunner::addPendingAsyncScript(ScriptLoader
* scriptLoader
)
73 m_document
->incrementLoadEventDelayCount();
74 m_pendingAsyncScripts
.add(scriptLoader
);
77 void ScriptRunner::queueScriptForExecution(ScriptLoader
* scriptLoader
, ExecutionType executionType
)
81 switch (executionType
) {
83 addPendingAsyncScript(scriptLoader
);
86 case IN_ORDER_EXECUTION
:
87 m_document
->incrementLoadEventDelayCount();
88 m_scriptsToExecuteInOrder
.append(scriptLoader
);
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
113 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(m_pendingAsyncScripts
.contains(scriptLoader
));
114 m_scriptsToExecuteSoon
.append(scriptLoader
);
115 m_pendingAsyncScripts
.remove(scriptLoader
);
118 case IN_ORDER_EXECUTION
:
119 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_scriptsToExecuteInOrder
.isEmpty());
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
133 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(m_pendingAsyncScripts
.contains(scriptLoader
));
134 m_pendingAsyncScripts
.remove(scriptLoader
);
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
);
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())
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
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
);
215 m_scriptsToExecuteSoon
.prepend(script
);
219 bool ScriptRunner::yieldForHighPriorityWork()
221 if (!Platform::current()->currentThread()->scheduler()->shouldYieldForHighPriorityWork())
224 postTaskIfOneIsNotAlreadyInFlight();
228 void ScriptRunner::postTaskIfOneIsNotAlreadyInFlight()
230 if (m_executeScriptsTaskFactory
.isPending())
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
)
240 visitor
->trace(m_document
);
241 visitor
->trace(m_scriptsToExecuteInOrder
);
242 visitor
->trace(m_scriptsToExecuteSoon
);
243 visitor
->trace(m_pendingAsyncScripts
);