initial import
[vuplus_webkit] / Source / WebCore / html / parser / HTMLScriptRunner.cpp
1 /*
2  * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3  *
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.
12  *
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. 
24  */
25
26 #include "config.h"
27 #include "HTMLScriptRunner.h"
28
29 #include "Attribute.h"
30 #include "CachedScript.h"
31 #include "CachedResourceLoader.h"
32 #include "Element.h"
33 #include "Event.h"
34 #include "Frame.h"
35 #include "HTMLInputStream.h"
36 #include "HTMLNames.h"
37 #include "HTMLScriptRunnerHost.h"
38 #include "IgnoreDestructiveWriteCountIncrementer.h"
39 #include "NestingLevelIncrementer.h"
40 #include "NotImplemented.h"
41 #include "ScriptElement.h"
42 #include "ScriptSourceCode.h"
43
44 namespace WebCore {
45
46 using namespace HTMLNames;
47
48 HTMLScriptRunner::HTMLScriptRunner(Document* document, HTMLScriptRunnerHost* host)
49     : m_document(document)
50     , m_host(host)
51     , m_scriptNestingLevel(0)
52     , m_hasScriptsWaitingForStylesheets(false)
53 {
54     ASSERT(m_host);
55 }
56
57 HTMLScriptRunner::~HTMLScriptRunner()
58 {
59     // FIXME: Should we be passed a "done loading/parsing" callback sooner than destruction?
60     if (m_parsingBlockingScript.cachedScript() && m_parsingBlockingScript.watchingForLoad())
61         stopWatchingForLoad(m_parsingBlockingScript);
62
63     while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
64         PendingScript pendingScript = m_scriptsToExecuteAfterParsing.takeFirst();
65         if (pendingScript.cachedScript() && pendingScript.watchingForLoad())
66             stopWatchingForLoad(pendingScript);
67     }
68 }
69
70 void HTMLScriptRunner::detach()
71 {
72     m_document = 0;
73 }
74
75 static KURL documentURLForScriptExecution(Document* document)
76 {
77     if (!document || !document->frame())
78         return KURL();
79
80     // Use the URL of the currently active document for this frame.
81     return document->frame()->document()->url();
82 }
83
84 inline PassRefPtr<Event> createScriptLoadEvent()
85 {
86     return Event::create(eventNames().loadEvent, false, false);
87 }
88
89 ScriptSourceCode HTMLScriptRunner::sourceFromPendingScript(const PendingScript& script, bool& errorOccurred) const
90 {
91     if (script.cachedScript()) {
92         errorOccurred = script.cachedScript()->errorOccurred();
93         ASSERT(script.cachedScript()->isLoaded());
94         return ScriptSourceCode(script.cachedScript());
95     }
96     errorOccurred = false;
97     return ScriptSourceCode(script.element()->textContent(), documentURLForScriptExecution(m_document), script.startingPosition());
98 }
99
100 bool HTMLScriptRunner::isPendingScriptReady(const PendingScript& script)
101 {
102     m_hasScriptsWaitingForStylesheets = !m_document->haveStylesheetsLoaded();
103     if (m_hasScriptsWaitingForStylesheets)
104         return false;
105     if (script.cachedScript() && !script.cachedScript()->isLoaded())
106         return false;
107     return true;
108 }
109
110 void HTMLScriptRunner::executeParsingBlockingScript()
111 {
112     ASSERT(m_document);
113     ASSERT(!m_scriptNestingLevel);
114     ASSERT(m_document->haveStylesheetsLoaded());
115     ASSERT(isPendingScriptReady(m_parsingBlockingScript));
116
117     InsertionPointRecord insertionPointRecord(m_host->inputStream());
118     executePendingScriptAndDispatchEvent(m_parsingBlockingScript);
119 }
120
121 void HTMLScriptRunner::executePendingScriptAndDispatchEvent(PendingScript& pendingScript)
122 {
123     bool errorOccurred = false;
124     ScriptSourceCode sourceCode = sourceFromPendingScript(pendingScript, errorOccurred);
125
126     // Stop watching loads before executeScript to prevent recursion if the script reloads itself.
127     if (pendingScript.cachedScript() && pendingScript.watchingForLoad())
128         stopWatchingForLoad(pendingScript);
129
130     // Clear the pending script before possible rentrancy from executeScript()
131     RefPtr<Element> element = pendingScript.releaseElementAndClear();
132     if (ScriptElement* scriptElement = toScriptElement(element.get())) {
133         NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel);
134         IgnoreDestructiveWriteCountIncrementer ignoreDestructiveWriteCountIncrementer(m_document);
135         if (errorOccurred)
136             scriptElement->dispatchErrorEvent();
137         else {
138             ASSERT(isExecutingScript());
139             scriptElement->executeScript(sourceCode);
140             element->dispatchEvent(createScriptLoadEvent());
141         }
142     }
143     ASSERT(!m_scriptNestingLevel);
144 }
145
146 void HTMLScriptRunner::watchForLoad(PendingScript& pendingScript)
147 {
148     ASSERT(!pendingScript.watchingForLoad());
149     m_host->watchForLoad(pendingScript.cachedScript());
150     pendingScript.setWatchingForLoad(true);
151 }
152
153 void HTMLScriptRunner::stopWatchingForLoad(PendingScript& pendingScript)
154 {
155     ASSERT(pendingScript.watchingForLoad());
156     m_host->stopWatchingForLoad(pendingScript.cachedScript());
157     pendingScript.setWatchingForLoad(false);
158 }
159
160 // This function should match 10.2.5.11 "An end tag whose tag name is 'script'"
161 // Script handling lives outside the tree builder to keep the each class simple.
162 bool HTMLScriptRunner::execute(PassRefPtr<Element> scriptElement, const TextPosition1& scriptStartPosition)
163 {
164     ASSERT(scriptElement);
165     // FIXME: If scripting is disabled, always just return true;
166
167     bool hadPreloadScanner = m_host->hasPreloadScanner();
168
169     // Try to execute the script given to us.
170     runScript(scriptElement.get(), scriptStartPosition);
171
172     if (haveParsingBlockingScript()) {
173         if (m_scriptNestingLevel)
174             return false; // Block the parser.  Unwind to the outermost HTMLScriptRunner::execute before continuing parsing.
175         // If preload scanner got created, it is missing the source after the current insertion point. Append it and scan.
176         if (!hadPreloadScanner && m_host->hasPreloadScanner())
177             m_host->appendCurrentInputStreamToPreloadScannerAndScan();
178         if (!executeParsingBlockingScripts())
179             return false; // We still have a parsing blocking script, block the parser.
180     }
181     return true; // Scripts executed as expected, continue parsing.
182 }
183
184 bool HTMLScriptRunner::haveParsingBlockingScript() const
185 {
186     return !!m_parsingBlockingScript.element();
187 }
188
189 bool HTMLScriptRunner::executeParsingBlockingScripts()
190 {
191     while (haveParsingBlockingScript()) {
192         // We only really need to check once.
193         if (!isPendingScriptReady(m_parsingBlockingScript))
194             return false;
195         executeParsingBlockingScript();
196     }
197     return true;
198 }
199
200 bool HTMLScriptRunner::executeScriptsWaitingForLoad(CachedResource* cachedScript)
201 {
202     ASSERT(!m_scriptNestingLevel);
203     ASSERT(haveParsingBlockingScript());
204     ASSERT_UNUSED(cachedScript, m_parsingBlockingScript.cachedScript() == cachedScript);
205     ASSERT(m_parsingBlockingScript.cachedScript()->isLoaded());
206     return executeParsingBlockingScripts();
207 }
208
209 bool HTMLScriptRunner::executeScriptsWaitingForStylesheets()
210 {
211     ASSERT(m_document);
212     // Callers should check hasScriptsWaitingForStylesheets() before calling
213     // to prevent parser or script re-entry during </style> parsing.
214     ASSERT(hasScriptsWaitingForStylesheets());
215     ASSERT(!m_scriptNestingLevel);
216     ASSERT(m_document->haveStylesheetsLoaded());
217     return executeParsingBlockingScripts();
218 }
219
220 bool HTMLScriptRunner::executeScriptsWaitingForParsing()
221 {
222     while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
223         ASSERT(!m_scriptNestingLevel);
224         ASSERT(!haveParsingBlockingScript());
225         ASSERT(m_scriptsToExecuteAfterParsing.first().cachedScript());
226         if (!m_scriptsToExecuteAfterParsing.first().cachedScript()->isLoaded()) {
227             watchForLoad(m_scriptsToExecuteAfterParsing.first());
228             return false;
229         }
230         PendingScript first = m_scriptsToExecuteAfterParsing.takeFirst();
231         executePendingScriptAndDispatchEvent(first);
232         if (!m_document)
233             return false;
234     }
235     return true;
236 }
237
238 void HTMLScriptRunner::requestParsingBlockingScript(Element* element)
239 {
240     if (!requestPendingScript(m_parsingBlockingScript, element))
241         return;
242
243     ASSERT(m_parsingBlockingScript.cachedScript());
244
245     // We only care about a load callback if cachedScript is not already
246     // in the cache.  Callers will attempt to run the m_parsingBlockingScript
247     // if possible before returning control to the parser.
248     if (!m_parsingBlockingScript.cachedScript()->isLoaded())
249         watchForLoad(m_parsingBlockingScript);
250 }
251
252 void HTMLScriptRunner::requestDeferredScript(Element* element)
253 {
254     PendingScript pendingScript;
255     if (!requestPendingScript(pendingScript, element))
256         return;
257
258     ASSERT(pendingScript.cachedScript());
259     m_scriptsToExecuteAfterParsing.append(pendingScript);
260 }
261
262 bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Element* script) const
263 {
264     ASSERT(!pendingScript.element());
265     pendingScript.setElement(script);
266     // This should correctly return 0 for empty or invalid srcValues.
267     CachedScript* cachedScript = toScriptElement(script)->cachedScript().get();
268     if (!cachedScript) {
269         notImplemented(); // Dispatch error event.
270         return false;
271     }
272     pendingScript.setCachedScript(cachedScript);
273     return true;
274 }
275
276 // This method is meant to match the HTML5 definition of "running a script"
277 // http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#running-a-script
278 void HTMLScriptRunner::runScript(Element* script, const TextPosition1& scriptStartPosition)
279 {
280     ASSERT(m_document);
281     ASSERT(!haveParsingBlockingScript());
282     {
283         InsertionPointRecord insertionPointRecord(m_host->inputStream());
284         NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel);
285
286         ScriptElement* scriptElement = toScriptElement(script);
287
288         // This contains both and ASSERTION and a null check since we should not
289         // be getting into the case of a null script element, but seem to be from
290         // time to time. The assertion is left in to help find those cases and
291         // is being tracked by <https://bugs.webkit.org/show_bug.cgi?id=60559>.
292         ASSERT(scriptElement);
293         if (!scriptElement)
294             return;
295
296         scriptElement->prepareScript(scriptStartPosition);
297
298         if (!scriptElement->willBeParserExecuted())
299             return;
300
301         if (scriptElement->willExecuteWhenDocumentFinishedParsing())
302             requestDeferredScript(script);
303         else if (scriptElement->readyToBeParserExecuted()) {
304             if (m_scriptNestingLevel == 1) {
305                 m_parsingBlockingScript.setElement(script);
306                 m_parsingBlockingScript.setStartingPosition(scriptStartPosition);
307             } else {
308                 ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition);
309                 scriptElement->executeScript(sourceCode);
310             }
311         } else
312             requestParsingBlockingScript(script);
313     }
314 }
315
316 }