| // Copyright 2019 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/services/app_service/public/cpp/instance_registry.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/containers/contains.h" |
| #include "base/unguessable_token.h" |
| #include "components/services/app_service/public/cpp/instance.h" |
| #include "components/services/app_service/public/cpp/instance_update.h" |
| |
| namespace apps { |
| |
| InstanceParams::InstanceParams(const std::string& app_id, aura::Window* window) |
| : app_id(app_id), window(window) {} |
| |
| InstanceParams::~InstanceParams() = default; |
| |
| InstanceRegistry::Observer::Observer(InstanceRegistry* instance_registry) { |
| Observe(instance_registry); |
| } |
| |
| InstanceRegistry::Observer::Observer() = default; |
| InstanceRegistry::Observer::~Observer() { |
| if (instance_registry_) { |
| instance_registry_->RemoveObserver(this); |
| } |
| } |
| |
| void InstanceRegistry::Observer::Observe(InstanceRegistry* instance_registry) { |
| if (instance_registry == instance_registry_) { |
| return; |
| } |
| |
| if (instance_registry_) { |
| instance_registry_->RemoveObserver(this); |
| } |
| |
| instance_registry_ = instance_registry; |
| if (instance_registry_) { |
| instance_registry_->AddObserver(this); |
| } |
| } |
| |
| InstanceRegistry::InstanceRegistry() = default; |
| |
| InstanceRegistry::~InstanceRegistry() { |
| for (auto& obs : observers_) { |
| obs.OnInstanceRegistryWillBeDestroyed(this); |
| } |
| DCHECK(observers_.empty()); |
| } |
| |
| void InstanceRegistry::AddObserver(Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void InstanceRegistry::RemoveObserver(Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void InstanceRegistry::CreateOrUpdateInstance(InstanceParams&& params) { |
| base::UnguessableToken instance_id; |
| auto it = window_to_instance_ids_.find(params.window); |
| if (it == window_to_instance_ids_.end()) { |
| instance_id = base::UnguessableToken::Create(); |
| } else { |
| instance_id = *it->second.begin(); |
| } |
| |
| auto instance = |
| std::make_unique<Instance>(params.app_id, instance_id, params.window); |
| |
| if (params.launch_id.has_value()) { |
| instance->SetLaunchId(params.launch_id.value()); |
| } |
| |
| if (params.state.has_value()) { |
| instance->UpdateState(params.state.value().first, |
| params.state.value().second); |
| } |
| |
| if (params.browser_context.has_value()) { |
| instance->SetBrowserContext(params.browser_context.value()); |
| } |
| OnInstance(std::move(instance)); |
| } |
| |
| void InstanceRegistry::OnInstance(InstancePtr delta) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_); |
| |
| if (!delta || !delta->InstanceId()) { |
| return; |
| } |
| |
| // If the instance state is not kDestroyed, adds to |
| // `window_to_instance_ids_`, otherwise removes the instance key from |
| // `window_to_instance_ids_`. |
| if (static_cast<InstanceState>(delta->State() & InstanceState::kDestroyed) == |
| InstanceState::kUnknown) { |
| auto it = instance_id_to_window_.find(delta->InstanceId()); |
| // If `window` is changed, remove the instance id from |
| // `window_to_instance_ids_`. |
| if (it != instance_id_to_window_.end() && it->second != delta->Window()) { |
| MaybeRemoveInstanceId(/*instance_id=*/it->first, /*window=*/it->second); |
| } |
| window_to_instance_ids_[delta->Window()].insert(delta->InstanceId()); |
| instance_id_to_window_[delta->InstanceId()] = delta->Window(); |
| } else { |
| MaybeRemoveInstanceId(delta->InstanceId(), delta->Window()); |
| instance_id_to_window_.erase(delta->InstanceId()); |
| } |
| |
| if (in_progress_) { |
| deltas_pending_.push_back(std::move(delta)); |
| return; |
| } |
| DoOnInstance(std::move(delta)); |
| while (!deltas_pending_.empty()) { |
| InstancePtr instance = std::move(*deltas_pending_.begin()); |
| deltas_pending_.pop_front(); |
| DoOnInstance(std::move(instance)); |
| } |
| } |
| |
| std::set<const Instance*> InstanceRegistry::GetInstances( |
| const std::string& app_id) { |
| auto it = app_id_to_instances_.find(app_id); |
| if (it == app_id_to_instances_.end()) { |
| return std::set<const Instance*>(); |
| } |
| return it->second; |
| } |
| |
| InstanceState InstanceRegistry::GetState(const aura::Window* window) const { |
| InstanceState state = InstanceState::kUnknown; |
| ForInstancesWithWindow(window, [&state](const apps::InstanceUpdate& update) { |
| state = update.State(); |
| }); |
| return state; |
| } |
| |
| ash::ShelfID InstanceRegistry::GetShelfId(const aura::Window* window) const { |
| ash::ShelfID shelf_id; |
| ForInstancesWithWindow( |
| window, [&shelf_id](const apps::InstanceUpdate& update) { |
| shelf_id = ash::ShelfID(update.AppId(), update.LaunchId()); |
| }); |
| return shelf_id; |
| } |
| |
| bool InstanceRegistry::Exists(const aura::Window* window) const { |
| bool found = false; |
| ForInstancesWithWindow( |
| window, [&](const apps::InstanceUpdate& update) { found = true; }); |
| return found; |
| } |
| |
| bool InstanceRegistry::ContainsAppId(const std::string& app_id) const { |
| return base::Contains(app_id_to_instances_, app_id); |
| } |
| |
| void InstanceRegistry::DoOnInstance(InstancePtr delta) { |
| in_progress_ = true; |
| |
| auto s_iter = states_.find(delta->InstanceId()); |
| Instance* state = (s_iter != states_.end()) ? s_iter->second.get() : nullptr; |
| if (InstanceUpdate::Equals(state, delta.get())) { |
| in_progress_ = false; |
| return; |
| } |
| |
| Instance* new_delta = delta.get(); |
| if (state) { |
| old_state_ = state->Clone(); |
| InstanceUpdate::Merge(state, new_delta); |
| } else { |
| old_state_.reset(); |
| |
| // `new_delta` is still valid, though `delta` is moved, because `new_delta` |
| // is the pointer to the content of `delta`. |
| states_.insert(std::make_pair(new_delta->InstanceId(), std::move(delta))); |
| app_id_to_instances_[new_delta->AppId()].insert(new_delta); |
| } |
| |
| for (auto& obs : observers_) { |
| obs.OnInstanceUpdate(InstanceUpdate(old_state_.get(), new_delta)); |
| } |
| |
| if (static_cast<InstanceState>(new_delta->State() & |
| InstanceState::kDestroyed) != |
| InstanceState::kUnknown) { |
| MaybeRemoveInstance(new_delta); |
| } |
| |
| old_state_.reset(); |
| in_progress_ = false; |
| } |
| |
| void InstanceRegistry::MaybeRemoveInstance(const Instance* delta) { |
| DCHECK(delta); |
| |
| auto it = states_.find(delta->InstanceId()); |
| if (it == states_.end()) { |
| return; |
| } |
| |
| const auto& app_id = delta->AppId(); |
| app_id_to_instances_[app_id].erase(it->second.get()); |
| if (app_id_to_instances_[app_id].empty()) { |
| app_id_to_instances_.erase(app_id); |
| } |
| |
| states_.erase(it); |
| } |
| |
| void InstanceRegistry::MaybeRemoveInstanceId( |
| const base::UnguessableToken& instance_id, |
| aura::Window* window) { |
| window_to_instance_ids_[window].erase(instance_id); |
| if (window_to_instance_ids_[window].empty()) { |
| window_to_instance_ids_.erase(window); |
| } |
| } |
| |
| } // namespace apps |