1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h"
6
7 #include "base/bit_cast.h"
8 #include "third_party/blink/renderer/platform/crypto.h"
9 #include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
10 #include "third_party/blink/renderer/platform/wtf/text/string_hasher.h"
11 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
12
13 namespace blink {
14
15 // Defined here for storage/ODR reasons, but initialized in the header.
16 const size_t SourceKeyedCachedMetadataHandler::kKeySize;
17
18 class SourceKeyedCachedMetadataHandler::SingleKeyHandler final
19 : public SingleCachedMetadataHandler {
20 public:
Trace(Visitor * visitor) const21 void Trace(Visitor* visitor) const override {
22 visitor->Trace(parent_);
23 SingleCachedMetadataHandler::Trace(visitor);
24 }
25
SingleKeyHandler(SourceKeyedCachedMetadataHandler * parent,Key key)26 SingleKeyHandler(SourceKeyedCachedMetadataHandler* parent, Key key)
27 : parent_(parent), key_(key) {}
28
SetCachedMetadata(uint32_t data_type_id,const uint8_t * data,size_t size)29 void SetCachedMetadata(uint32_t data_type_id,
30 const uint8_t* data,
31 size_t size) override {
32 DCHECK(!parent_->cached_metadata_map_.Contains(key_));
33 parent_->cached_metadata_map_.insert(
34 key_, CachedMetadata::Create(data_type_id, data, size));
35 if (!disable_send_to_platform_for_testing_)
36 parent_->SendToPlatform();
37 }
38
ClearCachedMetadata(ClearCacheType cache_type)39 void ClearCachedMetadata(ClearCacheType cache_type) override {
40 if (cache_type == kDiscardLocally)
41 return;
42 parent_->cached_metadata_map_.erase(key_);
43 if (cache_type == CachedMetadataHandler::kClearPersistentStorage)
44 parent_->SendToPlatform();
45 }
46
GetCachedMetadata(uint32_t data_type_id) const47 scoped_refptr<CachedMetadata> GetCachedMetadata(
48 uint32_t data_type_id) const override {
49 scoped_refptr<CachedMetadata> cached_metadata =
50 parent_->cached_metadata_map_.at(key_);
51 if (!cached_metadata || cached_metadata->DataTypeID() != data_type_id)
52 return nullptr;
53 return cached_metadata;
54 }
55
Encoding() const56 String Encoding() const override { return parent_->Encoding(); }
57
IsServedFromCacheStorage() const58 bool IsServedFromCacheStorage() const override {
59 return parent_->IsServedFromCacheStorage();
60 }
61
OnMemoryDump(WebProcessMemoryDump * pmd,const String & dump_prefix) const62 void OnMemoryDump(WebProcessMemoryDump* pmd,
63 const String& dump_prefix) const override {
64 // No memory to report here because it is attributed to |parent_|.
65 }
66
GetCodeCacheSize() const67 size_t GetCodeCacheSize() const override {
68 // No need to implement this because it is attributed to |parent_|.
69 return 0;
70 }
71
72 private:
73 Member<SourceKeyedCachedMetadataHandler> parent_;
74 Key key_;
75 };
76
77 class SourceKeyedCachedMetadataHandler::KeyHash {
78 STATIC_ONLY(KeyHash);
79
80 public:
GetHash(const Key & key)81 static unsigned GetHash(const Key& key) {
82 return StringHasher::ComputeHash(key.data(),
83 static_cast<uint32_t>(key.size()));
84 }
85
Equal(const Key & a,const Key & b)86 static bool Equal(const Key& a, const Key& b) { return a == b; }
87
88 static const bool safe_to_compare_to_empty_or_deleted = true;
89 };
90
HandlerForSource(const String & source)91 SingleCachedMetadataHandler* SourceKeyedCachedMetadataHandler::HandlerForSource(
92 const String& source) {
93 DigestValue digest_value;
94
95 if (!ComputeDigest(kHashAlgorithmSha256,
96 static_cast<const char*>(source.Bytes()),
97 source.CharactersSizeInBytes(), digest_value))
98 return nullptr;
99
100 Key key(kKeySize);
101 DCHECK_EQ(digest_value.size(), kKeySize);
102 memcpy(key.data(), digest_value.data(), kKeySize);
103
104 return MakeGarbageCollected<SingleKeyHandler>(this, key);
105 }
106
ClearCachedMetadata(CachedMetadataHandler::ClearCacheType cache_type)107 void SourceKeyedCachedMetadataHandler::ClearCachedMetadata(
108 CachedMetadataHandler::ClearCacheType cache_type) {
109 if (cache_type == kDiscardLocally)
110 return;
111 cached_metadata_map_.clear();
112 if (cache_type == CachedMetadataHandler::kClearPersistentStorage)
113 SendToPlatform();
114 }
115
Encoding() const116 String SourceKeyedCachedMetadataHandler::Encoding() const {
117 return String(encoding_.GetName());
118 }
119
OnMemoryDump(WebProcessMemoryDump * pmd,const String & dump_prefix) const120 void SourceKeyedCachedMetadataHandler::OnMemoryDump(
121 WebProcessMemoryDump* pmd,
122 const String& dump_prefix) const {
123 if (cached_metadata_map_.IsEmpty())
124 return;
125
126 const String dump_name = dump_prefix + "/inline";
127 uint64_t value = 0;
128 for (const auto& metadata : cached_metadata_map_.Values()) {
129 value += metadata->SerializedData().size();
130 }
131 auto* dump = pmd->CreateMemoryAllocatorDump(dump_name);
132 dump->AddScalar("size", "bytes", value);
133 pmd->AddSuballocation(dump->Guid(),
134 String(WTF::Partitions::kAllocatedObjectPoolName));
135 }
136
137 // Encoding of keyed map:
138 // - marker: CachedMetadataHandler::kSourceKeyedMap (uint32_t)
139 // - num_entries (int)
140 // - key 1 (Key type)
141 // - len data 1 (size_t)
142 // - type data 1
143 // - data for key 1
144 // ...
145 // - key N (Key type)
146 // - len data N (size_t)
147 // - type data N
148 // - data for key N
149
150 namespace {
151 // Reading a value from a char buffer without using reinterpret cast. This
152 // should inline and optimize to the same code as *reinterpret_cast<T>(data),
153 // but without the risk of undefined behaviour.
154 template <typename T>
ReadVal(const uint8_t * data)155 T ReadVal(const uint8_t* data) {
156 static_assert(base::is_trivially_copyable<T>::value,
157 "ReadVal requires the value type to be copyable");
158 T ret;
159 memcpy(&ret, data, sizeof(T));
160 return ret;
161 }
162 } // namespace
163
SetSerializedCachedMetadata(mojo_base::BigBuffer data_buffer)164 void SourceKeyedCachedMetadataHandler::SetSerializedCachedMetadata(
165 mojo_base::BigBuffer data_buffer) {
166 const uint8_t* data = data_buffer.data();
167 size_t size = data_buffer.size();
168
169 // We only expect to receive cached metadata from the platform once. If this
170 // triggers, it indicates an efficiency problem which is most likely
171 // unexpected in code designed to improve performance.
172 DCHECK(cached_metadata_map_.IsEmpty());
173
174 // Ensure we have a marker.
175 if (size < sizeof(uint32_t))
176 return;
177 uint32_t marker = ReadVal<uint32_t>(data);
178 // Check for our marker to avoid conflicts with other kinds of cached
179 // metadata.
180 if (marker != CachedMetadataHandler::kSourceKeyedMap) {
181 return;
182 }
183 data += sizeof(uint32_t);
184 size -= sizeof(uint32_t);
185
186 // Ensure we have a length.
187 if (size < sizeof(int))
188 return;
189 int num_entries = ReadVal<int>(data);
190 data += sizeof(int);
191 size -= sizeof(int);
192
193 for (int i = 0; i < num_entries; ++i) {
194 // Ensure we have an entry key and size.
195 if (size < kKeySize + sizeof(size_t)) {
196 cached_metadata_map_.clear();
197 return;
198 }
199
200 Key key(kKeySize);
201 std::copy(data, data + kKeySize, std::begin(key));
202 data += kKeySize;
203 size_t entry_size = ReadVal<size_t>(data);
204 data += sizeof(size_t);
205
206 size -= kKeySize + sizeof(size_t);
207
208 // Ensure we have enough data for this entry.
209 if (size < entry_size) {
210 cached_metadata_map_.clear();
211 return;
212 }
213
214 if (scoped_refptr<CachedMetadata> deserialized_entry =
215 CachedMetadata::CreateFromSerializedData(data, entry_size)) {
216 // Only insert the deserialized entry if it deserialized correctly.
217 cached_metadata_map_.insert(key, std::move(deserialized_entry));
218 }
219 data += entry_size;
220 size -= entry_size;
221 }
222
223 // Ensure we have no more data.
224 if (size > 0) {
225 cached_metadata_map_.clear();
226 }
227 }
228
SendToPlatform()229 void SourceKeyedCachedMetadataHandler::SendToPlatform() {
230 if (!sender_)
231 return;
232
233 if (cached_metadata_map_.IsEmpty()) {
234 sender_->Send(nullptr, 0);
235 } else {
236 Vector<uint8_t> serialized_data;
237 uint32_t marker = CachedMetadataHandler::kSourceKeyedMap;
238 serialized_data.Append(reinterpret_cast<uint8_t*>(&marker), sizeof(marker));
239 int num_entries = cached_metadata_map_.size();
240 serialized_data.Append(reinterpret_cast<uint8_t*>(&num_entries),
241 sizeof(num_entries));
242 for (const auto& metadata : cached_metadata_map_) {
243 serialized_data.Append(metadata.key.data(), kKeySize);
244 base::span<const uint8_t> data = metadata.value->SerializedData();
245 size_t entry_size = data.size();
246 serialized_data.Append(reinterpret_cast<const uint8_t*>(&entry_size),
247 sizeof(entry_size));
248 serialized_data.Append(data.data(), data.size());
249 }
250 sender_->Send(serialized_data.data(), serialized_data.size());
251 }
252 }
253
254 } // namespace blink
255