summaryrefslogtreecommitdiffstats
path: root/chromium/components/policy/core/common/schema_registry.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/policy/core/common/schema_registry.cc')
-rw-r--r--chromium/components/policy/core/common/schema_registry.cc279
1 files changed, 279 insertions, 0 deletions
diff --git a/chromium/components/policy/core/common/schema_registry.cc b/chromium/components/policy/core/common/schema_registry.cc
new file mode 100644
index 00000000000..3891636d268
--- /dev/null
+++ b/chromium/components/policy/core/common/schema_registry.cc
@@ -0,0 +1,279 @@
+// Copyright 2013 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 "components/policy/core/common/schema_registry.h"
+
+#include "base/check_op.h"
+#include "base/notreached.h"
+#include "base/observer_list.h"
+#include "extensions/buildflags/buildflags.h"
+
+namespace policy {
+
+SchemaRegistry::Observer::~Observer() {}
+
+SchemaRegistry::InternalObserver::~InternalObserver() {}
+
+SchemaRegistry::SchemaRegistry() : schema_map_(new SchemaMap) {
+ for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i)
+ domains_ready_[i] = false;
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
+ SetExtensionsDomainsReady();
+#endif
+}
+
+SchemaRegistry::~SchemaRegistry() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ for (auto& observer : internal_observers_)
+ observer.OnSchemaRegistryShuttingDown(this);
+}
+
+void SchemaRegistry::RegisterComponent(const PolicyNamespace& ns,
+ const Schema& schema) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ ComponentMap map;
+ map[ns.component_id] = schema;
+ RegisterComponents(ns.domain, map);
+}
+
+void SchemaRegistry::RegisterComponents(PolicyDomain domain,
+ const ComponentMap& components) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // Don't issue notifications if nothing is being registered.
+ if (components.empty())
+ return;
+ // Assume that a schema was updated if the namespace was already registered
+ // before.
+ DomainMap map(schema_map_->GetDomains());
+ for (auto it = components.begin(); it != components.end(); ++it)
+ map[domain][it->first] = it->second;
+ schema_map_ = new SchemaMap(std::move(map));
+ Notify(true);
+}
+
+void SchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DomainMap map(schema_map_->GetDomains());
+ if (map[ns.domain].erase(ns.component_id) != 0) {
+ schema_map_ = new SchemaMap(std::move(map));
+ Notify(false);
+ } else {
+ // Extension might be uninstalled before install so the associated policies
+ // are unregistered before registered. For example, a policy forced
+ // extension is removed from forced list during launch due to policy update.
+ DCHECK(ns.domain != POLICY_DOMAIN_CHROME);
+ }
+}
+
+bool SchemaRegistry::IsReady() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i) {
+ if (!domains_ready_[i])
+ return false;
+ }
+ return true;
+}
+
+void SchemaRegistry::SetDomainReady(PolicyDomain domain) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (domains_ready_[domain])
+ return;
+ domains_ready_[domain] = true;
+ if (IsReady()) {
+ for (auto& observer : observers_)
+ observer.OnSchemaRegistryReady();
+ }
+}
+
+void SchemaRegistry::SetAllDomainsReady() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i)
+ SetDomainReady(static_cast<PolicyDomain>(i));
+}
+
+void SchemaRegistry::SetExtensionsDomainsReady() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ SetDomainReady(POLICY_DOMAIN_EXTENSIONS);
+ SetDomainReady(POLICY_DOMAIN_SIGNIN_EXTENSIONS);
+}
+
+void SchemaRegistry::AddObserver(Observer* observer) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ observers_.AddObserver(observer);
+}
+
+void SchemaRegistry::RemoveObserver(Observer* observer) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ observers_.RemoveObserver(observer);
+}
+
+void SchemaRegistry::AddInternalObserver(InternalObserver* observer) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ internal_observers_.AddObserver(observer);
+}
+
+void SchemaRegistry::RemoveInternalObserver(InternalObserver* observer) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ internal_observers_.RemoveObserver(observer);
+}
+
+void SchemaRegistry::Notify(bool has_new_schemas) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ for (auto& observer : observers_)
+ observer.OnSchemaRegistryUpdated(has_new_schemas);
+}
+
+CombinedSchemaRegistry::CombinedSchemaRegistry()
+ : own_schema_map_(new SchemaMap) {
+ // The combined registry is always ready, since it can always start tracking
+ // another registry that is not ready yet and going from "ready" to "not
+ // ready" is not allowed.
+ SetAllDomainsReady();
+}
+
+CombinedSchemaRegistry::~CombinedSchemaRegistry() {}
+
+void CombinedSchemaRegistry::Track(SchemaRegistry* registry) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ registries_.insert(registry);
+ registry->AddObserver(this);
+ registry->AddInternalObserver(this);
+ // Recombine the maps only if the |registry| has any components other than
+ // POLICY_DOMAIN_CHROME.
+ if (registry->schema_map()->HasComponents())
+ Combine(true);
+}
+
+void CombinedSchemaRegistry::RegisterComponents(
+ PolicyDomain domain,
+ const ComponentMap& components) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DomainMap map(own_schema_map_->GetDomains());
+ for (auto it = components.begin(); it != components.end(); ++it)
+ map[domain][it->first] = it->second;
+ own_schema_map_ = new SchemaMap(std::move(map));
+ Combine(true);
+}
+
+void CombinedSchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DomainMap map(own_schema_map_->GetDomains());
+ if (map[ns.domain].erase(ns.component_id) != 0) {
+ own_schema_map_ = new SchemaMap(std::move(map));
+ Combine(false);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void CombinedSchemaRegistry::OnSchemaRegistryUpdated(bool has_new_schemas) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ Combine(has_new_schemas);
+}
+
+void CombinedSchemaRegistry::OnSchemaRegistryShuttingDown(
+ SchemaRegistry* registry) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ registry->RemoveObserver(this);
+ registry->RemoveInternalObserver(this);
+ if (registries_.erase(registry) != 0) {
+ if (registry->schema_map()->HasComponents())
+ Combine(false);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void CombinedSchemaRegistry::Combine(bool has_new_schemas) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // If two registries publish a Schema for the same component then it's
+ // undefined which version gets in the combined registry.
+ //
+ // The common case is that both registries want policy for the same component,
+ // and the Schemas should be the same; in that case this makes no difference.
+ //
+ // But if the Schemas are different then one of the components is out of date.
+ // In that case the policy loaded will be valid only for one of them, until
+ // the outdated components are updated. This is a known limitation of the
+ // way policies are loaded currently, but isn't a problem worth fixing for
+ // the time being.
+ DomainMap map(own_schema_map_->GetDomains());
+ for (auto reg_it = registries_.begin(); reg_it != registries_.end();
+ ++reg_it) {
+ const DomainMap& reg_domain_map = (*reg_it)->schema_map()->GetDomains();
+ for (auto domain_it = reg_domain_map.begin();
+ domain_it != reg_domain_map.end(); ++domain_it) {
+ const ComponentMap& reg_component_map = domain_it->second;
+ for (auto comp_it = reg_component_map.begin();
+ comp_it != reg_component_map.end(); ++comp_it) {
+ map[domain_it->first][comp_it->first] = comp_it->second;
+ }
+ }
+ }
+ schema_map_ = new SchemaMap(std::move(map));
+ Notify(has_new_schemas);
+}
+
+ForwardingSchemaRegistry::ForwardingSchemaRegistry(SchemaRegistry* wrapped)
+ : wrapped_(wrapped) {
+ schema_map_ = wrapped_->schema_map();
+ wrapped_->AddObserver(this);
+ wrapped_->AddInternalObserver(this);
+ UpdateReadiness();
+}
+
+ForwardingSchemaRegistry::~ForwardingSchemaRegistry() {
+ if (wrapped_) {
+ wrapped_->RemoveObserver(this);
+ wrapped_->RemoveInternalObserver(this);
+ }
+}
+
+void ForwardingSchemaRegistry::RegisterComponents(
+ PolicyDomain domain,
+ const ComponentMap& components) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // POLICY_DOMAIN_CHROME is skipped to avoid spurious updates when a new
+ // Profile is created. If the ForwardingSchemaRegistry is used outside
+ // device-level accounts then this should become configurable.
+ if (wrapped_ && domain != POLICY_DOMAIN_CHROME)
+ wrapped_->RegisterComponents(domain, components);
+ // Ignore otherwise.
+}
+
+void ForwardingSchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (wrapped_)
+ wrapped_->UnregisterComponent(ns);
+ // Ignore otherwise.
+}
+
+void ForwardingSchemaRegistry::OnSchemaRegistryUpdated(bool has_new_schemas) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ schema_map_ = wrapped_->schema_map();
+ Notify(has_new_schemas);
+}
+
+void ForwardingSchemaRegistry::OnSchemaRegistryReady() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ UpdateReadiness();
+}
+
+void ForwardingSchemaRegistry::OnSchemaRegistryShuttingDown(
+ SchemaRegistry* registry) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_EQ(wrapped_, registry);
+ wrapped_->RemoveObserver(this);
+ wrapped_->RemoveInternalObserver(this);
+ wrapped_ = nullptr;
+ // Keep serving the same |schema_map_|.
+}
+
+void ForwardingSchemaRegistry::UpdateReadiness() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (wrapped_->IsReady())
+ SetAllDomainsReady();
+}
+
+} // namespace policy