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

Side by Side Diff: third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp

Issue 2857143005: Gather serialization-related code into a directory. (Closed)
Patch Set: Created 3 years, 7 months 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
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "bindings/core/v8/SerializedScriptValue.h"
32
33 #include <memory>
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "bindings/core/v8/IDLTypes.h"
36 #include "bindings/core/v8/NativeValueTraitsImpl.h"
37 #include "bindings/core/v8/SerializationTag.h"
38 #include "bindings/core/v8/SerializedScriptValueFactory.h"
39 #include "bindings/core/v8/Transferables.h"
40 #include "bindings/core/v8/V8ArrayBuffer.h"
41 #include "bindings/core/v8/V8ImageBitmap.h"
42 #include "bindings/core/v8/V8MessagePort.h"
43 #include "bindings/core/v8/V8OffscreenCanvas.h"
44 #include "bindings/core/v8/V8SharedArrayBuffer.h"
45 #include "core/dom/DOMArrayBuffer.h"
46 #include "core/dom/DOMSharedArrayBuffer.h"
47 #include "core/dom/ExceptionCode.h"
48 #include "core/dom/MessagePort.h"
49 #include "core/frame/ImageBitmap.h"
50 #include "platform/SharedBuffer.h"
51 #include "platform/bindings/DOMDataStore.h"
52 #include "platform/bindings/DOMWrapperWorld.h"
53 #include "platform/bindings/ScriptState.h"
54 #include "platform/blob/BlobData.h"
55 #include "platform/heap/Handle.h"
56 #include "platform/wtf/Assertions.h"
57 #include "platform/wtf/ByteOrder.h"
58 #include "platform/wtf/PtrUtil.h"
59 #include "platform/wtf/Vector.h"
60 #include "platform/wtf/dtoa/utils.h"
61 #include "platform/wtf/text/StringBuffer.h"
62 #include "platform/wtf/text/StringHash.h"
63
64 namespace blink {
65
66 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Serialize(
67 v8::Isolate* isolate,
68 v8::Local<v8::Value> value,
69 const SerializeOptions& options,
70 ExceptionState& exception) {
71 return SerializedScriptValueFactory::Instance().Create(isolate, value,
72 options, exception);
73 }
74
75 PassRefPtr<SerializedScriptValue>
76 SerializedScriptValue::SerializeAndSwallowExceptions(
77 v8::Isolate* isolate,
78 v8::Local<v8::Value> value) {
79 DummyExceptionStateForTesting exception_state;
80 RefPtr<SerializedScriptValue> serialized =
81 Serialize(isolate, value, SerializeOptions(), exception_state);
82 if (exception_state.HadException())
83 return NullValue();
84 return serialized.Release();
85 }
86
87 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create() {
88 return AdoptRef(new SerializedScriptValue);
89 }
90
91 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create(
92 const String& data) {
93 return AdoptRef(new SerializedScriptValue(data));
94 }
95
96 // Versions 16 and below (prior to April 2017) used ntohs() to byte-swap SSV
97 // data when converting it to the wire format. This was a historical accient.
98 //
99 // As IndexedDB stores SSVs to disk indefinitely, we still need to keep around
100 // the code needed to deserialize the old format.
101 inline static bool IsByteSwappedWiredData(const char* data, size_t length) {
102 // TODO(pwnall): Return false early if we're on big-endian hardware. Chromium
103 // doesn't currently support big-endian hardware, and there's no header
104 // exposing endianness to Blink yet. ARCH_CPU_LITTLE_ENDIAN seems promising,
105 // but Blink is not currently allowed to include files from build/.
106
107 // The first SSV version without byte-swapping has two envelopes (Blink, V8),
108 // each of which is at least 2 bytes long.
109 if (length < 4)
110 return true;
111
112 // This code handles the following cases:
113 //
114 // v0 (byte-swapped) - [d, t, ...], t = tag byte, d = first data byte
115 // v1-16 (byte-swapped) - [v, 0xFF, ...], v = version (1 <= v <= 16)
116 // v17+ - [0xFF, v, ...], v = first byte of version varint
117
118 if (static_cast<uint8_t>(data[0]) == kVersionTag) {
119 // The only case where byte-swapped data can have 0xFF in byte zero is
120 // version 0. This can only happen if byte one is a tag (supported in
121 // version 0) that takes in extra data, and the first byte of extra data is
122 // 0xFF. There are 13 such tags, listed below. These tags cannot be used as
123 // version numbers in the Blink-side SSV envelope.
124 //
125 // 35 - 0x23 - # - ImageDataTag
126 // 64 - 0x40 - @ - SparseArrayTag
127 // 68 - 0x44 - D - DateTag
128 // 73 - 0x49 - I - Int32Tag
129 // 78 - 0x4E - N - NumberTag
130 // 82 - 0x52 - R - RegExpTag
131 // 83 - 0x53 - S - StringTag
132 // 85 - 0x55 - U - Uint32Tag
133 // 91 - 0x5B - [ - ArrayTag
134 // 98 - 0x62 - b - BlobTag
135 // 102 - 0x66 - f - FileTag
136 // 108 - 0x6C - l - FileListTag
137 // 123 - 0x7B - { - ObjectTag
138 //
139 // Why we care about version 0:
140 //
141 // IndexedDB stores values using the SSV format. Currently, IndexedDB does
142 // not do any sort of migration, so a value written with a SSV version will
143 // be stored with that version until it is removed via an update or delete.
144 //
145 // IndexedDB was shipped in Chrome 11, which was released on April 27, 2011.
146 // SSV version 1 was added in WebKit r91698, which was shipped in Chrome 14,
147 // which was released on September 16, 2011.
148 static_assert(
149 SerializedScriptValue::kWireFormatVersion != 35 &&
150 SerializedScriptValue::kWireFormatVersion != 64 &&
151 SerializedScriptValue::kWireFormatVersion != 68 &&
152 SerializedScriptValue::kWireFormatVersion != 73 &&
153 SerializedScriptValue::kWireFormatVersion != 78 &&
154 SerializedScriptValue::kWireFormatVersion != 82 &&
155 SerializedScriptValue::kWireFormatVersion != 83 &&
156 SerializedScriptValue::kWireFormatVersion != 85 &&
157 SerializedScriptValue::kWireFormatVersion != 91 &&
158 SerializedScriptValue::kWireFormatVersion != 98 &&
159 SerializedScriptValue::kWireFormatVersion != 102 &&
160 SerializedScriptValue::kWireFormatVersion != 108 &&
161 SerializedScriptValue::kWireFormatVersion != 123,
162 "Using a burned version will prevent us from reading SSV version 0");
163
164 // Fast path until the Blink-side SSV envelope reaches version 35.
165 if (SerializedScriptValue::kWireFormatVersion < 35) {
166 if (static_cast<uint8_t>(data[1]) < 35)
167 return false;
168
169 // TODO(pwnall): Add UMA metric here.
170 return true;
171 }
172
173 // Slower path that would kick in after version 35, assuming we don't remove
174 // support for SSV version 0 by then.
175 static constexpr uint8_t version0Tags[] = {35, 64, 68, 73, 78, 82, 83,
176 85, 91, 98, 102, 108, 123};
177 return std::find(std::begin(version0Tags), std::end(version0Tags),
178 data[1]) != std::end(version0Tags);
179 }
180
181 if (static_cast<uint8_t>(data[1]) == kVersionTag) {
182 // The last SSV format that used byte-swapping was version 16. The version
183 // number is stored (before byte-swapping) after a serialization tag, which
184 // is 0xFF.
185 return static_cast<uint8_t>(data[0]) != kVersionTag;
186 }
187
188 // If kVersionTag isn't in any of the first two bytes, this is SSV version 0,
189 // which was byte-swapped.
190 return true;
191 }
192
193 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create(
194 const char* data,
195 size_t length) {
196 if (!data)
197 return Create();
198
199 DCHECK(!(length % sizeof(UChar)));
200 const UChar* src = reinterpret_cast<const UChar*>(data);
201 size_t string_length = length / sizeof(UChar);
202
203 if (IsByteSwappedWiredData(data, length)) {
204 // Decode wire data from big endian to host byte order.
205 StringBuffer<UChar> buffer(string_length);
206 UChar* dst = buffer.Characters();
207 for (size_t i = 0; i < string_length; ++i)
208 dst[i] = ntohs(src[i]);
209
210 return AdoptRef(new SerializedScriptValue(String::Adopt(buffer)));
211 }
212
213 return AdoptRef(new SerializedScriptValue(String(src, string_length)));
214 }
215
216 SerializedScriptValue::SerializedScriptValue()
217 : has_registered_external_allocation_(false),
218 transferables_need_external_allocation_registration_(false) {}
219
220 SerializedScriptValue::SerializedScriptValue(const String& wire_data)
221 : has_registered_external_allocation_(false),
222 transferables_need_external_allocation_registration_(false) {
223 size_t byte_length = wire_data.length() * 2;
224 data_buffer_.reset(static_cast<uint8_t*>(WTF::Partitions::BufferMalloc(
225 byte_length, "SerializedScriptValue buffer")));
226 data_buffer_size_ = byte_length;
227 wire_data.CopyTo(reinterpret_cast<UChar*>(data_buffer_.get()), 0,
228 wire_data.length());
229 }
230
231 SerializedScriptValue::~SerializedScriptValue() {
232 // If the allocated memory was not registered before, then this class is
233 // likely used in a context other than Worker's onmessage environment and the
234 // presence of current v8 context is not guaranteed. Avoid calling v8 then.
235 if (has_registered_external_allocation_) {
236 DCHECK(v8::Isolate::GetCurrent());
237 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
238 -static_cast<int64_t>(DataLengthInBytes()));
239 }
240 }
241
242 PassRefPtr<SerializedScriptValue> SerializedScriptValue::NullValue() {
243 // The format here may fall a bit out of date, because we support
244 // deserializing SSVs written by old browser versions.
245 static const uint8_t kNullData[] = {0xFF, 17, 0xFF, 13, '0', 0x00};
246 return Create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData));
247 }
248
249 String SerializedScriptValue::ToWireString() const {
250 // Add the padding '\0', but don't put it in |m_dataBuffer|.
251 // This requires direct use of uninitialized strings, though.
252 UChar* destination;
253 size_t string_size_bytes = (data_buffer_size_ + 1) & ~1;
254 String wire_string =
255 String::CreateUninitialized(string_size_bytes / 2, destination);
256 memcpy(destination, data_buffer_.get(), data_buffer_size_);
257 if (string_size_bytes > data_buffer_size_)
258 reinterpret_cast<char*>(destination)[string_size_bytes - 1] = '\0';
259 return wire_string;
260 }
261
262 void SerializedScriptValue::ToWireBytes(Vector<char>& result) const {
263 DCHECK(result.IsEmpty());
264
265 size_t result_size = (data_buffer_size_ + 1) & ~1;
266 result.resize(result_size);
267 memcpy(result.data(), data_buffer_.get(), data_buffer_size_);
268
269 if (result_size > data_buffer_size_) {
270 DCHECK_EQ(result_size, data_buffer_size_ + 1);
271 result[data_buffer_size_] = 0;
272 }
273 }
274
275 std::unique_ptr<SerializedScriptValue::ImageBitmapContentsArray>
276 SerializedScriptValue::TransferImageBitmapContents(
277 v8::Isolate* isolate,
278 const ImageBitmapArray& image_bitmaps,
279 ExceptionState& exception_state) {
280 if (!image_bitmaps.size())
281 return nullptr;
282
283 for (size_t i = 0; i < image_bitmaps.size(); ++i) {
284 if (image_bitmaps[i]->IsNeutered()) {
285 exception_state.ThrowDOMException(
286 kDataCloneError, "ImageBitmap at index " + String::Number(i) +
287 " is already detached.");
288 return nullptr;
289 }
290 }
291
292 std::unique_ptr<ImageBitmapContentsArray> contents =
293 WTF::WrapUnique(new ImageBitmapContentsArray);
294 HeapHashSet<Member<ImageBitmap>> visited;
295 for (size_t i = 0; i < image_bitmaps.size(); ++i) {
296 if (visited.Contains(image_bitmaps[i]))
297 continue;
298 visited.insert(image_bitmaps[i]);
299 contents->push_back(image_bitmaps[i]->Transfer());
300 }
301 return contents;
302 }
303
304 void SerializedScriptValue::TransferImageBitmaps(
305 v8::Isolate* isolate,
306 const ImageBitmapArray& image_bitmaps,
307 ExceptionState& exception_state) {
308 std::unique_ptr<ImageBitmapContentsArray> contents =
309 TransferImageBitmapContents(isolate, image_bitmaps, exception_state);
310 image_bitmap_contents_array_ = std::move(contents);
311 }
312
313 void SerializedScriptValue::TransferOffscreenCanvas(
314 v8::Isolate* isolate,
315 const OffscreenCanvasArray& offscreen_canvases,
316 ExceptionState& exception_state) {
317 if (!offscreen_canvases.size())
318 return;
319
320 HeapHashSet<Member<OffscreenCanvas>> visited;
321 for (size_t i = 0; i < offscreen_canvases.size(); i++) {
322 if (visited.Contains(offscreen_canvases[i].Get()))
323 continue;
324 if (offscreen_canvases[i]->IsNeutered()) {
325 exception_state.ThrowDOMException(
326 kDataCloneError, "OffscreenCanvas at index " + String::Number(i) +
327 " is already detached.");
328 return;
329 }
330 if (offscreen_canvases[i]->RenderingContext()) {
331 exception_state.ThrowDOMException(
332 kDataCloneError, "OffscreenCanvas at index " + String::Number(i) +
333 " has an associated context.");
334 return;
335 }
336 visited.insert(offscreen_canvases[i].Get());
337 offscreen_canvases[i].Get()->SetNeutered();
338 }
339 }
340
341 void SerializedScriptValue::TransferArrayBuffers(
342 v8::Isolate* isolate,
343 const ArrayBufferArray& array_buffers,
344 ExceptionState& exception_state) {
345 array_buffer_contents_array_ =
346 TransferArrayBufferContents(isolate, array_buffers, exception_state);
347 }
348
349 v8::Local<v8::Value> SerializedScriptValue::Deserialize(
350 v8::Isolate* isolate,
351 const DeserializeOptions& options) {
352 return SerializedScriptValueFactory::Instance().Deserialize(this, isolate,
353 options);
354 }
355
356 bool SerializedScriptValue::ExtractTransferables(
357 v8::Isolate* isolate,
358 v8::Local<v8::Value> value,
359 int argument_index,
360 Transferables& transferables,
361 ExceptionState& exception_state) {
362 if (value.IsEmpty() || value->IsUndefined())
363 return true;
364
365 Vector<v8::Local<v8::Value>> transferable_array =
366 NativeValueTraits<IDLSequence<v8::Local<v8::Value>>>::NativeValue(
367 isolate, value, exception_state);
368 if (exception_state.HadException())
369 return false;
370
371 // Validate the passed array of transferables.
372 uint32_t i = 0;
373 for (const auto& transferable_object : transferable_array) {
374 // Validation of non-null objects, per HTML5 spec 10.3.3.
375 if (IsUndefinedOrNull(transferable_object)) {
376 exception_state.ThrowTypeError(
377 "Value at index " + String::Number(i) + " is an untransferable " +
378 (transferable_object->IsUndefined() ? "'undefined'" : "'null'") +
379 " value.");
380 return false;
381 }
382 // Validation of Objects implementing an interface, per WebIDL spec 4.1.15.
383 if (V8MessagePort::hasInstance(transferable_object, isolate)) {
384 MessagePort* port = V8MessagePort::toImpl(
385 v8::Local<v8::Object>::Cast(transferable_object));
386 // Check for duplicate MessagePorts.
387 if (transferables.message_ports.Contains(port)) {
388 exception_state.ThrowDOMException(
389 kDataCloneError, "Message port at index " + String::Number(i) +
390 " is a duplicate of an earlier port.");
391 return false;
392 }
393 transferables.message_ports.push_back(port);
394 } else if (transferable_object->IsArrayBuffer()) {
395 DOMArrayBuffer* array_buffer = V8ArrayBuffer::toImpl(
396 v8::Local<v8::Object>::Cast(transferable_object));
397 if (transferables.array_buffers.Contains(array_buffer)) {
398 exception_state.ThrowDOMException(
399 kDataCloneError, "ArrayBuffer at index " + String::Number(i) +
400 " is a duplicate of an earlier ArrayBuffer.");
401 return false;
402 }
403 transferables.array_buffers.push_back(array_buffer);
404 } else if (transferable_object->IsSharedArrayBuffer()) {
405 DOMSharedArrayBuffer* shared_array_buffer = V8SharedArrayBuffer::toImpl(
406 v8::Local<v8::Object>::Cast(transferable_object));
407 if (transferables.array_buffers.Contains(shared_array_buffer)) {
408 exception_state.ThrowDOMException(
409 kDataCloneError,
410 "SharedArrayBuffer at index " + String::Number(i) +
411 " is a duplicate of an earlier SharedArrayBuffer.");
412 return false;
413 }
414 transferables.array_buffers.push_back(shared_array_buffer);
415 } else if (V8ImageBitmap::hasInstance(transferable_object, isolate)) {
416 ImageBitmap* image_bitmap = V8ImageBitmap::toImpl(
417 v8::Local<v8::Object>::Cast(transferable_object));
418 if (transferables.image_bitmaps.Contains(image_bitmap)) {
419 exception_state.ThrowDOMException(
420 kDataCloneError, "ImageBitmap at index " + String::Number(i) +
421 " is a duplicate of an earlier ImageBitmap.");
422 return false;
423 }
424 transferables.image_bitmaps.push_back(image_bitmap);
425 } else if (V8OffscreenCanvas::hasInstance(transferable_object, isolate)) {
426 OffscreenCanvas* offscreen_canvas = V8OffscreenCanvas::toImpl(
427 v8::Local<v8::Object>::Cast(transferable_object));
428 if (transferables.offscreen_canvases.Contains(offscreen_canvas)) {
429 exception_state.ThrowDOMException(
430 kDataCloneError,
431 "OffscreenCanvas at index " + String::Number(i) +
432 " is a duplicate of an earlier OffscreenCanvas.");
433 return false;
434 }
435 transferables.offscreen_canvases.push_back(offscreen_canvas);
436 } else {
437 exception_state.ThrowTypeError("Value at index " + String::Number(i) +
438 " does not have a transferable type.");
439 return false;
440 }
441 i++;
442 }
443 return true;
444 }
445
446 std::unique_ptr<SerializedScriptValue::ArrayBufferContentsArray>
447 SerializedScriptValue::TransferArrayBufferContents(
448 v8::Isolate* isolate,
449 const ArrayBufferArray& array_buffers,
450 ExceptionState& exception_state) {
451 if (!array_buffers.size())
452 return nullptr;
453
454 for (auto it = array_buffers.begin(); it != array_buffers.end(); ++it) {
455 DOMArrayBufferBase* array_buffer = *it;
456 if (array_buffer->IsNeutered()) {
457 size_t index = std::distance(array_buffers.begin(), it);
458 exception_state.ThrowDOMException(
459 kDataCloneError, "ArrayBuffer at index " + String::Number(index) +
460 " is already neutered.");
461 return nullptr;
462 }
463 }
464
465 std::unique_ptr<ArrayBufferContentsArray> contents =
466 WTF::WrapUnique(new ArrayBufferContentsArray(array_buffers.size()));
467
468 HeapHashSet<Member<DOMArrayBufferBase>> visited;
469 for (auto it = array_buffers.begin(); it != array_buffers.end(); ++it) {
470 DOMArrayBufferBase* array_buffer_base = *it;
471 if (visited.Contains(array_buffer_base))
472 continue;
473 visited.insert(array_buffer_base);
474
475 size_t index = std::distance(array_buffers.begin(), it);
476 if (array_buffer_base->IsShared()) {
477 DOMSharedArrayBuffer* shared_array_buffer =
478 static_cast<DOMSharedArrayBuffer*>(array_buffer_base);
479 if (!shared_array_buffer->ShareContentsWith(contents->at(index))) {
480 exception_state.ThrowDOMException(kDataCloneError,
481 "SharedArrayBuffer at index " +
482 String::Number(index) +
483 " could not be transferred.");
484 return nullptr;
485 }
486 } else {
487 DOMArrayBuffer* array_buffer =
488 static_cast<DOMArrayBuffer*>(array_buffer_base);
489
490 if (!array_buffer->Transfer(isolate, contents->at(index))) {
491 exception_state.ThrowDOMException(
492 kDataCloneError, "ArrayBuffer at index " + String::Number(index) +
493 " could not be transferred.");
494 return nullptr;
495 }
496 }
497 }
498 return contents;
499 }
500
501 void SerializedScriptValue::
502 UnregisterMemoryAllocatedWithCurrentScriptContext() {
503 if (has_registered_external_allocation_) {
504 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
505 -static_cast<int64_t>(DataLengthInBytes()));
506 has_registered_external_allocation_ = false;
507 }
508
509 // TODO: if other transferables start accounting for their external
510 // allocations with V8, extend this with corresponding cases.
511 if (array_buffer_contents_array_ &&
512 !transferables_need_external_allocation_registration_) {
513 for (auto& buffer : *array_buffer_contents_array_)
514 buffer.UnregisterExternalAllocationWithCurrentContext();
515 transferables_need_external_allocation_registration_ = true;
516 }
517 }
518
519 void SerializedScriptValue::RegisterMemoryAllocatedWithCurrentScriptContext() {
520 if (has_registered_external_allocation_)
521 return;
522
523 has_registered_external_allocation_ = true;
524 int64_t diff = static_cast<int64_t>(DataLengthInBytes());
525 DCHECK_GE(diff, 0);
526 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(diff);
527
528 // Only (re)register allocation cost for transferables if this
529 // SerializedScriptValue has explicitly unregistered them before.
530 if (array_buffer_contents_array_ &&
531 transferables_need_external_allocation_registration_) {
532 for (auto& buffer : *array_buffer_contents_array_)
533 buffer.RegisterExternalAllocationWithCurrentContext();
534 }
535 }
536
537 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698