Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / core / v8 / ScriptStreamer.cpp
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "bindings/core/v8/ScriptStreamer.h"
7
8 #include "bindings/core/v8/ScriptStreamerThread.h"
9 #include "bindings/core/v8/V8ScriptRunner.h"
10 #include "core/dom/Document.h"
11 #include "core/dom/Element.h"
12 #include "core/dom/PendingScript.h"
13 #include "core/fetch/ScriptResource.h"
14 #include "core/frame/Settings.h"
15 #include "platform/SharedBuffer.h"
16 #include "platform/TraceEvent.h"
17 #include "public/platform/Platform.h"
18 #include "wtf/MainThread.h"
19 #include "wtf/text/TextEncodingRegistry.h"
20
21 namespace blink {
22
23 // For passing data between the main thread (producer) and the streamer thread
24 // (consumer). The main thread prepares the data (copies it from Resource) and
25 // the streamer thread feeds it to V8.
26 class SourceStreamDataQueue {
27     WTF_MAKE_NONCOPYABLE(SourceStreamDataQueue);
28 public:
29     SourceStreamDataQueue()
30         : m_finished(false) { }
31
32     ~SourceStreamDataQueue()
33     {
34         while (!m_data.isEmpty()) {
35             std::pair<const uint8_t*, size_t> next_data = m_data.takeFirst();
36             delete[] next_data.first;
37         }
38     }
39
40     void produce(const uint8_t* data, size_t length)
41     {
42         MutexLocker locker(m_mutex);
43         m_data.append(std::make_pair(data, length));
44         m_haveData.signal();
45     }
46
47     void finish()
48     {
49         MutexLocker locker(m_mutex);
50         m_finished = true;
51         m_haveData.signal();
52     }
53
54     void consume(const uint8_t** data, size_t* length)
55     {
56         MutexLocker locker(m_mutex);
57         while (!tryGetData(data, length))
58             m_haveData.wait(m_mutex);
59     }
60
61 private:
62     bool tryGetData(const uint8_t** data, size_t* length)
63     {
64         if (!m_data.isEmpty()) {
65             std::pair<const uint8_t*, size_t> next_data = m_data.takeFirst();
66             *data = next_data.first;
67             *length = next_data.second;
68             return true;
69         }
70         if (m_finished) {
71             *length = 0;
72             return true;
73         }
74         return false;
75     }
76
77     WTF::Deque<std::pair<const uint8_t*, size_t> > m_data;
78     bool m_finished;
79     Mutex m_mutex;
80     ThreadCondition m_haveData;
81 };
82
83
84 // SourceStream implements the streaming interface towards V8. The main
85 // functionality is preparing the data to give to V8 on main thread, and
86 // actually giving the data (via GetMoreData which is called on a background
87 // thread).
88 class SourceStream : public v8::ScriptCompiler::ExternalSourceStream {
89     WTF_MAKE_NONCOPYABLE(SourceStream);
90 public:
91     SourceStream(ScriptStreamer* streamer)
92         : v8::ScriptCompiler::ExternalSourceStream()
93         , m_streamer(streamer)
94         , m_cancelled(false)
95         , m_dataPosition(0) { }
96
97     virtual ~SourceStream() { }
98
99     // Called by V8 on a background thread. Should block until we can return
100     // some data.
101     virtual size_t GetMoreData(const uint8_t** src) override
102     {
103         ASSERT(!isMainThread());
104         {
105             MutexLocker locker(m_mutex);
106             if (m_cancelled)
107                 return 0;
108         }
109         size_t length = 0;
110         // This will wait until there is data.
111         m_dataQueue.consume(src, &length);
112         {
113             MutexLocker locker(m_mutex);
114             if (m_cancelled)
115                 return 0;
116         }
117         return length;
118     }
119
120     void didFinishLoading()
121     {
122         ASSERT(isMainThread());
123         m_dataQueue.finish();
124     }
125
126     void didReceiveData()
127     {
128         ASSERT(isMainThread());
129         prepareDataOnMainThread();
130     }
131
132     void cancel()
133     {
134         ASSERT(isMainThread());
135         // The script is no longer needed by the upper layers. Stop streaming
136         // it. The next time GetMoreData is called (or woken up), it will return
137         // 0, which will be interpreted as EOS by V8 and the parsing will
138         // fail. ScriptStreamer::streamingComplete will be called, and at that
139         // point we will release the references to SourceStream.
140         {
141             MutexLocker locker(m_mutex);
142             m_cancelled = true;
143         }
144         m_dataQueue.finish();
145     }
146
147 private:
148     void prepareDataOnMainThread()
149     {
150         ASSERT(isMainThread());
151         // The Resource must still be alive; otherwise we should've cancelled
152         // the streaming (if we have cancelled, the background thread is not
153         // waiting).
154         ASSERT(m_streamer->resource());
155
156         if (m_streamer->resource()->cachedMetadata(V8ScriptRunner::tagForCodeCache())) {
157             // The resource has a code cache, so it's unnecessary to stream and
158             // parse the code. Cancel the streaming and resume the non-streaming
159             // code path.
160             m_streamer->suppressStreaming();
161             {
162                 MutexLocker locker(m_mutex);
163                 m_cancelled = true;
164             }
165             m_dataQueue.finish();
166             return;
167         }
168
169         if (!m_resourceBuffer) {
170             // We don't have a buffer yet. Try to get it from the resource.
171             SharedBuffer* buffer = m_streamer->resource()->resourceBuffer();
172             if (!buffer)
173                 return;
174             m_resourceBuffer = RefPtr<SharedBuffer>(buffer);
175         }
176
177         // Get as much data from the ResourceBuffer as we can.
178         const char* data = 0;
179         Vector<const char*> chunks;
180         Vector<unsigned> chunkLengths;
181         size_t dataLength = 0;
182         while (unsigned length = m_resourceBuffer->getSomeData(data, m_dataPosition)) {
183             // FIXME: Here we can limit based on the total length, if it turns
184             // out that we don't want to give all the data we have (memory
185             // vs. speed).
186             chunks.append(data);
187             chunkLengths.append(length);
188             dataLength += length;
189             m_dataPosition += length;
190         }
191         // Copy the data chunks into a new buffer, since we're going to give the
192         // data to a background thread.
193         if (dataLength > 0) {
194             uint8_t* copiedData = new uint8_t[dataLength];
195             unsigned offset = 0;
196             for (size_t i = 0; i < chunks.size(); ++i) {
197                 memcpy(copiedData + offset, chunks[i], chunkLengths[i]);
198                 offset += chunkLengths[i];
199             }
200             m_dataQueue.produce(copiedData, dataLength);
201         }
202     }
203
204     ScriptStreamer* m_streamer;
205
206     // For coordinating between the main thread and background thread tasks.
207     // Guarded by m_mutex.
208     bool m_cancelled;
209     Mutex m_mutex;
210
211     unsigned m_dataPosition; // Only used by the main thread.
212     RefPtr<SharedBuffer> m_resourceBuffer; // Only used by the main thread.
213     SourceStreamDataQueue m_dataQueue; // Thread safe.
214 };
215
216 size_t ScriptStreamer::kSmallScriptThreshold = 30 * 1024;
217
218 void ScriptStreamer::startStreaming(PendingScript& script, Settings* settings, ScriptState* scriptState, PendingScript::Type scriptType)
219 {
220     // We don't yet know whether the script will really be streamed. E.g.,
221     // suppressing streaming for short scripts is done later. Record only the
222     // sure negative cases here.
223     bool startedStreaming = startStreamingInternal(script, settings, scriptState, scriptType);
224     if (!startedStreaming)
225         blink::Platform::current()->histogramEnumeration(startedStreamingHistogramName(scriptType), 0, 2);
226 }
227
228 void ScriptStreamer::streamingCompleteOnBackgroundThread()
229 {
230     ASSERT(!isMainThread());
231     MutexLocker locker(m_mutex);
232     m_parsingFinished = true;
233     // In the blocking case, the main thread is normally waiting at this
234     // point, but it can also happen that the load is not yet finished
235     // (e.g., a parse error). In that case, notifyFinished will be called
236     // eventually and it will not wait on m_parsingFinishedCondition.
237
238     // In the non-blocking case, notifyFinished might already be called, or it
239     // might be called in the future. In any case, do the cleanup here.
240     if (m_mainThreadWaitingForParserThread) {
241         m_parsingFinishedCondition.signal();
242     } else {
243         callOnMainThread(WTF::bind(&ScriptStreamer::streamingComplete, this));
244     }
245 }
246
247 void ScriptStreamer::cancel()
248 {
249     ASSERT(isMainThread());
250     // The upper layer doesn't need the script any more, but streaming might
251     // still be ongoing. Tell SourceStream to try to cancel it whenever it gets
252     // the control the next time. It can also be that V8 has already completed
253     // its operations and streamingComplete will be called soon.
254     m_detached = true;
255     m_resource = 0;
256     if (m_stream)
257         m_stream->cancel();
258 }
259
260 void ScriptStreamer::suppressStreaming()
261 {
262     MutexLocker locker(m_mutex);
263     ASSERT(!m_loadingFinished);
264     // It can be that the parsing task has already finished (e.g., if there was
265     // a parse error).
266     m_streamingSuppressed = true;
267 }
268
269 unsigned ScriptStreamer::cachedDataType() const
270 {
271     if (m_compileOptions == v8::ScriptCompiler::kProduceParserCache) {
272         return V8ScriptRunner::tagForParserCache();
273     }
274     if (m_compileOptions == v8::ScriptCompiler::kProduceCodeCache) {
275         return V8ScriptRunner::tagForCodeCache();
276     }
277     return 0;
278 }
279
280 void ScriptStreamer::notifyAppendData(ScriptResource* resource)
281 {
282     ASSERT(isMainThread());
283     ASSERT(m_resource == resource);
284     {
285         MutexLocker locker(m_mutex);
286         if (m_streamingSuppressed)
287             return;
288     }
289     if (!m_haveEnoughDataForStreaming) {
290         // Even if the first data chunk is small, the script can still be big
291         // enough - wait until the next data chunk comes before deciding whether
292         // to start the streaming.
293         if (resource->resourceBuffer()->size() < kSmallScriptThreshold) {
294             return;
295         }
296         m_haveEnoughDataForStreaming = true;
297         const char* histogramName = startedStreamingHistogramName(m_scriptType);
298
299         // Encoding should be detected only when we have some data. It's
300         // possible that resource->encoding() returns a different encoding
301         // before the loading has started and after we got some data.
302         WTF::TextEncoding textEncoding(resource->encoding());
303         const char* encodingName = textEncoding.name();
304
305         // Here's a list of encodings we can use for streaming. These are
306         // the canonical names.
307         v8::ScriptCompiler::StreamedSource::Encoding encoding;
308         if (strcmp(encodingName, "windows-1252") == 0
309             || strcmp(encodingName, "ISO-8859-1") == 0
310             || strcmp(encodingName, "US-ASCII") == 0) {
311             encoding = v8::ScriptCompiler::StreamedSource::ONE_BYTE;
312         } else if (strcmp(encodingName, "UTF-8") == 0) {
313             encoding = v8::ScriptCompiler::StreamedSource::UTF8;
314         } else {
315             // We don't stream other encodings; especially we don't stream two
316             // byte scripts to avoid the handling of byte order marks. Most
317             // scripts are Latin1 or UTF-8 anyway, so this should be enough for
318             // most real world purposes.
319             suppressStreaming();
320             blink::Platform::current()->histogramEnumeration(histogramName, 0, 2);
321             return;
322         }
323         if (ScriptStreamerThread::shared()->isRunningTask()) {
324             // At the moment we only have one thread for running the tasks. A
325             // new task shouldn't be queued before the running task completes,
326             // because the running task can block and wait for data from the
327             // network.
328             suppressStreaming();
329             blink::Platform::current()->histogramEnumeration(histogramName, 0, 2);
330             return;
331         }
332
333         if (!m_scriptState->contextIsValid()) {
334             suppressStreaming();
335             blink::Platform::current()->histogramEnumeration(histogramName, 0, 2);
336             return;
337         }
338
339         ASSERT(!m_stream);
340         ASSERT(!m_source);
341         m_stream = new SourceStream(this);
342         // m_source takes ownership of m_stream.
343         m_source = adoptPtr(new v8::ScriptCompiler::StreamedSource(m_stream, encoding));
344
345         ScriptState::Scope scope(m_scriptState.get());
346         WTF::OwnPtr<v8::ScriptCompiler::ScriptStreamingTask> scriptStreamingTask(adoptPtr(v8::ScriptCompiler::StartStreamingScript(m_scriptState->isolate(), m_source.get(), m_compileOptions)));
347         if (!scriptStreamingTask) {
348             // V8 cannot stream the script.
349             suppressStreaming();
350             m_stream = 0;
351             m_source.clear();
352             blink::Platform::current()->histogramEnumeration(histogramName, 0, 2);
353             return;
354         }
355
356         // ScriptStreamer needs to stay alive as long as the background task is
357         // running. This is taken care of with a manual ref() & deref() pair;
358         // the corresponding deref() is in streamingComplete or in
359         // notifyFinished.
360         ref();
361         ScriptStreamingTask* task = new ScriptStreamingTask(scriptStreamingTask.release(), this);
362         ScriptStreamerThread::shared()->postTask(task);
363         blink::Platform::current()->histogramEnumeration(histogramName, 1, 2);
364     }
365     if (m_stream)
366         m_stream->didReceiveData();
367 }
368
369 void ScriptStreamer::notifyFinished(Resource* resource)
370 {
371     ASSERT(isMainThread());
372     ASSERT(m_resource == resource);
373     // A special case: empty and small scripts. We didn't receive enough data to
374     // start the streaming before this notification. In that case, there won't
375     // be a "parsing complete" notification either, and we should not wait for
376     // it.
377     if (!m_haveEnoughDataForStreaming) {
378         const char* histogramName = startedStreamingHistogramName(m_scriptType);
379         blink::Platform::current()->histogramEnumeration(histogramName, 0, 2);
380         suppressStreaming();
381     }
382     if (m_stream)
383         m_stream->didFinishLoading();
384     m_loadingFinished = true;
385
386     if (shouldBlockMainThread()) {
387         // Make the main thead wait until the streaming is complete, to make
388         // sure that the script gets the main thread's attention as early as
389         // possible (for possible compiling, if the client wants to do it
390         // right away). Note that blocking here is not any worse than the
391         // non-streaming code path where the main thread eventually blocks
392         // to parse the script.
393         TRACE_EVENT0("v8", "v8.mainThreadWaitingForParserThread");
394         MutexLocker locker(m_mutex);
395         while (!isFinished()) {
396             ASSERT(!m_parsingFinished);
397             ASSERT(!m_streamingSuppressed);
398             m_mainThreadWaitingForParserThread = true;
399             m_parsingFinishedCondition.wait(m_mutex);
400         }
401     }
402
403     // Calling notifyFinishedToClient can result into the upper layers dropping
404     // references to ScriptStreamer. Keep it alive until this function ends.
405     RefPtr<ScriptStreamer> protect(this);
406
407     notifyFinishedToClient();
408
409     if (m_mainThreadWaitingForParserThread) {
410         ASSERT(m_parsingFinished);
411         ASSERT(!m_streamingSuppressed);
412         // streamingComplete won't be called, so do the ramp-down work
413         // here.
414         deref();
415     }
416 }
417
418 ScriptStreamer::ScriptStreamer(ScriptResource* resource, PendingScript::Type scriptType, ScriptStreamingMode mode, ScriptState* scriptState, v8::ScriptCompiler::CompileOptions compileOptions)
419     : m_resource(resource)
420     , m_detached(false)
421     , m_stream(0)
422     , m_client(0)
423     , m_loadingFinished(false)
424     , m_parsingFinished(false)
425     , m_haveEnoughDataForStreaming(false)
426     , m_streamingSuppressed(false)
427     , m_compileOptions(compileOptions)
428     , m_scriptState(scriptState)
429     , m_scriptType(scriptType)
430     , m_scriptStreamingMode(mode)
431     , m_mainThreadWaitingForParserThread(false)
432 {
433 }
434
435 void ScriptStreamer::streamingComplete()
436 {
437     // The background task is completed; do the necessary ramp-down in the main
438     // thread.
439     ASSERT(isMainThread());
440
441     // It's possible that the corresponding Resource was deleted before V8
442     // finished streaming. In that case, the data or the notification is not
443     // needed. In addition, if the streaming is suppressed, the non-streaming
444     // code path will resume after the resource has loaded, before the
445     // background task finishes.
446     if (m_detached || m_streamingSuppressed) {
447         deref();
448         return;
449     }
450
451     // We have now streamed the whole script to V8 and it has parsed the
452     // script. We're ready for the next step: compiling and executing the
453     // script.
454     notifyFinishedToClient();
455
456     // The background thread no longer holds an implicit reference.
457     deref();
458 }
459
460 void ScriptStreamer::notifyFinishedToClient()
461 {
462     ASSERT(isMainThread());
463     // Usually, the loading will be finished first, and V8 will still need some
464     // time to catch up. But the other way is possible too: if V8 detects a
465     // parse error, the V8 side can complete before loading has finished. Send
466     // the notification after both loading and V8 side operations have
467     // completed. Here we also check that we have a client: it can happen that a
468     // function calling notifyFinishedToClient was already scheduled in the task
469     // queue and the upper layer decided that it's not interested in the script
470     // and called removeClient.
471     {
472         MutexLocker locker(m_mutex);
473         if (!isFinished())
474             return;
475     }
476     if (m_client)
477         m_client->notifyFinished(m_resource);
478 }
479
480 const char* ScriptStreamer::startedStreamingHistogramName(PendingScript::Type scriptType)
481 {
482     switch (scriptType) {
483     case PendingScript::ParsingBlocking:
484         return "WebCore.Scripts.ParsingBlocking.StartedStreaming";
485         break;
486     case PendingScript::Deferred:
487         return "WebCore.Scripts.Deferred.StartedStreaming";
488         break;
489     case PendingScript::Async:
490         return "WebCore.Scripts.Async.StartedStreaming";
491         break;
492     default:
493         ASSERT_NOT_REACHED();
494         break;
495     }
496     return 0;
497 }
498
499 bool ScriptStreamer::startStreamingInternal(PendingScript& script, Settings* settings, ScriptState* scriptState, PendingScript::Type scriptType)
500 {
501     ASSERT(isMainThread());
502     if (!settings || !settings->v8ScriptStreamingEnabled())
503         return false;
504     if (settings->v8ScriptStreamingMode() == ScriptStreamingModeOnlyAsyncAndDefer
505         && scriptType == PendingScript::ParsingBlocking)
506         return false;
507
508     ScriptResource* resource = script.resource();
509     if (resource->isLoaded())
510         return false;
511     if (!resource->url().protocolIsInHTTPFamily())
512         return false;
513     if (resource->resourceToRevalidate()) {
514         // This happens e.g., during reloads. We're actually not going to load
515         // the current Resource of the PendingScript but switch to another
516         // Resource -> don't stream.
517         return false;
518     }
519     // We cannot filter out short scripts, even if we wait for the HTTP headers
520     // to arrive. In general, the web servers don't seem to send the
521     // Content-Length HTTP header for scripts.
522
523     if (!scriptState->contextIsValid())
524         return false;
525
526     // Decide what kind of cached data we should produce while streaming. By
527     // default, we generate the parser cache for streamed scripts, to emulate
528     // the non-streaming behavior (see V8ScriptRunner::compileScript).
529     v8::ScriptCompiler::CompileOptions compileOption = v8::ScriptCompiler::kProduceParserCache;
530     if (settings->v8CacheOptions() == V8CacheOptionsCode)
531         compileOption = v8::ScriptCompiler::kProduceCodeCache;
532
533     // The Resource might go out of scope if the script is no longer
534     // needed. This makes PendingScript notify the ScriptStreamer when it is
535     // destroyed.
536     script.setStreamer(adoptRef(new ScriptStreamer(resource, scriptType, settings->v8ScriptStreamingMode(), scriptState, compileOption)));
537
538     return true;
539 }
540
541 } // namespace blink