| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/lock_screen/lock_screen_storage_impl.h" |
| |
| #include <map> |
| #include <memory> |
| |
| #include "base/memory/ref_counted_delete_on_sequence.h" |
| #include "base/memory/singleton.h" |
| #include "base/path_service.h" |
| #include "base/sequence_checker.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/task_traits.h" |
| #include "base/task/thread_pool.h" |
| #include "base/values.h" |
| #include "components/value_store/value_store.h" |
| #include "components/value_store/value_store_factory.h" |
| #include "components/value_store/value_store_factory_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "crypto/sha2.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "url/origin.h" |
| |
| using value_store::ValueStore; |
| |
| namespace content { |
| |
| namespace { |
| |
| // See Extensions.Database.Open in histograms.xml. |
| const char kValueStoreDatabaseUMAClientName[] = "WebAppsLockScreen"; |
| |
| } // namespace |
| |
| // Helper class for running blocking tasks on a thread pool. |
| class LockScreenStorageHelper { |
| public: |
| LockScreenStorageHelper(); |
| ~LockScreenStorageHelper() = default; |
| |
| void Init(const base::FilePath& base_path); |
| std::vector<std::string> GetKeys(const url::Origin& origin); |
| bool SetData(const url::Origin& origin, |
| const std::string& key, |
| const std::string& data); |
| |
| private: |
| ValueStore* GetValueStoreForOrigin(const url::Origin& origin); |
| |
| scoped_refptr<value_store::ValueStoreFactory> value_store_factory_; |
| // Maps storage directory filename to ValueStore for a particular origin. |
| // TODO(crbug.com/1268227): If there can only be one lock screen app at a |
| // time, this does not need to be a map. Otherwise, there should be a clean |
| // way of evicting value stores databases from this cache. |
| std::map<std::string, std::unique_ptr<ValueStore>> storage_map_; |
| }; |
| |
| LockScreenStorageHelper::LockScreenStorageHelper() {} |
| |
| void LockScreenStorageHelper::Init(const base::FilePath& base_path) { |
| value_store_factory_ = |
| base::MakeRefCounted<value_store::ValueStoreFactoryImpl>(base_path); |
| } |
| |
| std::vector<std::string> LockScreenStorageHelper::GetKeys( |
| const url::Origin& origin) { |
| ValueStore* value_store = GetValueStoreForOrigin(origin); |
| ValueStore::ReadResult read = value_store->Get(); |
| std::vector<std::string> result; |
| if (!read.status().ok()) |
| return result; |
| for (auto kv : read.settings()) { |
| result.push_back(kv.first); |
| } |
| |
| return result; |
| } |
| |
| bool LockScreenStorageHelper::SetData(const url::Origin& origin, |
| const std::string& key, |
| const std::string& data) { |
| ValueStore* value_store = GetValueStoreForOrigin(origin); |
| ValueStore::WriteResult write = |
| value_store->Set(ValueStore::DEFAULTS, key, base::Value(data)); |
| return write.status().ok(); |
| } |
| |
| ValueStore* LockScreenStorageHelper::GetValueStoreForOrigin( |
| const url::Origin& origin) { |
| DCHECK(!origin.opaque()); |
| |
| // ValueStore will create a directory for storing its data. The directory name |
| // is passed in. We want to key data by origin, so we use a hash of the origin |
| // as the directory name under which to store the data. Origin.Serialize() |
| // should just concatenate the scheme/host/port, which are the components that |
| // need to appear identical if the two origins need to compare equal. Hence |
| // if two origins are equal, the serialized origins should also be equal. |
| std::string serialized_origin = origin.Serialize(); |
| uint8_t hash[crypto::kSHA256Length]; |
| crypto::SHA256HashString(serialized_origin, hash, sizeof(hash)); |
| std::string filename = base::HexEncode(hash, crypto::kSHA256Length); |
| |
| auto iter = storage_map_.find(filename); |
| if (iter != storage_map_.end()) |
| return iter->second.get(); |
| |
| base::FilePath value_store_path(filename); |
| std::unique_ptr<ValueStore> value_store = |
| value_store_factory_->CreateValueStore(value_store_path, |
| kValueStoreDatabaseUMAClientName); |
| ValueStore* result = value_store.get(); |
| storage_map_.emplace(filename, std::move(value_store)); |
| return result; |
| } |
| |
| // static |
| LockScreenStorage* LockScreenStorage::GetInstance() { |
| return LockScreenStorageImpl::GetInstance(); |
| } |
| |
| // static |
| LockScreenStorageImpl* LockScreenStorageImpl::GetInstance() { |
| return base::Singleton< |
| LockScreenStorageImpl, |
| base::LeakySingletonTraits<LockScreenStorageImpl>>::get(); |
| } |
| |
| LockScreenStorageImpl::LockScreenStorageImpl() |
| : helper_(base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})) { |
| } |
| |
| LockScreenStorageImpl::~LockScreenStorageImpl() = default; |
| |
| void LockScreenStorageImpl::Init(content::BrowserContext* browser_context, |
| const base::FilePath& base_path) { |
| DCHECK(!browser_context_); |
| DCHECK(!browser_context->IsOffTheRecord()); |
| browser_context_ = browser_context; |
| helper_.AsyncCall(&LockScreenStorageHelper::Init).WithArgs(base_path); |
| } |
| |
| void LockScreenStorageImpl::GetKeys( |
| const url::Origin& origin, |
| blink::mojom::LockScreenService::GetKeysCallback callback) { |
| helper_.AsyncCall(&LockScreenStorageHelper::GetKeys) |
| .WithArgs(origin) |
| .Then(base::BindOnce(&LockScreenStorageImpl::OnGetKeys, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void LockScreenStorageImpl::SetData( |
| const url::Origin& origin, |
| const std::string& key, |
| const std::string& data, |
| blink::mojom::LockScreenService::SetDataCallback callback) { |
| helper_.AsyncCall(&LockScreenStorageHelper::SetData) |
| .WithArgs(origin, key, data) |
| .Then(base::BindOnce(&LockScreenStorageImpl::OnSetData, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| bool LockScreenStorageImpl::IsAllowedBrowserContext( |
| content::BrowserContext* browser_context) { |
| return browser_context == browser_context_; |
| } |
| |
| void LockScreenStorageImpl::OnGetKeys( |
| blink::mojom::LockScreenService::GetKeysCallback callback, |
| const std::vector<std::string>& result) { |
| std::move(callback).Run(result); |
| } |
| |
| void LockScreenStorageImpl::OnSetData( |
| blink::mojom::LockScreenService::SetDataCallback callback, |
| bool success) { |
| if (success) { |
| std::move(callback).Run(blink::mojom::LockScreenServiceStatus::kSuccess); |
| } else { |
| std::move(callback).Run(blink::mojom::LockScreenServiceStatus::kWriteError); |
| } |
| } |
| |
| void LockScreenStorageImpl::InitForTesting( |
| content::BrowserContext* browser_context, |
| const base::FilePath& base_path) { |
| browser_context_ = nullptr; |
| Init(browser_context, base_path); |
| } |
| |
| } // namespace content |