| OLD | NEW |
| (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 | |
| OLD | NEW |