Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / core / v8 / ScriptStreamerTest.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
6 #include "config.h"
7 #include "bindings/core/v8/ScriptStreamer.h"
8
9 #include "bindings/core/v8/ScriptSourceCode.h"
10 #include "bindings/core/v8/ScriptStreamerThread.h"
11 #include "bindings/core/v8/ScriptStreamingMode.h"
12 #include "bindings/core/v8/V8Binding.h"
13 #include "bindings/core/v8/V8ScriptRunner.h"
14 #include "core/dom/PendingScript.h"
15 #include "core/frame/Settings.h"
16 #include "platform/Task.h"
17 #include "platform/heap/Handle.h"
18 #include "public/platform/Platform.h"
19
20 #include <gtest/gtest.h>
21 #include <v8.h>
22
23 namespace blink {
24
25 namespace {
26
27 // For the benefit of Oilpan, put the part object PendingScript inside
28 // a wrapper that's on the Oilpan heap and hold a reference to that wrapper
29 // from ScriptStreamingTest.
30 class PendingScriptWrapper : public NoBaseWillBeGarbageCollectedFinalized<PendingScriptWrapper> {
31 public:
32     static PassOwnPtrWillBeRawPtr<PendingScriptWrapper> create()
33     {
34         return adoptPtrWillBeNoop(new PendingScriptWrapper());
35     }
36
37     static PassOwnPtrWillBeRawPtr<PendingScriptWrapper> create(Element* element, ScriptResource* resource)
38     {
39         return adoptPtrWillBeNoop(new PendingScriptWrapper(element, resource));
40     }
41
42     PendingScript& get() { return m_pendingScript; }
43
44     void trace(Visitor* visitor)
45     {
46         visitor->trace(m_pendingScript);
47     }
48
49 private:
50     PendingScriptWrapper()
51     {
52     }
53
54     PendingScriptWrapper(Element* element, ScriptResource* resource)
55         : m_pendingScript(PendingScript(element, resource))
56     {
57     }
58
59     PendingScript m_pendingScript;
60 };
61
62 // The bool param for ScriptStreamingTest controls whether to make the main
63 // thread block and wait for parsing.
64 class ScriptStreamingTest : public testing::TestWithParam<bool> {
65 public:
66     ScriptStreamingTest()
67         : m_scope(v8::Isolate::GetCurrent())
68         , m_settings(Settings::create())
69         , m_resourceRequest("http://www.streaming-test.com/")
70         , m_resource(new ScriptResource(m_resourceRequest, "text/utf-8"))
71         , m_pendingScript(PendingScriptWrapper::create(0, m_resource)) // Takes ownership of m_resource.
72     {
73         m_settings->setV8ScriptStreamingEnabled(true);
74         if (GetParam())
75             m_settings->setV8ScriptStreamingMode(ScriptStreamingModeAllPlusBlockParsingBlocking);
76         m_resource->setLoading(true);
77         ScriptStreamer::setSmallScriptThresholdForTesting(0);
78     }
79
80     ScriptState* scriptState() const { return m_scope.scriptState(); }
81     v8::Isolate* isolate() const { return m_scope.isolate(); }
82
83     PendingScript& pendingScript() const { return m_pendingScript->get(); }
84
85 protected:
86     void appendData(const char* data)
87     {
88         m_resource->appendData(data, strlen(data));
89         // Yield control to the background thread, so that V8 gets a change to
90         // process the data before the main thread adds more. Note that we
91         // cannot fully control in what kind of chunks the data is passed to V8
92         // (if the V8 is not requesting more data between two appendData calls,
93         // V8 will get both chunks together).
94         Platform::current()->yieldCurrentThread();
95     }
96
97     void appendPadding()
98     {
99         for (int i = 0; i < 10; ++i) {
100             appendData(" /* this is padding to make the script long enough, so "
101                 "that V8's buffer gets filled and it starts processing "
102                 "the data */ ");
103         }
104     }
105
106     void finish()
107     {
108         m_resource->finish();
109         m_resource->setLoading(false);
110     }
111
112     void processTasksUntilStreamingComplete()
113     {
114         WebThread* currentThread = blink::Platform::current()->currentThread();
115         while (ScriptStreamerThread::shared()->isRunningTask()) {
116             currentThread->postTask(new Task(WTF::bind(&WebThread::exitRunLoop, currentThread)));
117             currentThread->enterRunLoop();
118         }
119         // Once more, because the "streaming complete" notification might only
120         // now be in the task queue.
121         currentThread->postTask(new Task(WTF::bind(&WebThread::exitRunLoop, currentThread)));
122         currentThread->enterRunLoop();
123     }
124
125     V8TestingScope m_scope;
126     OwnPtr<Settings> m_settings;
127     // The Resource and PendingScript where we stream from. These don't really
128     // fetch any data outside the test; the test controls the data by calling
129     // ScriptResource::appendData.
130     ResourceRequest m_resourceRequest;
131     ScriptResource* m_resource;
132     OwnPtrWillBePersistent<PendingScriptWrapper> m_pendingScript;
133 };
134
135 class TestScriptResourceClient : public ScriptResourceClient {
136 public:
137     TestScriptResourceClient()
138         : m_finished(false) { }
139
140     virtual void notifyFinished(Resource*) override { m_finished = true; }
141
142     bool finished() const { return m_finished; }
143
144 private:
145     bool m_finished;
146 };
147
148 TEST_P(ScriptStreamingTest, CompilingStreamedScript)
149 {
150     // Test that we can successfully compile a streamed script.
151     ScriptStreamer::startStreaming(pendingScript(), m_settings.get(), m_scope.scriptState(), PendingScript::ParsingBlocking);
152     TestScriptResourceClient client;
153     pendingScript().watchForLoad(&client);
154
155     appendData("function foo() {");
156     appendPadding();
157     appendData("return 5; }");
158     appendPadding();
159     appendData("foo();");
160     EXPECT_FALSE(client.finished());
161     finish();
162
163     // Process tasks on the main thread until the streaming background thread
164     // has completed its tasks.
165     processTasksUntilStreamingComplete();
166     EXPECT_TRUE(client.finished());
167     bool errorOccurred = false;
168     ScriptSourceCode sourceCode = pendingScript().getSource(KURL(), errorOccurred);
169     EXPECT_FALSE(errorOccurred);
170     EXPECT_TRUE(sourceCode.streamer());
171     v8::TryCatch tryCatch;
172     v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(sourceCode, isolate());
173     EXPECT_FALSE(script.IsEmpty());
174     EXPECT_FALSE(tryCatch.HasCaught());
175 }
176
177 TEST_P(ScriptStreamingTest, CompilingStreamedScriptWithParseError)
178 {
179     // Test that scripts with parse errors are handled properly. In those cases,
180     // the V8 side typically finished before loading finishes: make sure we
181     // handle it gracefully.
182     ScriptStreamer::startStreaming(pendingScript(), m_settings.get(), m_scope.scriptState(), PendingScript::ParsingBlocking);
183     TestScriptResourceClient client;
184     pendingScript().watchForLoad(&client);
185     appendData("function foo() {");
186     appendData("this is the part which will be a parse error");
187     // V8 won't realize the parse error until it actually starts parsing the
188     // script, and this happens only when its buffer is filled.
189     appendPadding();
190
191     EXPECT_FALSE(client.finished());
192
193     // Force the V8 side to finish before the loading.
194     processTasksUntilStreamingComplete();
195     EXPECT_FALSE(client.finished());
196
197     finish();
198     EXPECT_TRUE(client.finished());
199
200     bool errorOccurred = false;
201     ScriptSourceCode sourceCode = pendingScript().getSource(KURL(), errorOccurred);
202     EXPECT_FALSE(errorOccurred);
203     EXPECT_TRUE(sourceCode.streamer());
204     v8::TryCatch tryCatch;
205     v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(sourceCode, isolate());
206     EXPECT_TRUE(script.IsEmpty());
207     EXPECT_TRUE(tryCatch.HasCaught());
208 }
209
210 TEST_P(ScriptStreamingTest, CancellingStreaming)
211 {
212     // Test that the upper layers (PendingScript and up) can be ramped down
213     // while streaming is ongoing, and ScriptStreamer handles it gracefully.
214     ScriptStreamer::startStreaming(pendingScript(), m_settings.get(), m_scope.scriptState(), PendingScript::ParsingBlocking);
215     TestScriptResourceClient client;
216     pendingScript().watchForLoad(&client);
217     appendData("function foo() {");
218
219     // In general, we cannot control what the background thread is doing
220     // (whether it's parsing or waiting for more data). In this test, we have
221     // given it so little data that it's surely waiting for more.
222
223     // Simulate cancelling the network load (e.g., because the user navigated
224     // away).
225     EXPECT_FALSE(client.finished());
226     pendingScript().stopWatchingForLoad(&client);
227     pendingScript().releaseElementAndClear();
228     m_pendingScript = PendingScriptWrapper::create(); // This will destroy m_resource.
229     m_resource = 0;
230
231     // The V8 side will complete too. This should not crash. We don't receive
232     // any results from the streaming and the client doesn't get notified.
233     processTasksUntilStreamingComplete();
234     EXPECT_FALSE(client.finished());
235 }
236
237 TEST_P(ScriptStreamingTest, SuppressingStreaming)
238 {
239     // If we notice during streaming that there is a code cache, streaming
240     // is suppressed (V8 doesn't parse while the script is loading), and the
241     // upper layer (ScriptResourceClient) should get a notification when the
242     // script is loaded.
243     ScriptStreamer::startStreaming(pendingScript(), m_settings.get(), m_scope.scriptState(), PendingScript::ParsingBlocking);
244     TestScriptResourceClient client;
245     pendingScript().watchForLoad(&client);
246     appendData("function foo() {");
247     appendPadding();
248
249     m_resource->setCachedMetadata(V8ScriptRunner::tagForCodeCache(), "X", 1, Resource::CacheLocally);
250
251     appendPadding();
252     finish();
253     processTasksUntilStreamingComplete();
254     EXPECT_TRUE(client.finished());
255
256     bool errorOccurred = false;
257     ScriptSourceCode sourceCode = pendingScript().getSource(KURL(), errorOccurred);
258     EXPECT_FALSE(errorOccurred);
259     // ScriptSourceCode doesn't refer to the streamer, since we have suppressed
260     // the streaming and resumed the non-streaming code path for script
261     // compilation.
262     EXPECT_FALSE(sourceCode.streamer());
263 }
264
265 TEST_P(ScriptStreamingTest, EmptyScripts)
266 {
267     // Empty scripts should also be streamed properly, that is, the upper layer
268     // (ScriptResourceClient) should be notified when an empty script has been
269     // loaded.
270     ScriptStreamer::startStreaming(pendingScript(), m_settings.get(), m_scope.scriptState(), PendingScript::ParsingBlocking);
271     TestScriptResourceClient client;
272     pendingScript().watchForLoad(&client);
273
274     // Finish the script without sending any data.
275     finish();
276     // The finished notification should arrive immediately and not be cycled
277     // through a background thread.
278     EXPECT_TRUE(client.finished());
279
280     bool errorOccurred = false;
281     ScriptSourceCode sourceCode = pendingScript().getSource(KURL(), errorOccurred);
282     EXPECT_FALSE(errorOccurred);
283     EXPECT_FALSE(sourceCode.streamer());
284 }
285
286 TEST_P(ScriptStreamingTest, SmallScripts)
287 {
288     // Small scripts shouldn't be streamed.
289     ScriptStreamer::setSmallScriptThresholdForTesting(100);
290
291     ScriptStreamer::startStreaming(pendingScript(), m_settings.get(), m_scope.scriptState(), PendingScript::ParsingBlocking);
292     TestScriptResourceClient client;
293     pendingScript().watchForLoad(&client);
294
295     appendData("function foo() { }");
296
297     finish();
298
299     // The finished notification should arrive immediately and not be cycled
300     // through a background thread.
301     EXPECT_TRUE(client.finished());
302
303     bool errorOccurred = false;
304     ScriptSourceCode sourceCode = pendingScript().getSource(KURL(), errorOccurred);
305     EXPECT_FALSE(errorOccurred);
306     EXPECT_FALSE(sourceCode.streamer());
307 }
308
309 TEST_P(ScriptStreamingTest, ScriptsWithSmallFirstChunk)
310 {
311     // If a script is long enough, if should be streamed, even if the first data
312     // chunk is small.
313     ScriptStreamer::setSmallScriptThresholdForTesting(100);
314
315     ScriptStreamer::startStreaming(pendingScript(), m_settings.get(), m_scope.scriptState(), PendingScript::ParsingBlocking);
316     TestScriptResourceClient client;
317     pendingScript().watchForLoad(&client);
318
319     // This is the first data chunk which is small.
320     appendData("function foo() { }");
321     appendPadding();
322     appendPadding();
323     appendPadding();
324
325     finish();
326
327     processTasksUntilStreamingComplete();
328     EXPECT_TRUE(client.finished());
329     bool errorOccurred = false;
330     ScriptSourceCode sourceCode = pendingScript().getSource(KURL(), errorOccurred);
331     EXPECT_FALSE(errorOccurred);
332     EXPECT_TRUE(sourceCode.streamer());
333     v8::TryCatch tryCatch;
334     v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(sourceCode, isolate());
335     EXPECT_FALSE(script.IsEmpty());
336     EXPECT_FALSE(tryCatch.HasCaught());
337 }
338
339 TEST_P(ScriptStreamingTest, EncodingChanges)
340 {
341     // It's possible that the encoding of the Resource changes after we start
342     // loading it.
343     m_resource->setEncoding("windows-1252");
344
345     ScriptStreamer::startStreaming(pendingScript(), m_settings.get(), m_scope.scriptState(), PendingScript::ParsingBlocking);
346     TestScriptResourceClient client;
347     pendingScript().watchForLoad(&client);
348
349     m_resource->setEncoding("UTF-8");
350     // \xec\x92\x81 are the raw bytes for \uc481.
351     appendData("function foo() { var foob\xec\x92\x81r = 13; return foob\xec\x92\x81r; } foo();");
352
353     finish();
354
355     processTasksUntilStreamingComplete();
356     EXPECT_TRUE(client.finished());
357     bool errorOccurred = false;
358     ScriptSourceCode sourceCode = pendingScript().getSource(KURL(), errorOccurred);
359     EXPECT_FALSE(errorOccurred);
360     EXPECT_TRUE(sourceCode.streamer());
361     v8::TryCatch tryCatch;
362     v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(sourceCode, isolate());
363     EXPECT_FALSE(script.IsEmpty());
364     EXPECT_FALSE(tryCatch.HasCaught());
365 }
366
367 INSTANTIATE_TEST_CASE_P(ScriptStreamingInstantiation, ScriptStreamingTest, ::testing::Values(false, true));
368
369 } // namespace
370
371 } // namespace blink