Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(94)

Side by Side Diff: Source/bindings/core/v8/ScriptStreamer.cpp

Issue 708093002: Script streaming: remove byte order marks, detect encoding based on them. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: fix: don't persist cached data produced by streaming Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | Source/bindings/core/v8/ScriptStreamerTest.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "config.h" 5 #include "config.h"
6 #include "bindings/core/v8/ScriptStreamer.h" 6 #include "bindings/core/v8/ScriptStreamer.h"
7 7
8 #include "bindings/core/v8/ScriptStreamerThread.h" 8 #include "bindings/core/v8/ScriptStreamerThread.h"
9 #include "bindings/core/v8/V8ScriptRunner.h" 9 #include "bindings/core/v8/V8ScriptRunner.h"
10 #include "core/dom/Document.h" 10 #include "core/dom/Document.h"
11 #include "core/dom/Element.h" 11 #include "core/dom/Element.h"
12 #include "core/dom/PendingScript.h" 12 #include "core/dom/PendingScript.h"
13 #include "core/fetch/ScriptResource.h" 13 #include "core/fetch/ScriptResource.h"
14 #include "core/frame/Settings.h" 14 #include "core/frame/Settings.h"
15 #include "core/html/parser/TextResourceDecoder.h"
15 #include "platform/SharedBuffer.h" 16 #include "platform/SharedBuffer.h"
16 #include "platform/TraceEvent.h" 17 #include "platform/TraceEvent.h"
17 #include "public/platform/Platform.h" 18 #include "public/platform/Platform.h"
18 #include "wtf/MainThread.h" 19 #include "wtf/MainThread.h"
19 #include "wtf/text/TextEncodingRegistry.h" 20 #include "wtf/text/TextEncodingRegistry.h"
20 21
21 namespace blink { 22 namespace blink {
22 23
23 // For passing data between the main thread (producer) and the streamer thread 24 // 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 // (consumer). The main thread prepares the data (copies it from Resource) and
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
116 } 117 }
117 return length; 118 return length;
118 } 119 }
119 120
120 void didFinishLoading() 121 void didFinishLoading()
121 { 122 {
122 ASSERT(isMainThread()); 123 ASSERT(isMainThread());
123 m_dataQueue.finish(); 124 m_dataQueue.finish();
124 } 125 }
125 126
126 void didReceiveData() 127 void didReceiveData(size_t lengthOfBOM)
127 { 128 {
128 ASSERT(isMainThread()); 129 ASSERT(isMainThread());
129 prepareDataOnMainThread(); 130 prepareDataOnMainThread(lengthOfBOM);
130 } 131 }
131 132
132 void cancel() 133 void cancel()
133 { 134 {
134 ASSERT(isMainThread()); 135 ASSERT(isMainThread());
135 // The script is no longer needed by the upper layers. Stop streaming 136 // 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 // 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 // 0, which will be interpreted as EOS by V8 and the parsing will
138 // fail. ScriptStreamer::streamingComplete will be called, and at that 139 // fail. ScriptStreamer::streamingComplete will be called, and at that
139 // point we will release the references to SourceStream. 140 // point we will release the references to SourceStream.
140 { 141 {
141 MutexLocker locker(m_mutex); 142 MutexLocker locker(m_mutex);
142 m_cancelled = true; 143 m_cancelled = true;
143 } 144 }
144 m_dataQueue.finish(); 145 m_dataQueue.finish();
145 } 146 }
146 147
147 private: 148 private:
148 void prepareDataOnMainThread() 149 void prepareDataOnMainThread(size_t lengthOfBOM)
149 { 150 {
150 ASSERT(isMainThread()); 151 ASSERT(isMainThread());
151 // The Resource must still be alive; otherwise we should've cancelled 152 // The Resource must still be alive; otherwise we should've cancelled
152 // the streaming (if we have cancelled, the background thread is not 153 // the streaming (if we have cancelled, the background thread is not
153 // waiting). 154 // waiting).
154 ASSERT(m_streamer->resource()); 155 ASSERT(m_streamer->resource());
155 156
157 // BOM can only occur at the beginning of the data.
158 ASSERT(lengthOfBOM == 0 || m_dataPosition == 0);
159
156 if (m_streamer->resource()->cachedMetadata(V8ScriptRunner::tagForCodeCac he())) { 160 if (m_streamer->resource()->cachedMetadata(V8ScriptRunner::tagForCodeCac he())) {
157 // The resource has a code cache, so it's unnecessary to stream and 161 // 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 162 // parse the code. Cancel the streaming and resume the non-streaming
159 // code path. 163 // code path.
160 m_streamer->suppressStreaming(); 164 m_streamer->suppressStreaming();
161 { 165 {
162 MutexLocker locker(m_mutex); 166 MutexLocker locker(m_mutex);
163 m_cancelled = true; 167 m_cancelled = true;
164 } 168 }
165 m_dataQueue.finish(); 169 m_dataQueue.finish();
166 return; 170 return;
167 } 171 }
168 172
169 if (!m_resourceBuffer) { 173 if (!m_resourceBuffer) {
170 // We don't have a buffer yet. Try to get it from the resource. 174 // We don't have a buffer yet. Try to get it from the resource.
171 SharedBuffer* buffer = m_streamer->resource()->resourceBuffer(); 175 SharedBuffer* buffer = m_streamer->resource()->resourceBuffer();
172 if (!buffer)
173 return;
174 m_resourceBuffer = RefPtr<SharedBuffer>(buffer); 176 m_resourceBuffer = RefPtr<SharedBuffer>(buffer);
175 } 177 }
176 178
177 // Get as much data from the ResourceBuffer as we can. 179 // Get as much data from the ResourceBuffer as we can.
178 const char* data = 0; 180 const char* data = 0;
179 Vector<const char*> chunks; 181 Vector<const char*> chunks;
180 Vector<unsigned> chunkLengths; 182 Vector<unsigned> chunkLengths;
181 size_t dataLength = 0; 183 size_t dataLength = 0;
182 while (unsigned length = m_resourceBuffer->getSomeData(data, m_dataPosit ion)) { 184 while (unsigned length = m_resourceBuffer->getSomeData(data, m_dataPosit ion)) {
183 // FIXME: Here we can limit based on the total length, if it turns 185 // 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 186 // out that we don't want to give all the data we have (memory
185 // vs. speed). 187 // vs. speed).
186 chunks.append(data); 188 chunks.append(data);
187 chunkLengths.append(length); 189 chunkLengths.append(length);
188 dataLength += length; 190 dataLength += length;
189 m_dataPosition += length; 191 m_dataPosition += length;
190 } 192 }
191 // Copy the data chunks into a new buffer, since we're going to give the 193 // Copy the data chunks into a new buffer, since we're going to give the
192 // data to a background thread. 194 // data to a background thread.
193 if (dataLength > 0) { 195 if (dataLength > lengthOfBOM) {
196 dataLength -= lengthOfBOM;
194 uint8_t* copiedData = new uint8_t[dataLength]; 197 uint8_t* copiedData = new uint8_t[dataLength];
195 unsigned offset = 0; 198 unsigned offset = 0;
196 for (size_t i = 0; i < chunks.size(); ++i) { 199 for (size_t i = 0; i < chunks.size(); ++i) {
197 memcpy(copiedData + offset, chunks[i], chunkLengths[i]); 200 memcpy(copiedData + offset, chunks[i] + lengthOfBOM, chunkLength s[i] - lengthOfBOM);
198 offset += chunkLengths[i]; 201 offset += chunkLengths[i] - lengthOfBOM;
202 // BOM is only in the first chunk
203 lengthOfBOM = 0;
199 } 204 }
200 m_dataQueue.produce(copiedData, dataLength); 205 m_dataQueue.produce(copiedData, dataLength);
201 } 206 }
202 } 207 }
203 208
204 ScriptStreamer* m_streamer; 209 ScriptStreamer* m_streamer;
205 210
206 // For coordinating between the main thread and background thread tasks. 211 // For coordinating between the main thread and background thread tasks.
207 // Guarded by m_mutex. 212 // Guarded by m_mutex.
208 bool m_cancelled; 213 bool m_cancelled;
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 284
280 void ScriptStreamer::notifyAppendData(ScriptResource* resource) 285 void ScriptStreamer::notifyAppendData(ScriptResource* resource)
281 { 286 {
282 ASSERT(isMainThread()); 287 ASSERT(isMainThread());
283 ASSERT(m_resource == resource); 288 ASSERT(m_resource == resource);
284 { 289 {
285 MutexLocker locker(m_mutex); 290 MutexLocker locker(m_mutex);
286 if (m_streamingSuppressed) 291 if (m_streamingSuppressed)
287 return; 292 return;
288 } 293 }
294 size_t lengthOfBOM = 0;
289 if (!m_haveEnoughDataForStreaming) { 295 if (!m_haveEnoughDataForStreaming) {
290 // Even if the first data chunk is small, the script can still be big 296 // 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 297 // enough - wait until the next data chunk comes before deciding whether
292 // to start the streaming. 298 // to start the streaming.
293 if (resource->resourceBuffer()->size() < kSmallScriptThreshold) { 299 ASSERT(resource->resourceBuffer());
300 if (resource->resourceBuffer()->size() < kSmallScriptThreshold)
294 return; 301 return;
295 }
296 m_haveEnoughDataForStreaming = true; 302 m_haveEnoughDataForStreaming = true;
297 const char* histogramName = startedStreamingHistogramName(m_scriptType); 303 const char* histogramName = startedStreamingHistogramName(m_scriptType);
298 304
299 // Encoding should be detected only when we have some data. It's 305 // Encoding should be detected only when we have some data. It's
300 // possible that resource->encoding() returns a different encoding 306 // possible that resource->encoding() returns a different encoding
301 // before the loading has started and after we got some data. 307 // before the loading has started and after we got some data. In
302 WTF::TextEncoding textEncoding(resource->encoding()); 308 // addition, check for byte order marks. Note that checking the byte
303 const char* encodingName = textEncoding.name(); 309 // order mark might change the encoding. We cannot decode the full text
310 // here, because it might contain incomplete UTF-8 characters. Also note
311 // that have at least kSmallScriptThreshold worth of data, which is more
312 // than enough for detecting a BOM.
313 const char* data = 0;
314 unsigned length = resource->resourceBuffer()->getSomeData(data, 0);
315
316 OwnPtr<TextResourceDecoder> decoder(TextResourceDecoder::create("applica tion/javascript", resource->encoding()));
317 lengthOfBOM = decoder->checkForBOM(data, length);
318
319 // Maybe the encoding changed because we saw the BOM; get the encoding
320 // from the decoder.
321 const char* encodingName = decoder->encoding().name();
304 322
305 // Here's a list of encodings we can use for streaming. These are 323 // Here's a list of encodings we can use for streaming. These are
306 // the canonical names. 324 // the canonical names.
307 v8::ScriptCompiler::StreamedSource::Encoding encoding; 325 v8::ScriptCompiler::StreamedSource::Encoding encoding;
308 if (strcmp(encodingName, "windows-1252") == 0 326 if (strcmp(encodingName, "windows-1252") == 0
309 || strcmp(encodingName, "ISO-8859-1") == 0 327 || strcmp(encodingName, "ISO-8859-1") == 0
310 || strcmp(encodingName, "US-ASCII") == 0) { 328 || strcmp(encodingName, "US-ASCII") == 0) {
311 encoding = v8::ScriptCompiler::StreamedSource::ONE_BYTE; 329 encoding = v8::ScriptCompiler::StreamedSource::ONE_BYTE;
312 } else if (strcmp(encodingName, "UTF-8") == 0) { 330 } else if (strcmp(encodingName, "UTF-8") == 0) {
313 encoding = v8::ScriptCompiler::StreamedSource::UTF8; 331 encoding = v8::ScriptCompiler::StreamedSource::UTF8;
314 } else { 332 } else {
315 // We don't stream other encodings; especially we don't stream two 333 // We don't stream other encodings; especially we don't stream two
316 // byte scripts to avoid the handling of byte order marks. Most 334 // byte scripts to avoid the handling of endianness. Most scripts
317 // scripts are Latin1 or UTF-8 anyway, so this should be enough for 335 // are Latin1 or UTF-8 anyway, so this should be enough for most
318 // most real world purposes. 336 // real world purposes.
319 suppressStreaming(); 337 suppressStreaming();
320 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2 ); 338 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2 );
321 return; 339 return;
322 } 340 }
323 if (ScriptStreamerThread::shared()->isRunningTask()) { 341 if (ScriptStreamerThread::shared()->isRunningTask()) {
324 // At the moment we only have one thread for running the tasks. A 342 // 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, 343 // new task shouldn't be queued before the running task completes,
326 // because the running task can block and wait for data from the 344 // because the running task can block and wait for data from the
327 // network. 345 // network.
328 suppressStreaming(); 346 suppressStreaming();
(...skipping 27 matching lines...) Expand all
356 // ScriptStreamer needs to stay alive as long as the background task is 374 // 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; 375 // running. This is taken care of with a manual ref() & deref() pair;
358 // the corresponding deref() is in streamingComplete or in 376 // the corresponding deref() is in streamingComplete or in
359 // notifyFinished. 377 // notifyFinished.
360 ref(); 378 ref();
361 ScriptStreamingTask* task = new ScriptStreamingTask(scriptStreamingTask. release(), this); 379 ScriptStreamingTask* task = new ScriptStreamingTask(scriptStreamingTask. release(), this);
362 ScriptStreamerThread::shared()->postTask(task); 380 ScriptStreamerThread::shared()->postTask(task);
363 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); 381 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2);
364 } 382 }
365 if (m_stream) 383 if (m_stream)
366 m_stream->didReceiveData(); 384 m_stream->didReceiveData(lengthOfBOM);
367 } 385 }
368 386
369 void ScriptStreamer::notifyFinished(Resource* resource) 387 void ScriptStreamer::notifyFinished(Resource* resource)
370 { 388 {
371 ASSERT(isMainThread()); 389 ASSERT(isMainThread());
372 ASSERT(m_resource == resource); 390 ASSERT(m_resource == resource);
373 // A special case: empty and small scripts. We didn't receive enough data to 391 // 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 392 // 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 393 // be a "parsing complete" notification either, and we should not wait for
376 // it. 394 // it.
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
532 550
533 // The Resource might go out of scope if the script is no longer 551 // The Resource might go out of scope if the script is no longer
534 // needed. This makes PendingScript notify the ScriptStreamer when it is 552 // needed. This makes PendingScript notify the ScriptStreamer when it is
535 // destroyed. 553 // destroyed.
536 script.setStreamer(adoptRef(new ScriptStreamer(resource, scriptType, setting s->v8ScriptStreamingMode(), scriptState, compileOption))); 554 script.setStreamer(adoptRef(new ScriptStreamer(resource, scriptType, setting s->v8ScriptStreamingMode(), scriptState, compileOption)));
537 555
538 return true; 556 return true;
539 } 557 }
540 558
541 } // namespace blink 559 } // namespace blink
OLDNEW
« no previous file with comments | « no previous file | Source/bindings/core/v8/ScriptStreamerTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698