| 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 #include "bindings/core/v8/ScriptStreamer.h" | 5 #include "bindings/core/v8/ScriptStreamer.h" |
| 6 | 6 |
| 7 #include "bindings/core/v8/ScriptSourceCode.h" | 7 #include "bindings/core/v8/ScriptSourceCode.h" |
| 8 #include "bindings/core/v8/ScriptStreamerThread.h" | 8 #include "bindings/core/v8/ScriptStreamerThread.h" |
| 9 #include "bindings/core/v8/V8Binding.h" | 9 #include "bindings/core/v8/V8Binding.h" |
| 10 #include "bindings/core/v8/V8BindingForTesting.h" | 10 #include "bindings/core/v8/V8BindingForTesting.h" |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 84 WebTaskRunner* m_loadingTaskRunner; // NOT OWNED | 84 WebTaskRunner* m_loadingTaskRunner; // NOT OWNED |
| 85 std::unique_ptr<Settings> m_settings; | 85 std::unique_ptr<Settings> m_settings; |
| 86 // The Resource and PendingScript where we stream from. These don't really | 86 // The Resource and PendingScript where we stream from. These don't really |
| 87 // fetch any data outside the test; the test controls the data by calling | 87 // fetch any data outside the test; the test controls the data by calling |
| 88 // ScriptResource::appendData. | 88 // ScriptResource::appendData. |
| 89 ResourceRequest m_resourceRequest; | 89 ResourceRequest m_resourceRequest; |
| 90 Persistent<ScriptResource> m_resource; | 90 Persistent<ScriptResource> m_resource; |
| 91 Persistent<PendingScript> m_pendingScript; | 91 Persistent<PendingScript> m_pendingScript; |
| 92 }; | 92 }; |
| 93 | 93 |
| 94 class TestScriptResourceClient | 94 class TestPendingScriptClient |
| 95 : public GarbageCollectedFinalized<TestScriptResourceClient>, | 95 : public GarbageCollectedFinalized<TestPendingScriptClient>, |
| 96 public ScriptResourceClient { | 96 public PendingScriptClient { |
| 97 USING_GARBAGE_COLLECTED_MIXIN(TestScriptResourceClient); | 97 USING_GARBAGE_COLLECTED_MIXIN(TestPendingScriptClient); |
| 98 | 98 |
| 99 public: | 99 public: |
| 100 TestScriptResourceClient() : m_finished(false) {} | 100 TestPendingScriptClient() : m_finished(false) {} |
| 101 | 101 void pendingScriptFinished(PendingScript*) override { m_finished = true; } |
| 102 void notifyFinished(Resource*) override { m_finished = true; } | |
| 103 String debugName() const override { return "TestScriptResourceClient"; } | |
| 104 | |
| 105 bool finished() const { return m_finished; } | 102 bool finished() const { return m_finished; } |
| 106 | 103 |
| 107 private: | 104 private: |
| 108 bool m_finished; | 105 bool m_finished; |
| 109 }; | 106 }; |
| 110 | 107 |
| 111 TEST_F(ScriptStreamingTest, CompilingStreamedScript) { | 108 TEST_F(ScriptStreamingTest, CompilingStreamedScript) { |
| 112 // Test that we can successfully compile a streamed script. | 109 // Test that we can successfully compile a streamed script. |
| 113 V8TestingScope scope; | 110 V8TestingScope scope; |
| 114 ScriptStreamer::startStreaming( | 111 ScriptStreamer::startStreaming( |
| 115 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), | 112 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), |
| 116 scope.getScriptState(), m_loadingTaskRunner); | 113 scope.getScriptState(), m_loadingTaskRunner); |
| 117 TestScriptResourceClient* client = new TestScriptResourceClient; | 114 TestPendingScriptClient* client = new TestPendingScriptClient; |
| 118 getPendingScript()->watchForLoad(client); | 115 getPendingScript()->watchForLoad(client); |
| 119 | 116 |
| 120 appendData("function foo() {"); | 117 appendData("function foo() {"); |
| 121 appendPadding(); | 118 appendPadding(); |
| 122 appendData("return 5; }"); | 119 appendData("return 5; }"); |
| 123 appendPadding(); | 120 appendPadding(); |
| 124 appendData("foo();"); | 121 appendData("foo();"); |
| 125 EXPECT_FALSE(client->finished()); | 122 EXPECT_FALSE(client->finished()); |
| 126 finish(); | 123 finish(); |
| 127 | 124 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 142 } | 139 } |
| 143 | 140 |
| 144 TEST_F(ScriptStreamingTest, CompilingStreamedScriptWithParseError) { | 141 TEST_F(ScriptStreamingTest, CompilingStreamedScriptWithParseError) { |
| 145 // Test that scripts with parse errors are handled properly. In those cases, | 142 // Test that scripts with parse errors are handled properly. In those cases, |
| 146 // the V8 side typically finished before loading finishes: make sure we | 143 // the V8 side typically finished before loading finishes: make sure we |
| 147 // handle it gracefully. | 144 // handle it gracefully. |
| 148 V8TestingScope scope; | 145 V8TestingScope scope; |
| 149 ScriptStreamer::startStreaming( | 146 ScriptStreamer::startStreaming( |
| 150 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), | 147 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), |
| 151 scope.getScriptState(), m_loadingTaskRunner); | 148 scope.getScriptState(), m_loadingTaskRunner); |
| 152 TestScriptResourceClient* client = new TestScriptResourceClient; | 149 TestPendingScriptClient* client = new TestPendingScriptClient; |
| 153 getPendingScript()->watchForLoad(client); | 150 getPendingScript()->watchForLoad(client); |
| 154 appendData("function foo() {"); | 151 appendData("function foo() {"); |
| 155 appendData("this is the part which will be a parse error"); | 152 appendData("this is the part which will be a parse error"); |
| 156 // V8 won't realize the parse error until it actually starts parsing the | 153 // V8 won't realize the parse error until it actually starts parsing the |
| 157 // script, and this happens only when its buffer is filled. | 154 // script, and this happens only when its buffer is filled. |
| 158 appendPadding(); | 155 appendPadding(); |
| 159 | 156 |
| 160 EXPECT_FALSE(client->finished()); | 157 EXPECT_FALSE(client->finished()); |
| 161 | 158 |
| 162 // Force the V8 side to finish before the loading. | 159 // Force the V8 side to finish before the loading. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 178 EXPECT_TRUE(tryCatch.HasCaught()); | 175 EXPECT_TRUE(tryCatch.HasCaught()); |
| 179 } | 176 } |
| 180 | 177 |
| 181 TEST_F(ScriptStreamingTest, CancellingStreaming) { | 178 TEST_F(ScriptStreamingTest, CancellingStreaming) { |
| 182 // Test that the upper layers (PendingScript and up) can be ramped down | 179 // Test that the upper layers (PendingScript and up) can be ramped down |
| 183 // while streaming is ongoing, and ScriptStreamer handles it gracefully. | 180 // while streaming is ongoing, and ScriptStreamer handles it gracefully. |
| 184 V8TestingScope scope; | 181 V8TestingScope scope; |
| 185 ScriptStreamer::startStreaming( | 182 ScriptStreamer::startStreaming( |
| 186 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), | 183 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), |
| 187 scope.getScriptState(), m_loadingTaskRunner); | 184 scope.getScriptState(), m_loadingTaskRunner); |
| 188 TestScriptResourceClient* client = new TestScriptResourceClient; | 185 TestPendingScriptClient* client = new TestPendingScriptClient; |
| 189 getPendingScript()->watchForLoad(client); | 186 getPendingScript()->watchForLoad(client); |
| 190 appendData("function foo() {"); | 187 appendData("function foo() {"); |
| 191 | 188 |
| 192 // In general, we cannot control what the background thread is doing | 189 // In general, we cannot control what the background thread is doing |
| 193 // (whether it's parsing or waiting for more data). In this test, we have | 190 // (whether it's parsing or waiting for more data). In this test, we have |
| 194 // given it so little data that it's surely waiting for more. | 191 // given it so little data that it's surely waiting for more. |
| 195 | 192 |
| 196 // Simulate cancelling the network load (e.g., because the user navigated | 193 // Simulate cancelling the network load (e.g., because the user navigated |
| 197 // away). | 194 // away). |
| 198 EXPECT_FALSE(client->finished()); | 195 EXPECT_FALSE(client->finished()); |
| 199 getPendingScript()->dispose(); | 196 getPendingScript()->dispose(); |
| 200 m_pendingScript = nullptr; // This will destroy m_resource. | 197 m_pendingScript = nullptr; // This will destroy m_resource. |
| 201 m_resource = nullptr; | 198 m_resource = nullptr; |
| 202 | 199 |
| 203 // The V8 side will complete too. This should not crash. We don't receive | 200 // The V8 side will complete too. This should not crash. We don't receive |
| 204 // any results from the streaming and the client doesn't get notified. | 201 // any results from the streaming and the client doesn't get notified. |
| 205 processTasksUntilStreamingComplete(); | 202 processTasksUntilStreamingComplete(); |
| 206 EXPECT_FALSE(client->finished()); | 203 EXPECT_FALSE(client->finished()); |
| 207 } | 204 } |
| 208 | 205 |
| 209 TEST_F(ScriptStreamingTest, SuppressingStreaming) { | 206 TEST_F(ScriptStreamingTest, SuppressingStreaming) { |
| 210 // If we notice during streaming that there is a code cache, streaming | 207 // If we notice during streaming that there is a code cache, streaming |
| 211 // is suppressed (V8 doesn't parse while the script is loading), and the | 208 // is suppressed (V8 doesn't parse while the script is loading), and the |
| 212 // upper layer (ScriptResourceClient) should get a notification when the | 209 // upper layer (ScriptResourceClient) should get a notification when the |
| 213 // script is loaded. | 210 // script is loaded. |
| 214 V8TestingScope scope; | 211 V8TestingScope scope; |
| 215 ScriptStreamer::startStreaming( | 212 ScriptStreamer::startStreaming( |
| 216 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), | 213 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), |
| 217 scope.getScriptState(), m_loadingTaskRunner); | 214 scope.getScriptState(), m_loadingTaskRunner); |
| 218 TestScriptResourceClient* client = new TestScriptResourceClient; | 215 TestPendingScriptClient* client = new TestPendingScriptClient; |
| 219 getPendingScript()->watchForLoad(client); | 216 getPendingScript()->watchForLoad(client); |
| 220 appendData("function foo() {"); | 217 appendData("function foo() {"); |
| 221 appendPadding(); | 218 appendPadding(); |
| 222 | 219 |
| 223 CachedMetadataHandler* cacheHandler = m_resource->cacheHandler(); | 220 CachedMetadataHandler* cacheHandler = m_resource->cacheHandler(); |
| 224 EXPECT_TRUE(cacheHandler); | 221 EXPECT_TRUE(cacheHandler); |
| 225 cacheHandler->setCachedMetadata(V8ScriptRunner::tagForCodeCache(cacheHandler), | 222 cacheHandler->setCachedMetadata(V8ScriptRunner::tagForCodeCache(cacheHandler), |
| 226 "X", 1, CachedMetadataHandler::CacheLocally); | 223 "X", 1, CachedMetadataHandler::CacheLocally); |
| 227 | 224 |
| 228 appendPadding(); | 225 appendPadding(); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 241 } | 238 } |
| 242 | 239 |
| 243 TEST_F(ScriptStreamingTest, EmptyScripts) { | 240 TEST_F(ScriptStreamingTest, EmptyScripts) { |
| 244 // Empty scripts should also be streamed properly, that is, the upper layer | 241 // Empty scripts should also be streamed properly, that is, the upper layer |
| 245 // (ScriptResourceClient) should be notified when an empty script has been | 242 // (ScriptResourceClient) should be notified when an empty script has been |
| 246 // loaded. | 243 // loaded. |
| 247 V8TestingScope scope; | 244 V8TestingScope scope; |
| 248 ScriptStreamer::startStreaming( | 245 ScriptStreamer::startStreaming( |
| 249 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), | 246 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), |
| 250 scope.getScriptState(), m_loadingTaskRunner); | 247 scope.getScriptState(), m_loadingTaskRunner); |
| 251 TestScriptResourceClient* client = new TestScriptResourceClient; | 248 TestPendingScriptClient* client = new TestPendingScriptClient; |
| 252 getPendingScript()->watchForLoad(client); | 249 getPendingScript()->watchForLoad(client); |
| 253 | 250 |
| 254 // Finish the script without sending any data. | 251 // Finish the script without sending any data. |
| 255 finish(); | 252 finish(); |
| 256 // The finished notification should arrive immediately and not be cycled | 253 // The finished notification should arrive immediately and not be cycled |
| 257 // through a background thread. | 254 // through a background thread. |
| 258 EXPECT_TRUE(client->finished()); | 255 EXPECT_TRUE(client->finished()); |
| 259 | 256 |
| 260 bool errorOccurred = false; | 257 bool errorOccurred = false; |
| 261 ScriptSourceCode sourceCode = | 258 ScriptSourceCode sourceCode = |
| 262 getPendingScript()->getSource(KURL(), errorOccurred); | 259 getPendingScript()->getSource(KURL(), errorOccurred); |
| 263 EXPECT_FALSE(errorOccurred); | 260 EXPECT_FALSE(errorOccurred); |
| 264 EXPECT_FALSE(sourceCode.streamer()); | 261 EXPECT_FALSE(sourceCode.streamer()); |
| 265 } | 262 } |
| 266 | 263 |
| 267 TEST_F(ScriptStreamingTest, SmallScripts) { | 264 TEST_F(ScriptStreamingTest, SmallScripts) { |
| 268 // Small scripts shouldn't be streamed. | 265 // Small scripts shouldn't be streamed. |
| 269 V8TestingScope scope; | 266 V8TestingScope scope; |
| 270 ScriptStreamer::setSmallScriptThresholdForTesting(100); | 267 ScriptStreamer::setSmallScriptThresholdForTesting(100); |
| 271 | 268 |
| 272 ScriptStreamer::startStreaming( | 269 ScriptStreamer::startStreaming( |
| 273 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), | 270 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), |
| 274 scope.getScriptState(), m_loadingTaskRunner); | 271 scope.getScriptState(), m_loadingTaskRunner); |
| 275 TestScriptResourceClient* client = new TestScriptResourceClient; | 272 TestPendingScriptClient* client = new TestPendingScriptClient; |
| 276 getPendingScript()->watchForLoad(client); | 273 getPendingScript()->watchForLoad(client); |
| 277 | 274 |
| 278 appendData("function foo() { }"); | 275 appendData("function foo() { }"); |
| 279 | 276 |
| 280 finish(); | 277 finish(); |
| 281 | 278 |
| 282 // The finished notification should arrive immediately and not be cycled | 279 // The finished notification should arrive immediately and not be cycled |
| 283 // through a background thread. | 280 // through a background thread. |
| 284 EXPECT_TRUE(client->finished()); | 281 EXPECT_TRUE(client->finished()); |
| 285 | 282 |
| 286 bool errorOccurred = false; | 283 bool errorOccurred = false; |
| 287 ScriptSourceCode sourceCode = | 284 ScriptSourceCode sourceCode = |
| 288 getPendingScript()->getSource(KURL(), errorOccurred); | 285 getPendingScript()->getSource(KURL(), errorOccurred); |
| 289 EXPECT_FALSE(errorOccurred); | 286 EXPECT_FALSE(errorOccurred); |
| 290 EXPECT_FALSE(sourceCode.streamer()); | 287 EXPECT_FALSE(sourceCode.streamer()); |
| 291 } | 288 } |
| 292 | 289 |
| 293 TEST_F(ScriptStreamingTest, ScriptsWithSmallFirstChunk) { | 290 TEST_F(ScriptStreamingTest, ScriptsWithSmallFirstChunk) { |
| 294 // If a script is long enough, if should be streamed, even if the first data | 291 // If a script is long enough, if should be streamed, even if the first data |
| 295 // chunk is small. | 292 // chunk is small. |
| 296 V8TestingScope scope; | 293 V8TestingScope scope; |
| 297 ScriptStreamer::setSmallScriptThresholdForTesting(100); | 294 ScriptStreamer::setSmallScriptThresholdForTesting(100); |
| 298 | 295 |
| 299 ScriptStreamer::startStreaming( | 296 ScriptStreamer::startStreaming( |
| 300 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), | 297 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), |
| 301 scope.getScriptState(), m_loadingTaskRunner); | 298 scope.getScriptState(), m_loadingTaskRunner); |
| 302 TestScriptResourceClient* client = new TestScriptResourceClient; | 299 TestPendingScriptClient* client = new TestPendingScriptClient; |
| 303 getPendingScript()->watchForLoad(client); | 300 getPendingScript()->watchForLoad(client); |
| 304 | 301 |
| 305 // This is the first data chunk which is small. | 302 // This is the first data chunk which is small. |
| 306 appendData("function foo() { }"); | 303 appendData("function foo() { }"); |
| 307 appendPadding(); | 304 appendPadding(); |
| 308 appendPadding(); | 305 appendPadding(); |
| 309 appendPadding(); | 306 appendPadding(); |
| 310 | 307 |
| 311 finish(); | 308 finish(); |
| 312 | 309 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 326 | 323 |
| 327 TEST_F(ScriptStreamingTest, EncodingChanges) { | 324 TEST_F(ScriptStreamingTest, EncodingChanges) { |
| 328 // It's possible that the encoding of the Resource changes after we start | 325 // It's possible that the encoding of the Resource changes after we start |
| 329 // loading it. | 326 // loading it. |
| 330 V8TestingScope scope; | 327 V8TestingScope scope; |
| 331 m_resource->setEncoding("windows-1252"); | 328 m_resource->setEncoding("windows-1252"); |
| 332 | 329 |
| 333 ScriptStreamer::startStreaming( | 330 ScriptStreamer::startStreaming( |
| 334 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), | 331 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), |
| 335 scope.getScriptState(), m_loadingTaskRunner); | 332 scope.getScriptState(), m_loadingTaskRunner); |
| 336 TestScriptResourceClient* client = new TestScriptResourceClient; | 333 TestPendingScriptClient* client = new TestPendingScriptClient; |
| 337 getPendingScript()->watchForLoad(client); | 334 getPendingScript()->watchForLoad(client); |
| 338 | 335 |
| 339 m_resource->setEncoding("UTF-8"); | 336 m_resource->setEncoding("UTF-8"); |
| 340 // \xec\x92\x81 are the raw bytes for \uc481. | 337 // \xec\x92\x81 are the raw bytes for \uc481. |
| 341 appendData( | 338 appendData( |
| 342 "function foo() { var foob\xec\x92\x81r = 13; return foob\xec\x92\x81r; " | 339 "function foo() { var foob\xec\x92\x81r = 13; return foob\xec\x92\x81r; " |
| 343 "} foo();"); | 340 "} foo();"); |
| 344 | 341 |
| 345 finish(); | 342 finish(); |
| 346 | 343 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 361 TEST_F(ScriptStreamingTest, EncodingFromBOM) { | 358 TEST_F(ScriptStreamingTest, EncodingFromBOM) { |
| 362 // Byte order marks should be removed before giving the data to V8. They | 359 // Byte order marks should be removed before giving the data to V8. They |
| 363 // will also affect encoding detection. | 360 // will also affect encoding detection. |
| 364 V8TestingScope scope; | 361 V8TestingScope scope; |
| 365 m_resource->setEncoding( | 362 m_resource->setEncoding( |
| 366 "windows-1252"); // This encoding is wrong on purpose. | 363 "windows-1252"); // This encoding is wrong on purpose. |
| 367 | 364 |
| 368 ScriptStreamer::startStreaming( | 365 ScriptStreamer::startStreaming( |
| 369 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), | 366 getPendingScript(), ScriptStreamer::ParsingBlocking, m_settings.get(), |
| 370 scope.getScriptState(), m_loadingTaskRunner); | 367 scope.getScriptState(), m_loadingTaskRunner); |
| 371 TestScriptResourceClient* client = new TestScriptResourceClient; | 368 TestPendingScriptClient* client = new TestPendingScriptClient; |
| 372 getPendingScript()->watchForLoad(client); | 369 getPendingScript()->watchForLoad(client); |
| 373 | 370 |
| 374 // \xef\xbb\xbf is the UTF-8 byte order mark. \xec\x92\x81 are the raw bytes | 371 // \xef\xbb\xbf is the UTF-8 byte order mark. \xec\x92\x81 are the raw bytes |
| 375 // for \uc481. | 372 // for \uc481. |
| 376 appendData( | 373 appendData( |
| 377 "\xef\xbb\xbf function foo() { var foob\xec\x92\x81r = 13; return " | 374 "\xef\xbb\xbf function foo() { var foob\xec\x92\x81r = 13; return " |
| 378 "foob\xec\x92\x81r; } foo();"); | 375 "foob\xec\x92\x81r; } foo();"); |
| 379 | 376 |
| 380 finish(); | 377 finish(); |
| 381 processTasksUntilStreamingComplete(); | 378 processTasksUntilStreamingComplete(); |
| 382 EXPECT_TRUE(client->finished()); | 379 EXPECT_TRUE(client->finished()); |
| 383 bool errorOccurred = false; | 380 bool errorOccurred = false; |
| 384 ScriptSourceCode sourceCode = | 381 ScriptSourceCode sourceCode = |
| 385 getPendingScript()->getSource(KURL(), errorOccurred); | 382 getPendingScript()->getSource(KURL(), errorOccurred); |
| 386 EXPECT_FALSE(errorOccurred); | 383 EXPECT_FALSE(errorOccurred); |
| 387 EXPECT_TRUE(sourceCode.streamer()); | 384 EXPECT_TRUE(sourceCode.streamer()); |
| 388 v8::TryCatch tryCatch(scope.isolate()); | 385 v8::TryCatch tryCatch(scope.isolate()); |
| 389 v8::Local<v8::Script> script; | 386 v8::Local<v8::Script> script; |
| 390 EXPECT_TRUE(V8ScriptRunner::compileScript(sourceCode, scope.isolate()) | 387 EXPECT_TRUE(V8ScriptRunner::compileScript(sourceCode, scope.isolate()) |
| 391 .ToLocal(&script)); | 388 .ToLocal(&script)); |
| 392 EXPECT_FALSE(tryCatch.HasCaught()); | 389 EXPECT_FALSE(tryCatch.HasCaught()); |
| 393 } | 390 } |
| 394 | 391 |
| 395 } // namespace | 392 } // namespace |
| 396 | 393 |
| 397 } // namespace blink | 394 } // namespace blink |
| OLD | NEW |