| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/component_updater/ash/fake_component_manager_ash.h" |
| |
| #include <utility> |
| |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/logging.h" |
| #include "base/notimplemented.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/version.h" |
| |
| namespace component_updater { |
| |
| FakeComponentManagerAsh::ComponentInfo::ComponentInfo( |
| Error load_response, |
| const base::FilePath& install_path, |
| const base::FilePath& mount_path) |
| : load_response(load_response), |
| install_path(install_path), |
| mount_path(mount_path) { |
| // If component load fails, neither install nor mount path should be set. |
| DCHECK(load_response == Error::NONE || |
| (install_path.empty() && mount_path.empty())); |
| // Component should have install path set if it's expected to be loaded. |
| DCHECK(load_response != Error::NONE || (!install_path.empty())); |
| } |
| |
| FakeComponentManagerAsh::ComponentInfo::ComponentInfo( |
| Error load_response, |
| const base::FilePath& install_path, |
| const base::FilePath& mount_path, |
| const base::Version& version) |
| : load_response(load_response), |
| install_path(install_path), |
| mount_path(mount_path), |
| version(version) { |
| // If component load fails, neither install nor mount path should be set and |
| // version should be invalid. |
| DCHECK(load_response == Error::NONE || |
| (install_path.empty() && mount_path.empty() && !version.IsValid())); |
| // Component should have install path and version set if it's expected to be |
| // loaded. |
| DCHECK(load_response != Error::NONE || |
| (!install_path.empty() && version.IsValid())); |
| } |
| |
| FakeComponentManagerAsh::ComponentInfo::ComponentInfo( |
| const FakeComponentManagerAsh::ComponentInfo& other) = default; |
| |
| FakeComponentManagerAsh::ComponentInfo& |
| FakeComponentManagerAsh::ComponentInfo::operator=( |
| const FakeComponentManagerAsh::ComponentInfo& other) = default; |
| |
| FakeComponentManagerAsh::ComponentInfo::~ComponentInfo() = default; |
| |
| FakeComponentManagerAsh::FakeComponentManagerAsh() = default; |
| |
| FakeComponentManagerAsh::~FakeComponentManagerAsh() = default; |
| |
| bool FakeComponentManagerAsh::FinishLoadRequest( |
| const std::string& name, |
| const ComponentInfo& component_info) { |
| if (!pending_loads_.count(name) || pending_loads_[name].empty()) { |
| LOG(ERROR) << "No pending load for " << name; |
| return false; |
| } |
| |
| auto& pending_load = pending_loads_[name].front(); |
| FinishComponentLoad(name, pending_load.mount_requested, component_info); |
| |
| LoadCallback callback = std::move(pending_load.callback); |
| pending_loads_[name].pop_front(); |
| |
| std::move(callback).Run(component_info.load_response, |
| component_info.load_response == Error::NONE |
| ? component_info.mount_path |
| : base::FilePath()); |
| return true; |
| } |
| |
| bool FakeComponentManagerAsh::ResetComponentState(const std::string& name, |
| const ComponentInfo& state) { |
| if (!supported_components_.count(name)) { |
| return false; |
| } |
| |
| installed_components_.erase(name); |
| mounted_components_.erase(name); |
| |
| component_infos_.erase(name); |
| component_infos_.emplace(name, ComponentInfo(state)); |
| return true; |
| } |
| |
| bool FakeComponentManagerAsh::HasPendingInstall( |
| const std::string& name) const { |
| DCHECK(queue_load_requests_); |
| |
| const auto& it = pending_loads_.find(name); |
| return it != pending_loads_.end() && !it->second.empty(); |
| } |
| |
| bool FakeComponentManagerAsh::UpdateRequested(const std::string& name) const { |
| DCHECK(queue_load_requests_); |
| |
| const auto& it = pending_loads_.find(name); |
| return it != pending_loads_.end() && !it->second.empty() && |
| it->second.front().needs_update; |
| } |
| |
| void FakeComponentManagerAsh::SetDelegate(Delegate* delegate) { |
| // No-op, not used by the fake. |
| } |
| |
| void FakeComponentManagerAsh::Load(const std::string& name, |
| MountPolicy mount_policy, |
| UpdatePolicy update_policy, |
| LoadCallback load_callback) { |
| if (!supported_components_.count(name)) { |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(load_callback), |
| Error::UNKNOWN_COMPONENT, base::FilePath())); |
| return; |
| } |
| |
| bool needs_update = update_policy == UpdatePolicy::kForce || |
| (!installed_components_.count(name) && |
| update_policy != UpdatePolicy::kSkip); |
| |
| // The request has to be handled if the component is not yet installed, or it |
| // requires immediate update. |
| if (needs_update || !installed_components_.count(name)) { |
| HandlePendingRequest(name, mount_policy == MountPolicy::kMount, |
| needs_update, std::move(load_callback)); |
| return; |
| } |
| |
| // Handle request if the component has yet to be mounted - e.g. if previous |
| // loads installed the component without mounting it. |
| if (!mounted_components_.count(name) && mount_policy == MountPolicy::kMount) { |
| HandlePendingRequest(name, true /*mount_requested*/, false /*needs_update*/, |
| std::move(load_callback)); |
| return; |
| } |
| |
| // The component has been prevoiusly installed, and mounted as required by |
| // this load request - run the callback according to the existing state. |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(load_callback), Error::NONE, |
| mount_policy == MountPolicy::kMount |
| ? mounted_components_[name] |
| : base::FilePath())); |
| } |
| |
| bool FakeComponentManagerAsh::Unload(const std::string& name) { |
| { |
| base::AutoLock lock(registered_components_lock_); |
| registered_components_.erase(name); |
| } |
| mounted_components_.erase(name); |
| installed_components_.erase(name); |
| return unload_component_result_; |
| } |
| |
| void FakeComponentManagerAsh::RegisterCompatiblePath( |
| const std::string& name, |
| CompatibleComponentInfo info) { |
| installed_components_[name] = std::move(info); |
| } |
| |
| void FakeComponentManagerAsh::UnregisterCompatiblePath( |
| const std::string& name) { |
| installed_components_.erase(name); |
| } |
| |
| base::FilePath FakeComponentManagerAsh::GetCompatiblePath( |
| const std::string& name) const { |
| const auto& it = installed_components_.find(name); |
| if (it == installed_components_.end()) { |
| return base::FilePath(); |
| } |
| return it->second.path; |
| } |
| |
| void FakeComponentManagerAsh::SetRegisteredComponents( |
| const std::set<std::string>& components) { |
| base::AutoLock lock(registered_components_lock_); |
| registered_components_ = components; |
| } |
| |
| bool FakeComponentManagerAsh::IsRegisteredMayBlock(const std::string& name) { |
| base::AutoLock lock(registered_components_lock_); |
| return registered_components_.count(name); |
| } |
| |
| void FakeComponentManagerAsh::RegisterInstalled() { |
| NOTIMPLEMENTED(); |
| } |
| |
| void FakeComponentManagerAsh::GetVersion( |
| const std::string& name, |
| base::OnceCallback<void(const base::Version&)> version_callback) const { |
| const auto& component_info = component_infos_.find(name); |
| if (component_info == component_infos_.end() || |
| !component_info->second.version.has_value()) { |
| std::move(version_callback).Run(base::Version()); |
| return; |
| } |
| |
| std::move(version_callback).Run(component_info->second.version.value()); |
| } |
| |
| FakeComponentManagerAsh::LoadRequest::LoadRequest(bool mount_requested, |
| bool needs_update, |
| LoadCallback callback) |
| : mount_requested(mount_requested), |
| needs_update(needs_update), |
| callback(std::move(callback)) {} |
| |
| FakeComponentManagerAsh::LoadRequest::~LoadRequest() = default; |
| |
| void FakeComponentManagerAsh::HandlePendingRequest(const std::string& name, |
| bool mount_requested, |
| bool needs_update, |
| LoadCallback callback) { |
| if (queue_load_requests_) { |
| pending_loads_[name].emplace_back(mount_requested, needs_update, |
| std::move(callback)); |
| return; |
| } |
| |
| const auto& component_info = component_infos_.find(name); |
| if (component_info == component_infos_.end()) { |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), Error::INSTALL_FAILURE, |
| base::FilePath())); |
| return; |
| } |
| |
| FinishComponentLoad(name, mount_requested, component_info->second); |
| |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(callback), component_info->second.load_response, |
| component_info->second.mount_path)); |
| } |
| |
| void FakeComponentManagerAsh::FinishComponentLoad( |
| const std::string& name, |
| bool mount_requested, |
| const ComponentInfo& component_info) { |
| { |
| base::AutoLock lock(registered_components_lock_); |
| registered_components_.insert(name); |
| } |
| |
| if (component_info.load_response != Error::NONE) { |
| return; |
| } |
| |
| DCHECK_EQ(mount_requested, !component_info.mount_path.empty()); |
| installed_components_[name] = CompatibleComponentInfo( |
| component_info.install_path, component_info.version); |
| if (mount_requested) { |
| mounted_components_[name] = component_info.mount_path; |
| } |
| } |
| |
| } // namespace component_updater |