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