OLD | NEW |
(Empty) | |
| 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/ScriptStreamerThread.h" |
| 10 #include "bindings/core/v8/V8Binding.h" |
| 11 #include "bindings/core/v8/V8ScriptRunner.h" |
| 12 #include "core/dom/PendingScript.h" |
| 13 #include "core/frame/Settings.h" |
| 14 #include "platform/Task.h" |
| 15 #include "public/platform/Platform.h" |
| 16 |
| 17 #include <gtest/gtest.h> |
| 18 #include <v8.h> |
| 19 |
| 20 namespace blink { |
| 21 |
| 22 namespace { |
| 23 |
| 24 class ScriptStreamingTest : public testing::Test { |
| 25 public: |
| 26 ScriptStreamingTest() |
| 27 : m_scope(v8::Isolate::GetCurrent()) |
| 28 , m_settings(Settings::create()) |
| 29 , m_resourceRequest("http://www.streaming-test.com/") |
| 30 , m_resource(new ScriptResource(m_resourceRequest, "text/utf-8")) |
| 31 , m_pendingScript(0, m_resource) // Takes ownership of m_resource. |
| 32 { |
| 33 m_settings->setV8ScriptStreamingEnabled(true); |
| 34 m_resource->setLoading(true); |
| 35 } |
| 36 |
| 37 ScriptState* scriptState() const { return m_scope.scriptState(); } |
| 38 v8::Isolate* isolate() const { return m_scope.isolate(); } |
| 39 |
| 40 protected: |
| 41 void appendData(const char* data) |
| 42 { |
| 43 m_resource->appendData(data, strlen(data)); |
| 44 // Yield control to the background thread, so that V8 gets a change to |
| 45 // process the data before the main thread adds more. Note that we |
| 46 // cannot fully control in what kind of chunks the data is passed to V8 |
| 47 // (if the V8 is not requesting more data between two appendData calls, |
| 48 // V8 will get both chunks together). |
| 49 WTF::yield(); |
| 50 } |
| 51 |
| 52 void appendPadding() |
| 53 { |
| 54 for (int i = 0; i < 10; ++i) { |
| 55 appendData(" /* this is padding to make the script long enough, so " |
| 56 "that V8's buffer gets filled and it starts processing " |
| 57 "the data */ "); |
| 58 } |
| 59 } |
| 60 |
| 61 void finish() |
| 62 { |
| 63 m_resource->finish(); |
| 64 m_resource->setLoading(false); |
| 65 } |
| 66 |
| 67 void processTasksUntilStreamingComplete() |
| 68 { |
| 69 while (ScriptStreamerThread::shared()->isRunningTask()) { |
| 70 WebThread* currentThread = blink::Platform::current()->currentThread
(); |
| 71 currentThread->postTask(new Task(WTF::bind(&WebThread::exitRunLoop,
currentThread))); |
| 72 currentThread->enterRunLoop(); |
| 73 } |
| 74 } |
| 75 |
| 76 V8TestingScope m_scope; |
| 77 OwnPtr<Settings> m_settings; |
| 78 // The Resource and PendingScript where we stream from. These don't really |
| 79 // fetch any data outside the test; the test controls the data by calling |
| 80 // ScriptResource::appendData. |
| 81 ResourceRequest m_resourceRequest; |
| 82 ScriptResource* m_resource; |
| 83 PendingScript m_pendingScript; |
| 84 }; |
| 85 |
| 86 class TestScriptResourceClient : public ScriptResourceClient { |
| 87 public: |
| 88 TestScriptResourceClient() |
| 89 : m_finished(false) { } |
| 90 |
| 91 virtual void notifyFinished(Resource*) OVERRIDE { m_finished = true; } |
| 92 |
| 93 bool finished() const { return m_finished; } |
| 94 |
| 95 private: |
| 96 bool m_finished; |
| 97 }; |
| 98 |
| 99 TEST_F(ScriptStreamingTest, CompilingStreamedScript) |
| 100 { |
| 101 // Test that we can successfully compile a streamed script. |
| 102 bool started = ScriptStreamer::startStreaming(m_pendingScript, m_settings.ge
t(), m_scope.scriptState()); |
| 103 TestScriptResourceClient client; |
| 104 m_pendingScript.watchForLoad(&client); |
| 105 EXPECT_TRUE(started); |
| 106 |
| 107 appendData("function foo() {"); |
| 108 appendPadding(); |
| 109 appendData("return 5; }"); |
| 110 appendPadding(); |
| 111 appendData("foo();"); |
| 112 EXPECT_FALSE(client.finished()); |
| 113 finish(); |
| 114 |
| 115 // Process tasks on the main thread until the streaming background thread |
| 116 // has completed its tasks. |
| 117 processTasksUntilStreamingComplete(); |
| 118 EXPECT_TRUE(client.finished()); |
| 119 bool errorOccurred = false; |
| 120 ScriptSourceCode sourceCode = m_pendingScript.getSource(KURL(), errorOccurre
d); |
| 121 EXPECT_FALSE(errorOccurred); |
| 122 EXPECT_TRUE(sourceCode.streamer()); |
| 123 v8::TryCatch tryCatch; |
| 124 v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(sourceCode, is
olate()); |
| 125 EXPECT_FALSE(script.IsEmpty()); |
| 126 EXPECT_FALSE(tryCatch.HasCaught()); |
| 127 } |
| 128 |
| 129 TEST_F(ScriptStreamingTest, CompilingStreamedScriptWithParseError) |
| 130 { |
| 131 // Test that scripts with parse errors are handled properly. In those cases, |
| 132 // the V8 side typically finished before loading finishes: make sure we |
| 133 // handle it gracefully. |
| 134 bool started = ScriptStreamer::startStreaming(m_pendingScript, m_settings.ge
t(), m_scope.scriptState()); |
| 135 TestScriptResourceClient client; |
| 136 m_pendingScript.watchForLoad(&client); |
| 137 EXPECT_TRUE(started); |
| 138 appendData("function foo() {"); |
| 139 appendData("this is the part which will be a parse error"); |
| 140 // V8 won't realize the parse error until it actually starts parsing the |
| 141 // script, and this happens only when its buffer is filled. |
| 142 appendPadding(); |
| 143 |
| 144 EXPECT_FALSE(client.finished()); |
| 145 |
| 146 // Force the V8 side to finish before the loading. |
| 147 processTasksUntilStreamingComplete(); |
| 148 EXPECT_FALSE(client.finished()); |
| 149 |
| 150 finish(); |
| 151 EXPECT_TRUE(client.finished()); |
| 152 |
| 153 bool errorOccurred = false; |
| 154 ScriptSourceCode sourceCode = m_pendingScript.getSource(KURL(), errorOccurre
d); |
| 155 EXPECT_FALSE(errorOccurred); |
| 156 EXPECT_TRUE(sourceCode.streamer()); |
| 157 v8::TryCatch tryCatch; |
| 158 v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(sourceCode, is
olate()); |
| 159 EXPECT_TRUE(script.IsEmpty()); |
| 160 EXPECT_TRUE(tryCatch.HasCaught()); |
| 161 } |
| 162 |
| 163 TEST_F(ScriptStreamingTest, CancellingStreaming) |
| 164 { |
| 165 // Test that the upper layers (PendingScript and up) can be ramped down |
| 166 // while streaming is ongoing, and ScriptStreamer handles it gracefully. |
| 167 bool started = ScriptStreamer::startStreaming(m_pendingScript, m_settings.ge
t(), m_scope.scriptState()); |
| 168 TestScriptResourceClient client; |
| 169 m_pendingScript.watchForLoad(&client); |
| 170 EXPECT_TRUE(started); |
| 171 appendData("function foo() {"); |
| 172 |
| 173 // In general, we cannot control what the background thread is doing |
| 174 // (whether it's parsing or waiting for more data). In this test, we have |
| 175 // given it so little data that it's surely waiting for more. |
| 176 |
| 177 // Simulate cancelling the network load (e.g., because the user navigated |
| 178 // away). |
| 179 EXPECT_FALSE(client.finished()); |
| 180 m_pendingScript.stopWatchingForLoad(&client); |
| 181 m_pendingScript = PendingScript(); // This will destroy m_resource. |
| 182 m_resource = 0; |
| 183 |
| 184 // The V8 side will complete too. This should not crash. We don't receive |
| 185 // any results from the streaming and the client doesn't get notified. |
| 186 processTasksUntilStreamingComplete(); |
| 187 EXPECT_FALSE(client.finished()); |
| 188 } |
| 189 |
| 190 TEST_F(ScriptStreamingTest, SuppressingStreaming) |
| 191 { |
| 192 // If we notice during streaming that there is a code cache, streaming |
| 193 // is suppressed (V8 doesn't parse while the script is loading), and the |
| 194 // upper layer (ScriptResourceClient) should get a notification when the |
| 195 // script is loaded. |
| 196 bool started = ScriptStreamer::startStreaming(m_pendingScript, m_settings.ge
t(), m_scope.scriptState()); |
| 197 TestScriptResourceClient client; |
| 198 m_pendingScript.watchForLoad(&client); |
| 199 EXPECT_TRUE(started); |
| 200 appendData("function foo() {"); |
| 201 appendPadding(); |
| 202 |
| 203 m_resource->setCachedMetadata(V8ScriptRunner::tagForCodeCache(), "X", 1, Res
ource::CacheLocally); |
| 204 |
| 205 appendPadding(); |
| 206 finish(); |
| 207 processTasksUntilStreamingComplete(); |
| 208 EXPECT_TRUE(client.finished()); |
| 209 |
| 210 bool errorOccurred = false; |
| 211 ScriptSourceCode sourceCode = m_pendingScript.getSource(KURL(), errorOccurre
d); |
| 212 EXPECT_FALSE(errorOccurred); |
| 213 // ScriptSourceCode doesn't refer to the streamer, since we have suppressed |
| 214 // the streaming and resumed the non-streaming code path for script |
| 215 // compilation. |
| 216 EXPECT_FALSE(sourceCode.streamer()); |
| 217 } |
| 218 |
| 219 } // namespace |
| 220 |
| 221 } // namespace blink |
OLD | NEW |