| // Copyright 2018 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/storage/store/memory_store.h" |
| |
| #include <map> |
| #include <string> |
| #include <tuple> |
| #include <vector> |
| |
| #include "base/debug/trace_event.h" |
| #include "cobalt/storage/storage_constants.h" |
| #include "cobalt/storage/store/storage.pb.h" |
| #include "googleurl/src/gurl.h" |
| #include "nb/memory_scope.h" |
| |
| namespace cobalt { |
| namespace storage { |
| namespace { |
| |
| bool IsValidFormat(const std::vector<uint8>& buffer) { |
| return buffer.size() >= kStorageHeaderSize && |
| memcmp(buffer.data(), kStorageHeader, kStorageHeaderSize) == 0; |
| } |
| |
| } // namespace |
| |
| // Using Pimpl design pattern to hide protobuf implementation details. |
| // The generated proto code (including the header) needs special build |
| // configuration to work as expected. Which means adding special build settings |
| // to any place where the headers is included. |
| class MemoryStore::Impl { |
| public: |
| // Initialization |
| Impl(); |
| bool Initialize(const std::vector<uint8>& in); |
| |
| // Serialization |
| bool Serialize(std::vector<uint8>* out) const; |
| |
| // Cookies |
| void GetAllCookies(std::vector<net::CanonicalCookie*>* cookies) const; |
| void AddCookie(const net::CanonicalCookie& cc, int64 expiration_time_us); |
| void UpdateCookieAccessTime(const net::CanonicalCookie& cc, int64 time_us); |
| void DeleteCookie(const net::CanonicalCookie& cc); |
| |
| // Local Storage |
| void ReadAllLocalStorage(const loader::Origin& origin, |
| LocalStorageMap* local_storage_map) const; |
| |
| void WriteToLocalStorage(const loader::Origin& origin, const std::string& key, |
| const std::string& value); |
| void DeleteFromLocalStorage(const loader::Origin& origin, |
| const std::string& key); |
| void ClearLocalStorage(const loader::Origin& origin); |
| |
| // Disable copying |
| Impl(const Impl& store) = delete; |
| Impl& operator=(const Impl& store) = delete; |
| |
| private: |
| typedef std::tuple<std::string, std::string, std::string> CookieKey; |
| std::map<CookieKey, Cookie> cookies_map_; |
| |
| typedef std::map<std::string, std::string> LocalStorageKeyValueMap; |
| typedef std::map<std::string, LocalStorageKeyValueMap> |
| LocalStorageByOriginMap; |
| |
| LocalStorageByOriginMap local_storage_by_origin_map_; |
| }; |
| |
| MemoryStore::Impl::Impl() {} |
| |
| bool MemoryStore::Impl::Initialize(const std::vector<uint8>& in) { |
| TRACK_MEMORY_SCOPE("Storage"); |
| if (!IsValidFormat(in)) { |
| LOG(ERROR) << "Invalid format with size=" << in.size(); |
| return false; |
| } |
| Storage storage_data; |
| if (!storage_data.ParseFromArray( |
| reinterpret_cast<const char*>(in.data() + kStorageHeaderSize), |
| in.size() - kStorageHeaderSize)) { |
| LOG(ERROR) << "Unable to parse storage with size" << in.size(); |
| return false; |
| } |
| for (auto& cookie : storage_data.cookies()) { |
| cookies_map_[std::make_tuple(cookie.domain(), cookie.path(), |
| cookie.name())] = cookie; |
| } |
| for (const auto& local_storages : storage_data.local_storages()) { |
| for (const auto& local_storage_entry : |
| local_storages.local_storage_entries()) { |
| GURL gurl(local_storages.serialized_origin()); |
| loader::Origin origin(gurl); |
| if (origin.is_opaque()) { |
| LOG(WARNING) << "Ignoring storage for opaque origin " |
| << local_storages.serialized_origin(); |
| continue; |
| } |
| local_storage_by_origin_map_[origin.SerializedOrigin()] |
| [local_storage_entry.key()] = |
| local_storage_entry.value(); |
| } |
| } |
| return true; |
| } |
| |
| bool MemoryStore::Impl::Serialize(std::vector<uint8>* out) const { |
| TRACK_MEMORY_SCOPE("Storage"); |
| Storage storage_data; |
| for (const auto& cookie_pair : cookies_map_) { |
| *(storage_data.add_cookies()) = cookie_pair.second; |
| } |
| for (const auto& origin_storage_pair : local_storage_by_origin_map_) { |
| LocalStorage* local_storage = storage_data.add_local_storages(); |
| local_storage->set_serialized_origin(origin_storage_pair.first); |
| for (const auto& key_value_pair : origin_storage_pair.second) { |
| LocalStorageEntry* local_storage_entry = |
| local_storage->add_local_storage_entries(); |
| local_storage_entry->set_key(key_value_pair.first); |
| local_storage_entry->set_value(key_value_pair.second); |
| } |
| } |
| size_t size = storage_data.ByteSize(); |
| out->resize(kStorageHeaderSize + size); |
| char* buffer_ptr = reinterpret_cast<char*>(out->data()); |
| memcpy(buffer_ptr, kStorageHeader, kStorageHeaderSize); |
| if (size > 0 && |
| !storage_data.SerializeToArray(buffer_ptr + kStorageHeaderSize, size)) { |
| LOG(ERROR) << "Failed to serialize message with size=" << size; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void MemoryStore::Impl::GetAllCookies( |
| std::vector<net::CanonicalCookie*>* cookies) const { |
| TRACK_MEMORY_SCOPE("Storage"); |
| for (const auto& cookie_pair : cookies_map_) { |
| // We create a CanonicalCookie directly through its constructor instead of |
| // through CanonicalCookie::Create() and its sanitization because these |
| // values are just serialized from a former instance of a CanonicalCookie |
| // object that *was* created through CanonicalCookie::Create(). |
| scoped_ptr<net::CanonicalCookie> cookie(new net::CanonicalCookie( |
| GURL(""), cookie_pair.second.name(), cookie_pair.second.value(), |
| cookie_pair.second.domain(), cookie_pair.second.path(), |
| "" /* mac_key */, "" /* mac_algorithm */, |
| base::Time::FromInternalValue(cookie_pair.second.creation_time_us()), |
| base::Time::FromInternalValue(cookie_pair.second.expiration_time_us()), |
| base::Time::FromInternalValue(cookie_pair.second.last_access_time_us()), |
| cookie_pair.second.secure(), cookie_pair.second.http_only())); |
| cookies->push_back(cookie.release()); |
| } |
| } |
| |
| void MemoryStore::Impl::AddCookie(const net::CanonicalCookie& cc, |
| int64 expiration_time_us) { |
| TRACK_MEMORY_SCOPE("Storage"); |
| Cookie cookie; |
| cookie.set_domain(cc.Domain()); |
| cookie.set_path(cc.Path()); |
| cookie.set_name(cc.Name()); |
| cookie.set_value(cc.Value()); |
| cookie.set_secure(cc.IsSecure()); |
| cookie.set_http_only(cc.IsHttpOnly()); |
| |
| cookie.set_creation_time_us(cc.CreationDate().ToInternalValue()); |
| cookie.set_expiration_time_us(expiration_time_us); |
| cookie.set_last_access_time_us(cc.LastAccessDate().ToInternalValue()); |
| |
| CookieKey key = std::make_tuple(cc.Domain(), cc.Path(), cc.Name()); |
| cookies_map_[key] = cookie; |
| } |
| |
| void MemoryStore::Impl::UpdateCookieAccessTime(const net::CanonicalCookie& cc, |
| int64 time_us) { |
| CookieKey key = std::make_tuple(cc.Domain(), cc.Path(), cc.Name()); |
| cookies_map_[key].set_last_access_time_us(time_us); |
| } |
| |
| void MemoryStore::Impl::DeleteCookie(const net::CanonicalCookie& cc) { |
| CookieKey key = std::make_tuple(cc.Domain(), cc.Path(), cc.Name()); |
| cookies_map_.erase(key); |
| } |
| |
| void MemoryStore::Impl::ReadAllLocalStorage( |
| const loader::Origin& origin, LocalStorageMap* local_storage_map) const { |
| TRACK_MEMORY_SCOPE("Storage"); |
| const auto& it = local_storage_by_origin_map_.find(origin.SerializedOrigin()); |
| |
| if (it != local_storage_by_origin_map_.end()) { |
| local_storage_map->insert(it->second.begin(), it->second.end()); |
| } |
| } |
| |
| void MemoryStore::Impl::WriteToLocalStorage(const loader::Origin& origin, |
| const std::string& key, |
| const std::string& value) { |
| TRACK_MEMORY_SCOPE("Storage"); |
| local_storage_by_origin_map_[origin.SerializedOrigin()][key] = value; |
| } |
| |
| void MemoryStore::Impl::DeleteFromLocalStorage(const loader::Origin& origin, |
| const std::string& key) { |
| TRACK_MEMORY_SCOPE("Storage"); |
| local_storage_by_origin_map_[origin.SerializedOrigin()].erase(key); |
| } |
| |
| void MemoryStore::Impl::ClearLocalStorage(const loader::Origin& origin) { |
| TRACK_MEMORY_SCOPE("Storage"); |
| local_storage_by_origin_map_[origin.SerializedOrigin()].clear(); |
| } |
| |
| MemoryStore::MemoryStore() { impl_.reset(new Impl()); } |
| |
| bool MemoryStore::Initialize(const std::vector<uint8>& in) { |
| return impl_->Initialize(in); |
| } |
| |
| bool MemoryStore::Serialize(std::vector<uint8>* out) const { |
| return impl_->Serialize(out); |
| } |
| |
| void MemoryStore::GetAllCookies( |
| std::vector<net::CanonicalCookie*>* cookies) const { |
| impl_->GetAllCookies(cookies); |
| } |
| |
| void MemoryStore::AddCookie(const net::CanonicalCookie& cc, |
| int64 expiration_time_us) { |
| impl_->AddCookie(cc, expiration_time_us); |
| } |
| |
| void MemoryStore::UpdateCookieAccessTime(const net::CanonicalCookie& cc, |
| int64 time_us) { |
| impl_->UpdateCookieAccessTime(cc, time_us); |
| } |
| |
| void MemoryStore::DeleteCookie(const net::CanonicalCookie& cc) { |
| impl_->DeleteCookie(cc); |
| } |
| |
| void MemoryStore::ReadAllLocalStorage( |
| const loader::Origin& origin, LocalStorageMap* local_storage_map) const { |
| impl_->ReadAllLocalStorage(origin, local_storage_map); |
| } |
| |
| void MemoryStore::WriteToLocalStorage(const loader::Origin& origin, |
| const std::string& key, |
| const std::string& value) { |
| impl_->WriteToLocalStorage(origin, key, value); |
| } |
| |
| void MemoryStore::DeleteFromLocalStorage(const loader::Origin& origin, |
| const std::string& key) { |
| impl_->DeleteFromLocalStorage(origin, key); |
| } |
| |
| void MemoryStore::ClearLocalStorage(const loader::Origin& origin) { |
| impl_->ClearLocalStorage(origin); |
| } |
| |
| MemoryStore::~MemoryStore() {} |
| } // namespace storage |
| } // namespace cobalt |