Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 "core/html/parser/HTMLScriptRunner.h"
28
29 #include "bindings/core/v8/ScriptSourceCode.h"
30 #include "core/dom/Element.h"
31 #include "core/events/Event.h"
32 #include "core/dom/IgnoreDestructiveWriteCountIncrementer.h"
33 #include "core/dom/Microtask.h"
34 #include "core/dom/ScriptLoader.h"
35 #include "core/fetch/ScriptResource.h"
36 #include "core/frame/LocalFrame.h"
37 #include "core/html/parser/HTMLInputStream.h"
38 #include "core/html/parser/HTMLScriptRunnerHost.h"
39 #include "core/html/parser/NestingLevelIncrementer.h"
40 #include "platform/NotImplemented.h"
41
42 namespace blink {
43
44 using namespace HTMLNames;
45
46 HTMLScriptRunner::HTMLScriptRunner(Document* document, HTMLScriptRunnerHost* host)
47     : m_document(document)
48     , m_host(host)
49     , m_scriptNestingLevel(0)
50     , m_hasScriptsWaitingForResources(false)
51 {
52     ASSERT(m_host);
53 }
54
55 HTMLScriptRunner::~HTMLScriptRunner()
56 {
57 #if ENABLE(OILPAN)
58     // If the document is destructed without having explicitly
59     // detached the parser (and this script runner object), perform
60     // detach steps now. This will happen if the Document, the parser
61     // and this script runner object are swept out in the same GC.
62     detach();
63 #else
64     // Verify that detach() has been called.
65     ASSERT(!m_document);
66 #endif
67 }
68
69 void HTMLScriptRunner::detach()
70 {
71     if (!m_document)
72         return;
73
74     if (m_parserBlockingScript.resource() && m_parserBlockingScript.watchingForLoad())
75         stopWatchingForLoad(m_parserBlockingScript);
76
77     while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
78         PendingScript pendingScript = m_scriptsToExecuteAfterParsing.takeFirst();
79         if (pendingScript.resource() && pendingScript.watchingForLoad())
80             stopWatchingForLoad(pendingScript);
81     }
82     m_document = nullptr;
83 }
84
85 static KURL documentURLForScriptExecution(Document* document)
86 {
87     if (!document)
88         return KURL();
89
90     if (!document->frame()) {
91         if (document->importsController())
92             return document->url();
93         return KURL();
94     }
95
96     // Use the URL of the currently active document for this frame.
97     return document->frame()->document()->url();
98 }
99
100 inline PassRefPtrWillBeRawPtr<Event> createScriptLoadEvent()
101 {
102     return Event::create(EventTypeNames::load);
103 }
104
105 ScriptSourceCode HTMLScriptRunner::sourceFromPendingScript(const PendingScript& script, bool& errorOccurred) const
106 {
107     if (script.resource()) {
108         errorOccurred = script.resource()->errorOccurred();
109         ASSERT(script.resource()->isLoaded());
110         return ScriptSourceCode(script.resource());
111     }
112     errorOccurred = false;
113     return ScriptSourceCode(script.element()->textContent(), documentURLForScriptExecution(m_document), script.startingPosition());
114 }
115
116 bool HTMLScriptRunner::isPendingScriptReady(const PendingScript& script)
117 {
118     m_hasScriptsWaitingForResources = !m_document->isScriptExecutionReady();
119     if (m_hasScriptsWaitingForResources)
120         return false;
121     if (script.resource() && !script.resource()->isLoaded())
122         return false;
123     return true;
124 }
125
126 void HTMLScriptRunner::executeParsingBlockingScript()
127 {
128     ASSERT(m_document);
129     ASSERT(!isExecutingScript());
130     ASSERT(m_document->isScriptExecutionReady());
131     ASSERT(isPendingScriptReady(m_parserBlockingScript));
132
133     InsertionPointRecord insertionPointRecord(m_host->inputStream());
134     executePendingScriptAndDispatchEvent(m_parserBlockingScript, PendingScriptBlockingParser);
135 }
136
137 void HTMLScriptRunner::executePendingScriptAndDispatchEvent(PendingScript& pendingScript, PendingScriptType pendingScriptType)
138 {
139     bool errorOccurred = false;
140     ScriptSourceCode sourceCode = sourceFromPendingScript(pendingScript, errorOccurred);
141
142     // Stop watching loads before executeScript to prevent recursion if the script reloads itself.
143     if (pendingScript.resource() && pendingScript.watchingForLoad())
144         stopWatchingForLoad(pendingScript);
145
146     if (!isExecutingScript()) {
147         Microtask::performCheckpoint();
148         if (pendingScriptType == PendingScriptBlockingParser) {
149             m_hasScriptsWaitingForResources = !m_document->isScriptExecutionReady();
150             // The parser cannot be unblocked as a microtask requested another resource
151             if (m_hasScriptsWaitingForResources)
152                 return;
153         }
154     }
155
156     // Clear the pending script before possible rentrancy from executeScript()
157     RefPtrWillBeRawPtr<Element> element = pendingScript.releaseElementAndClear();
158     if (ScriptLoader* scriptLoader = toScriptLoaderIfPossible(element.get())) {
159         NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel);
160         IgnoreDestructiveWriteCountIncrementer ignoreDestructiveWriteCountIncrementer(m_document);
161         if (errorOccurred)
162             scriptLoader->dispatchErrorEvent();
163         else {
164             ASSERT(isExecutingScript());
165             scriptLoader->executeScript(sourceCode);
166             element->dispatchEvent(createScriptLoadEvent());
167         }
168     }
169     ASSERT(!isExecutingScript());
170 }
171
172 void HTMLScriptRunner::watchForLoad(PendingScript& pendingScript)
173 {
174     ASSERT(!pendingScript.watchingForLoad());
175     ASSERT(!pendingScript.resource()->isLoaded());
176     // addClient() will call notifyFinished() if the load is complete.
177     // Callers do not expect to be re-entered from this call, so they
178     // should not become a client of an already-loaded Resource.
179     pendingScript.resource()->addClient(this);
180     pendingScript.setWatchingForLoad(true);
181 }
182
183 void HTMLScriptRunner::stopWatchingForLoad(PendingScript& pendingScript)
184 {
185     ASSERT(pendingScript.watchingForLoad());
186     pendingScript.resource()->removeClient(this);
187     pendingScript.setWatchingForLoad(false);
188 }
189
190 void HTMLScriptRunner::notifyFinished(Resource* cachedResource)
191 {
192     m_host->notifyScriptLoaded(cachedResource);
193 }
194
195 // Implements the steps for 'An end tag whose tag name is "script"'
196 // http://whatwg.org/html#scriptEndTag
197 // Script handling lives outside the tree builder to keep each class simple.
198 void HTMLScriptRunner::execute(PassRefPtrWillBeRawPtr<Element> scriptElement, const TextPosition& scriptStartPosition)
199 {
200     ASSERT(scriptElement);
201     // FIXME: If scripting is disabled, always just return.
202
203     bool hadPreloadScanner = m_host->hasPreloadScanner();
204
205     // Try to execute the script given to us.
206     runScript(scriptElement.get(), scriptStartPosition);
207
208     if (hasParserBlockingScript()) {
209         if (isExecutingScript())
210             return; // Unwind to the outermost HTMLScriptRunner::execute before continuing parsing.
211         // If preload scanner got created, it is missing the source after the current insertion point. Append it and scan.
212         if (!hadPreloadScanner && m_host->hasPreloadScanner())
213             m_host->appendCurrentInputStreamToPreloadScannerAndScan();
214         executeParsingBlockingScripts();
215     }
216 }
217
218 bool HTMLScriptRunner::hasParserBlockingScript() const
219 {
220     return !!m_parserBlockingScript.element();
221 }
222
223 void HTMLScriptRunner::executeParsingBlockingScripts()
224 {
225     while (hasParserBlockingScript() && isPendingScriptReady(m_parserBlockingScript))
226         executeParsingBlockingScript();
227 }
228
229 void HTMLScriptRunner::executeScriptsWaitingForLoad(Resource* resource)
230 {
231     ASSERT(!isExecutingScript());
232     ASSERT(hasParserBlockingScript());
233     ASSERT_UNUSED(resource, m_parserBlockingScript.resource() == resource);
234     ASSERT(m_parserBlockingScript.resource()->isLoaded());
235     executeParsingBlockingScripts();
236 }
237
238 void HTMLScriptRunner::executeScriptsWaitingForResources()
239 {
240     ASSERT(m_document);
241     // Callers should check hasScriptsWaitingForResources() before calling
242     // to prevent parser or script re-entry during </style> parsing.
243     ASSERT(hasScriptsWaitingForResources());
244     ASSERT(!isExecutingScript());
245     ASSERT(m_document->isScriptExecutionReady());
246     executeParsingBlockingScripts();
247 }
248
249 bool HTMLScriptRunner::executeScriptsWaitingForParsing()
250 {
251     while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
252         ASSERT(!isExecutingScript());
253         ASSERT(!hasParserBlockingScript());
254         ASSERT(m_scriptsToExecuteAfterParsing.first().resource());
255         if (!m_scriptsToExecuteAfterParsing.first().resource()->isLoaded()) {
256             watchForLoad(m_scriptsToExecuteAfterParsing.first());
257             return false;
258         }
259         PendingScript first = m_scriptsToExecuteAfterParsing.takeFirst();
260         executePendingScriptAndDispatchEvent(first, PendingScriptDeferred);
261         // FIXME: What is this m_document check for?
262         if (!m_document)
263             return false;
264     }
265     return true;
266 }
267
268 void HTMLScriptRunner::requestParsingBlockingScript(Element* element)
269 {
270     if (!requestPendingScript(m_parserBlockingScript, element))
271         return;
272
273     ASSERT(m_parserBlockingScript.resource());
274
275     // We only care about a load callback if resource is not already
276     // in the cache. Callers will attempt to run the m_parserBlockingScript
277     // if possible before returning control to the parser.
278     if (!m_parserBlockingScript.resource()->isLoaded())
279         watchForLoad(m_parserBlockingScript);
280 }
281
282 void HTMLScriptRunner::requestDeferredScript(Element* element)
283 {
284     PendingScript pendingScript;
285     if (!requestPendingScript(pendingScript, element))
286         return;
287
288     ASSERT(pendingScript.resource());
289     m_scriptsToExecuteAfterParsing.append(pendingScript);
290 }
291
292 bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Element* script) const
293 {
294     ASSERT(!pendingScript.element());
295     pendingScript.setElement(script);
296     // This should correctly return 0 for empty or invalid srcValues.
297     ScriptResource* resource = toScriptLoaderIfPossible(script)->resource().get();
298     if (!resource) {
299         notImplemented(); // Dispatch error event.
300         return false;
301     }
302     pendingScript.setScriptResource(resource);
303     return true;
304 }
305
306 // Implements the initial steps for 'An end tag whose tag name is "script"'
307 // http://whatwg.org/html#scriptEndTag
308 void HTMLScriptRunner::runScript(Element* script, const TextPosition& scriptStartPosition)
309 {
310     ASSERT(m_document);
311     ASSERT(!hasParserBlockingScript());
312     {
313         ScriptLoader* scriptLoader = toScriptLoaderIfPossible(script);
314
315         // This contains both and ASSERTION and a null check since we should not
316         // be getting into the case of a null script element, but seem to be from
317         // time to time. The assertion is left in to help find those cases and
318         // is being tracked by <https://bugs.webkit.org/show_bug.cgi?id=60559>.
319         ASSERT(scriptLoader);
320         if (!scriptLoader)
321             return;
322
323         ASSERT(scriptLoader->isParserInserted());
324
325         if (!isExecutingScript())
326             Microtask::performCheckpoint();
327
328         InsertionPointRecord insertionPointRecord(m_host->inputStream());
329         NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel);
330
331         scriptLoader->prepareScript(scriptStartPosition);
332
333         if (!scriptLoader->willBeParserExecuted())
334             return;
335
336         if (scriptLoader->willExecuteWhenDocumentFinishedParsing()) {
337             requestDeferredScript(script);
338         } else if (scriptLoader->readyToBeParserExecuted()) {
339             if (m_scriptNestingLevel == 1) {
340                 m_parserBlockingScript.setElement(script);
341                 m_parserBlockingScript.setStartingPosition(scriptStartPosition);
342             } else {
343                 ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition);
344                 scriptLoader->executeScript(sourceCode);
345             }
346         } else {
347             requestParsingBlockingScript(script);
348         }
349     }
350 }
351
352 void HTMLScriptRunner::trace(Visitor* visitor)
353 {
354     visitor->trace(m_document);
355     visitor->trace(m_host);
356     visitor->trace(m_parserBlockingScript);
357     visitor->trace(m_scriptsToExecuteAfterParsing);
358 }
359
360 }