Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "bindings/core/v8/SerializedScriptValue.h" | 31 #include "bindings/core/v8/SerializedScriptValue.h" |
| 32 | 32 |
| 33 #include <memory> | 33 #include <memory> |
| 34 #include "bindings/core/v8/DOMDataStore.h" | 34 #include "bindings/core/v8/DOMDataStore.h" |
| 35 #include "bindings/core/v8/DOMWrapperWorld.h" | 35 #include "bindings/core/v8/DOMWrapperWorld.h" |
| 36 #include "bindings/core/v8/ExceptionState.h" | 36 #include "bindings/core/v8/ExceptionState.h" |
| 37 #include "bindings/core/v8/ScriptState.h" | 37 #include "bindings/core/v8/ScriptState.h" |
| 38 #include "bindings/core/v8/SerializationTag.h" | |
| 38 #include "bindings/core/v8/SerializedScriptValueFactory.h" | 39 #include "bindings/core/v8/SerializedScriptValueFactory.h" |
| 39 #include "bindings/core/v8/Transferables.h" | 40 #include "bindings/core/v8/Transferables.h" |
| 40 #include "bindings/core/v8/V8ArrayBuffer.h" | 41 #include "bindings/core/v8/V8ArrayBuffer.h" |
| 41 #include "bindings/core/v8/V8ImageBitmap.h" | 42 #include "bindings/core/v8/V8ImageBitmap.h" |
| 42 #include "bindings/core/v8/V8MessagePort.h" | 43 #include "bindings/core/v8/V8MessagePort.h" |
| 43 #include "bindings/core/v8/V8OffscreenCanvas.h" | 44 #include "bindings/core/v8/V8OffscreenCanvas.h" |
| 44 #include "bindings/core/v8/V8SharedArrayBuffer.h" | 45 #include "bindings/core/v8/V8SharedArrayBuffer.h" |
| 45 #include "core/dom/DOMArrayBuffer.h" | 46 #include "core/dom/DOMArrayBuffer.h" |
| 46 #include "core/dom/DOMSharedArrayBuffer.h" | 47 #include "core/dom/DOMSharedArrayBuffer.h" |
| 47 #include "core/dom/ExceptionCode.h" | 48 #include "core/dom/ExceptionCode.h" |
| 48 #include "core/dom/MessagePort.h" | 49 #include "core/dom/MessagePort.h" |
| 49 #include "core/frame/ImageBitmap.h" | 50 #include "core/frame/ImageBitmap.h" |
| 50 #include "platform/SharedBuffer.h" | 51 #include "platform/SharedBuffer.h" |
| 51 #include "platform/blob/BlobData.h" | 52 #include "platform/blob/BlobData.h" |
| 52 #include "platform/heap/Handle.h" | 53 #include "platform/heap/Handle.h" |
| 53 #include "platform/wtf/Assertions.h" | 54 #include "platform/wtf/Assertions.h" |
| 54 #include "platform/wtf/ByteOrder.h" | 55 #include "platform/wtf/ByteOrder.h" |
| 55 #include "platform/wtf/PtrUtil.h" | 56 #include "platform/wtf/PtrUtil.h" |
| 56 #include "platform/wtf/Vector.h" | 57 #include "platform/wtf/Vector.h" |
| 58 #include "platform/wtf/dtoa/utils.h" | |
| 57 #include "platform/wtf/text/StringBuffer.h" | 59 #include "platform/wtf/text/StringBuffer.h" |
| 58 #include "platform/wtf/text/StringHash.h" | 60 #include "platform/wtf/text/StringHash.h" |
| 59 | 61 |
| 60 namespace blink { | 62 namespace blink { |
| 61 | 63 |
| 62 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Serialize( | 64 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Serialize( |
| 63 v8::Isolate* isolate, | 65 v8::Isolate* isolate, |
| 64 v8::Local<v8::Value> value, | 66 v8::Local<v8::Value> value, |
| 65 const SerializeOptions& options, | 67 const SerializeOptions& options, |
| 66 ExceptionState& exception) { | 68 ExceptionState& exception) { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 82 | 84 |
| 83 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create() { | 85 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create() { |
| 84 return AdoptRef(new SerializedScriptValue); | 86 return AdoptRef(new SerializedScriptValue); |
| 85 } | 87 } |
| 86 | 88 |
| 87 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create( | 89 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create( |
| 88 const String& data) { | 90 const String& data) { |
| 89 return AdoptRef(new SerializedScriptValue(data)); | 91 return AdoptRef(new SerializedScriptValue(data)); |
| 90 } | 92 } |
| 91 | 93 |
| 94 // Versions 16 and below (prior to April 2017) used ntohs() to byte-swap SSV | |
| 95 // data when converting it to the wire format. This was a historical accient. | |
| 96 // | |
| 97 // As IndexedDB stores SSVs to disk indefinitely, we still need to keep around | |
| 98 // the code needed to deserialize the old format. | |
| 99 inline static bool IsByteSwappedWiredData(const char* data, size_t length) { | |
| 100 // TODO(pwnall): Return false early if we're on big-endian hardware. Chromium | |
| 101 // doesn't currently support big-endian hardware, and there's no header | |
| 102 // exposing endianness to Blink yet. ARCH_CPU_LITTLE_ENDIAN seems promising, | |
| 103 // but Blink is not currently allowed to include files from build/. | |
| 104 | |
| 105 // The first SSV version without byte-swapping has two envelopes (Blink, V8), | |
| 106 // each of which is at least 2 bytes long. | |
| 107 if (length < 4) | |
| 108 return true; | |
| 109 | |
| 110 // This code handles the following cases: | |
| 111 // | |
| 112 // v0 (byte-swapped) - [d, t, ...], t = tag byte, d = first data byte | |
| 113 // v1-16 (byte-swapped) - [v, 0xFF, ...], v = version (1 <= v <= 16) | |
| 114 // v17+ - [0xFF, v, ...], v = first byte of version varint | |
| 115 | |
| 116 if (static_cast<uint8_t>(data[0]) == kVersionTag) { | |
| 117 // The only case where byte-swapped data can have 0xFF in byte zero is | |
| 118 // version 0. This can only happen if byte one is a tag (supported in | |
| 119 // version 0) that takes in extra data, and the first byte of extra data is | |
| 120 // 0xFF. There are 13 such tags, listed below. These tags cannot be used as | |
| 121 // version numbers in the Blink-side SSV envelope. | |
| 122 // | |
| 123 // 35 - 0x23 - # - ImageDataTag | |
| 124 // 64 - 0x40 - @ - SparseArrayTag | |
| 125 // 68 - 0x44 - D - DateTag | |
| 126 // 73 - 0x49 - I - Int32Tag | |
| 127 // 78 - 0x4E - N - NumberTag | |
| 128 // 82 - 0x52 - R - RegExpTag | |
| 129 // 83 - 0x53 - S - StringTag | |
| 130 // 85 - 0x55 - U - Uint32Tag | |
| 131 // 91 - 0x5B - [ - ArrayTag | |
| 132 // 98 - 0x62 - b - BlobTag | |
| 133 // 102 - 0x66 - f - FileTag | |
| 134 // 108 - 0x6C - l - FileListTag | |
| 135 // 123 - 0x7B - { - ObjectTag | |
| 136 // | |
| 137 // Why we care about version 0: | |
| 138 // | |
| 139 // IndexedDB stores values using the SSV format. Currently, IndexedDB does | |
| 140 // not do any sort of migration, so a value written with a SSV version will | |
| 141 // be stored with that version until it is removed via an update or delete. | |
| 142 // | |
| 143 // IndexedDB was shipped in Chrome 11, which was released on April 27, 2011. | |
| 144 // SSV version 1 was added in WebKit r91698, which was shipped in Chrome 14, | |
| 145 // which was released on September 16, 2011. | |
| 146 static_assert( | |
| 147 SerializedScriptValue::kWireFormatVersion != 35 && | |
| 148 SerializedScriptValue::kWireFormatVersion != 64 && | |
| 149 SerializedScriptValue::kWireFormatVersion != 68 && | |
| 150 SerializedScriptValue::kWireFormatVersion != 73 && | |
| 151 SerializedScriptValue::kWireFormatVersion != 78 && | |
| 152 SerializedScriptValue::kWireFormatVersion != 82 && | |
| 153 SerializedScriptValue::kWireFormatVersion != 83 && | |
| 154 SerializedScriptValue::kWireFormatVersion != 85 && | |
| 155 SerializedScriptValue::kWireFormatVersion != 91 && | |
| 156 SerializedScriptValue::kWireFormatVersion != 98 && | |
| 157 SerializedScriptValue::kWireFormatVersion != 102 && | |
| 158 SerializedScriptValue::kWireFormatVersion != 108 && | |
| 159 SerializedScriptValue::kWireFormatVersion != 123, | |
| 160 "Using a burned version will prevent us from reading SSV version 0"); | |
| 161 | |
| 162 // Fast path until the Blink-side SSV envelope reaches version 35. | |
| 163 if (SerializedScriptValue::kWireFormatVersion < 35) { | |
| 164 if (static_cast<uint8_t>(data[1]) < 35) | |
| 165 return false; | |
| 166 | |
| 167 // TODO(pwnall): Add UMA metric here. | |
| 168 return true; | |
| 169 } | |
| 170 | |
| 171 // Slower path that would kick in after version 35, assuming we don't remove | |
| 172 // support for SSV version 0 by then. | |
| 173 static constexpr uint8_t version0Tags[] = {35, 64, 68, 73, 78, 82, 83, | |
| 174 85, 91, 98, 102, 108, 123}; | |
| 175 return std::find(std::begin(version0Tags), std::end(version0Tags), | |
| 176 data[1]) != std::end(version0Tags); | |
| 177 } | |
| 178 | |
| 179 if (static_cast<uint8_t>(data[1]) == kVersionTag) { | |
| 180 // The last SSV format that used byte-flipping was version 16. The version | |
| 181 // number is stored (before byte-flipping) after a serialization tag, which | |
| 182 // is 0xFF. | |
| 183 return static_cast<uint8_t>(data[0]) != kVersionTag; | |
| 184 } | |
| 185 | |
| 186 // If kVersionTag isn't in any of the first two bytes, this is SSV version 0, | |
| 187 // which was byte-flipped. | |
|
jbroman
2017/04/12 18:34:04
change this comment and others to say "swapped" ra
pwnall
2017/04/12 21:37:30
Done.
Thanks for catching this! I did a find-repla
| |
| 188 return true; | |
| 189 } | |
| 190 | |
| 92 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create( | 191 PassRefPtr<SerializedScriptValue> SerializedScriptValue::Create( |
| 93 const char* data, | 192 const char* data, |
| 94 size_t length) { | 193 size_t length) { |
| 95 if (!data) | 194 if (!data) |
| 96 return Create(); | 195 return Create(); |
| 97 | 196 |
| 98 // Decode wire data from big endian to host byte order. | |
| 99 DCHECK(!(length % sizeof(UChar))); | 197 DCHECK(!(length % sizeof(UChar))); |
| 198 const UChar* src = reinterpret_cast<const UChar*>(data); | |
| 100 size_t string_length = length / sizeof(UChar); | 199 size_t string_length = length / sizeof(UChar); |
| 101 StringBuffer<UChar> buffer(string_length); | |
| 102 const UChar* src = reinterpret_cast<const UChar*>(data); | |
| 103 UChar* dst = buffer.Characters(); | |
| 104 for (size_t i = 0; i < string_length; i++) | |
| 105 dst[i] = ntohs(src[i]); | |
| 106 | 200 |
| 107 return AdoptRef(new SerializedScriptValue(String::Adopt(buffer))); | 201 if (IsByteSwappedWiredData(data, length)) { |
| 202 // Decode wire data from big endian to host byte order. | |
| 203 StringBuffer<UChar> buffer(string_length); | |
| 204 UChar* dst = buffer.Characters(); | |
| 205 for (size_t i = 0; i < string_length; ++i) | |
| 206 dst[i] = ntohs(src[i]); | |
| 207 | |
| 208 return AdoptRef(new SerializedScriptValue(String::Adopt(buffer))); | |
| 209 } | |
| 210 | |
| 211 return AdoptRef(new SerializedScriptValue(String(src, string_length))); | |
| 108 } | 212 } |
| 109 | 213 |
| 110 SerializedScriptValue::SerializedScriptValue() | 214 SerializedScriptValue::SerializedScriptValue() |
| 111 : has_registered_external_allocation_(false), | 215 : has_registered_external_allocation_(false), |
| 112 transferables_need_external_allocation_registration_(false) {} | 216 transferables_need_external_allocation_registration_(false) {} |
| 113 | 217 |
| 114 SerializedScriptValue::SerializedScriptValue(const String& wire_data) | 218 SerializedScriptValue::SerializedScriptValue(const String& wire_data) |
| 115 : has_registered_external_allocation_(false), | 219 : has_registered_external_allocation_(false), |
| 116 transferables_need_external_allocation_registration_(false) { | 220 transferables_need_external_allocation_registration_(false) { |
| 117 size_t byte_length = wire_data.length() * 2; | 221 size_t byte_length = wire_data.length() * 2; |
| 118 data_buffer_.reset(static_cast<uint8_t*>(WTF::Partitions::BufferMalloc( | 222 data_buffer_.reset(static_cast<uint8_t*>(WTF::Partitions::BufferMalloc( |
| 119 byte_length, "SerializedScriptValue buffer"))); | 223 byte_length, "SerializedScriptValue buffer"))); |
| 120 data_buffer_size_ = byte_length; | 224 data_buffer_size_ = byte_length; |
| 121 wire_data.CopyTo(reinterpret_cast<UChar*>(data_buffer_.get()), 0, | 225 wire_data.CopyTo(reinterpret_cast<UChar*>(data_buffer_.get()), 0, |
| 122 wire_data.length()); | 226 wire_data.length()); |
| 123 } | 227 } |
| 124 | 228 |
| 125 SerializedScriptValue::~SerializedScriptValue() { | 229 SerializedScriptValue::~SerializedScriptValue() { |
| 126 // If the allocated memory was not registered before, then this class is | 230 // If the allocated memory was not registered before, then this class is |
| 127 // likely used in a context other than Worker's onmessage environment and the | 231 // likely used in a context other than Worker's onmessage environment and the |
| 128 // presence of current v8 context is not guaranteed. Avoid calling v8 then. | 232 // presence of current v8 context is not guaranteed. Avoid calling v8 then. |
| 129 if (has_registered_external_allocation_) { | 233 if (has_registered_external_allocation_) { |
| 130 ASSERT(v8::Isolate::GetCurrent()); | 234 ASSERT(v8::Isolate::GetCurrent()); |
| 131 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory( | 235 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory( |
| 132 -static_cast<int64_t>(DataLengthInBytes())); | 236 -static_cast<int64_t>(DataLengthInBytes())); |
| 133 } | 237 } |
| 134 } | 238 } |
| 135 | 239 |
| 136 PassRefPtr<SerializedScriptValue> SerializedScriptValue::NullValue() { | 240 PassRefPtr<SerializedScriptValue> SerializedScriptValue::NullValue() { |
| 137 // UChar rather than uint8_t here to get host endian behavior. | 241 static const uint8_t kNullData[] = { |
| 138 static const UChar kNullData[] = {0xff09, 0x3000}; | 242 kVersionTag, kWireFormatVersion, // Blink envelope |
|
jbroman
2017/04/12 18:34:04
I don't strenuously object, but this level of deta
pwnall
2017/04/12 21:37:30
Done.
I'll explain my reasoning for the previous
| |
| 243 // 13 is v8::ValueSerializer::GetCurrentDataFormatVersion(). Having a | |
| 244 // hard-coded version here is better than getting V8's version directly. | |
| 245 // This approach lets v8 make drastic changes to its serialization format | |
| 246 // without a 3-sided patch, as long as it retains the ability to | |
| 247 // deserialize the version number listed here. | |
| 248 kVersionTag, 13, // V8 envelope | |
| 249 '0', // kNull in v8::internal::SerializationTag | |
| 250 0x00}; // Padding to an integral number of UChars | |
| 139 return Create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData)); | 251 return Create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData)); |
| 140 } | 252 } |
| 141 | 253 |
| 142 String SerializedScriptValue::ToWireString() const { | 254 String SerializedScriptValue::ToWireString() const { |
| 143 // Add the padding '\0', but don't put it in |m_dataBuffer|. | 255 // Add the padding '\0', but don't put it in |m_dataBuffer|. |
| 144 // This requires direct use of uninitialized strings, though. | 256 // This requires direct use of uninitialized strings, though. |
| 145 UChar* destination; | 257 UChar* destination; |
| 146 size_t string_size_bytes = (data_buffer_size_ + 1) & ~1; | 258 size_t string_size_bytes = (data_buffer_size_ + 1) & ~1; |
| 147 String wire_string = | 259 String wire_string = |
| 148 String::CreateUninitialized(string_size_bytes / 2, destination); | 260 String::CreateUninitialized(string_size_bytes / 2, destination); |
| 149 memcpy(destination, data_buffer_.get(), data_buffer_size_); | 261 memcpy(destination, data_buffer_.get(), data_buffer_size_); |
| 150 if (string_size_bytes > data_buffer_size_) | 262 if (string_size_bytes > data_buffer_size_) |
| 151 reinterpret_cast<char*>(destination)[string_size_bytes - 1] = '\0'; | 263 reinterpret_cast<char*>(destination)[string_size_bytes - 1] = '\0'; |
| 152 return wire_string; | 264 return wire_string; |
| 153 } | 265 } |
| 154 | 266 |
| 155 // Convert serialized string to big endian wire data. | |
| 156 void SerializedScriptValue::ToWireBytes(Vector<char>& result) const { | 267 void SerializedScriptValue::ToWireBytes(Vector<char>& result) const { |
| 157 DCHECK(result.IsEmpty()); | 268 DCHECK(result.IsEmpty()); |
| 158 | 269 |
| 159 size_t wire_size_bytes = (data_buffer_size_ + 1) & ~1; | 270 size_t result_size = (data_buffer_size_ + 1) & ~1; |
| 160 result.Resize(wire_size_bytes); | 271 result.Resize(result_size); |
| 272 memcpy(result.Data(), data_buffer_.get(), data_buffer_size_); | |
| 161 | 273 |
| 162 const UChar* src = reinterpret_cast<UChar*>(data_buffer_.get()); | 274 if (result_size > data_buffer_size_) { |
| 163 UChar* dst = reinterpret_cast<UChar*>(result.Data()); | 275 DCHECK_EQ(result_size, data_buffer_size_ + 1); |
| 164 for (size_t i = 0; i < data_buffer_size_ / 2; i++) | 276 result[data_buffer_size_] = 0; |
| 165 dst[i] = htons(src[i]); | 277 } |
| 166 | |
| 167 // This is equivalent to swapping the byte order of the two bytes (x, 0), | |
| 168 // depending on endianness. | |
| 169 if (data_buffer_size_ % 2) | |
| 170 dst[wire_size_bytes / 2 - 1] = data_buffer_[data_buffer_size_ - 1] << 8; | |
| 171 } | 278 } |
| 172 | 279 |
| 173 static void AccumulateArrayBuffersForAllWorlds( | 280 static void AccumulateArrayBuffersForAllWorlds( |
| 174 v8::Isolate* isolate, | 281 v8::Isolate* isolate, |
| 175 DOMArrayBuffer* object, | 282 DOMArrayBuffer* object, |
| 176 Vector<v8::Local<v8::ArrayBuffer>, 4>& buffers) { | 283 Vector<v8::Local<v8::ArrayBuffer>, 4>& buffers) { |
| 177 Vector<RefPtr<DOMWrapperWorld>> worlds; | 284 Vector<RefPtr<DOMWrapperWorld>> worlds; |
| 178 DOMWrapperWorld::AllWorldsInCurrentThread(worlds); | 285 DOMWrapperWorld::AllWorldsInCurrentThread(worlds); |
| 179 for (const auto& world : worlds) { | 286 for (const auto& world : worlds) { |
| 180 v8::Local<v8::Object> wrapper = world->DomDataStore().Get(object, isolate); | 287 v8::Local<v8::Object> wrapper = world->DomDataStore().Get(object, isolate); |
| (...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 462 // Only (re)register allocation cost for transferables if this | 569 // Only (re)register allocation cost for transferables if this |
| 463 // SerializedScriptValue has explicitly unregistered them before. | 570 // SerializedScriptValue has explicitly unregistered them before. |
| 464 if (array_buffer_contents_array_ && | 571 if (array_buffer_contents_array_ && |
| 465 transferables_need_external_allocation_registration_) { | 572 transferables_need_external_allocation_registration_) { |
| 466 for (auto& buffer : *array_buffer_contents_array_) | 573 for (auto& buffer : *array_buffer_contents_array_) |
| 467 buffer.RegisterExternalAllocationWithCurrentContext(); | 574 buffer.RegisterExternalAllocationWithCurrentContext(); |
| 468 } | 575 } |
| 469 } | 576 } |
| 470 | 577 |
| 471 } // namespace blink | 578 } // namespace blink |
| OLD | NEW |