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