|  | // Copyright 2015 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/domain_reliability/context_manager.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/metrics/histogram_macros.h" | 
|  |  | 
|  | namespace domain_reliability { | 
|  |  | 
|  | DomainReliabilityContextManager::DomainReliabilityContextManager( | 
|  | DomainReliabilityContext::Factory* context_factory) | 
|  | : context_factory_(context_factory) { | 
|  | } | 
|  |  | 
|  | DomainReliabilityContextManager::~DomainReliabilityContextManager() { | 
|  | RemoveContexts( | 
|  | base::Callback<bool(const GURL&)>() /* no filter - delete everything */); | 
|  | } | 
|  |  | 
|  | void DomainReliabilityContextManager::RouteBeacon( | 
|  | std::unique_ptr<DomainReliabilityBeacon> beacon) { | 
|  | DomainReliabilityContext* context = GetContextForHost(beacon->url.host()); | 
|  | if (!context) | 
|  | return; | 
|  |  | 
|  | bool queued = context->OnBeacon(std::move(beacon)); | 
|  | if (!queued) | 
|  | return; | 
|  |  | 
|  | base::TimeTicks now = base::TimeTicks::Now(); | 
|  | if (!last_routed_beacon_time_.is_null()) { | 
|  | UMA_HISTOGRAM_LONG_TIMES("DomainReliability.BeaconIntervalGlobal", | 
|  | now - last_routed_beacon_time_); | 
|  | } | 
|  | last_routed_beacon_time_ = now; | 
|  | } | 
|  |  | 
|  | void DomainReliabilityContextManager::SetConfig( | 
|  | const GURL& origin, | 
|  | std::unique_ptr<DomainReliabilityConfig> config, | 
|  | base::TimeDelta max_age) { | 
|  | std::string key = origin.host(); | 
|  |  | 
|  | if (!contexts_.count(key) && !removed_contexts_.count(key)) { | 
|  | LOG(WARNING) << "Ignoring NEL header for unknown origin " << origin.spec() | 
|  | << "."; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (contexts_.count(key)) { | 
|  | // Currently, there is no easy way to change the config of a context, so | 
|  | // updating the config requires recreating the context, which loses | 
|  | // pending beacons and collector backoff state. Therefore, don't do so | 
|  | // needlessly; make sure the config has actually changed before recreating | 
|  | // the context. | 
|  | bool config_same = contexts_[key]->config().Equals(*config); | 
|  | UMA_HISTOGRAM_BOOLEAN("DomainReliability.SetConfigRecreatedContext", | 
|  | !config_same); | 
|  | if (!config_same) { | 
|  | DVLOG(1) << "Ignoring unchanged NEL header for existing origin " | 
|  | << origin.spec() << "."; | 
|  | return; | 
|  | } | 
|  | // TODO(juliatuttle): Make Context accept Config changes. | 
|  | } | 
|  |  | 
|  | DVLOG(1) << "Adding/replacing context for existing origin " << origin.spec() | 
|  | << "."; | 
|  | removed_contexts_.erase(key); | 
|  | config->origin = origin; | 
|  | AddContextForConfig(std::move(config)); | 
|  | } | 
|  |  | 
|  | void DomainReliabilityContextManager::ClearConfig(const GURL& origin) { | 
|  | std::string key = origin.host(); | 
|  |  | 
|  | if (contexts_.count(key)) { | 
|  | DVLOG(1) << "Removing context for existing origin " << origin.spec() << "."; | 
|  | contexts_.erase(key); | 
|  | removed_contexts_.insert(key); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DomainReliabilityContextManager::ClearBeacons( | 
|  | const base::Callback<bool(const GURL&)>& origin_filter) { | 
|  | for (auto& context_entry : contexts_) { | 
|  | if (origin_filter.is_null() || | 
|  | origin_filter.Run(context_entry.second->config().origin)) { | 
|  | context_entry.second->ClearBeacons(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | DomainReliabilityContext* DomainReliabilityContextManager::AddContextForConfig( | 
|  | std::unique_ptr<const DomainReliabilityConfig> config) { | 
|  | std::string key = config->origin.host(); | 
|  | // TODO(juliatuttle): Convert this to actual origin. | 
|  |  | 
|  | std::unique_ptr<DomainReliabilityContext> context = | 
|  | context_factory_->CreateContextForConfig(std::move(config)); | 
|  | DomainReliabilityContext** entry = &contexts_[key]; | 
|  | if (*entry) | 
|  | delete *entry; | 
|  |  | 
|  | *entry = context.release(); | 
|  | return *entry; | 
|  | } | 
|  |  | 
|  | void DomainReliabilityContextManager::RemoveContexts( | 
|  | const base::Callback<bool(const GURL&)>& origin_filter) { | 
|  | for (ContextMap::iterator it = contexts_.begin(); it != contexts_.end(); ) { | 
|  | if (!origin_filter.is_null() && | 
|  | !origin_filter.Run(it->second->config().origin)) { | 
|  | ++it; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | delete it->second; | 
|  | it = contexts_.erase(it); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::unique_ptr<base::Value> DomainReliabilityContextManager::GetWebUIData() | 
|  | const { | 
|  | std::unique_ptr<base::ListValue> contexts_value(new base::ListValue()); | 
|  | for (const auto& context_entry : contexts_) | 
|  | contexts_value->Append(context_entry.second->GetWebUIData()); | 
|  | return std::move(contexts_value); | 
|  | } | 
|  |  | 
|  | DomainReliabilityContext* DomainReliabilityContextManager::GetContextForHost( | 
|  | const std::string& host) { | 
|  | ContextMap::const_iterator context_it; | 
|  |  | 
|  | context_it = contexts_.find(host); | 
|  | if (context_it != contexts_.end()) | 
|  | return context_it->second; | 
|  |  | 
|  | size_t dot_pos = host.find('.'); | 
|  | if (dot_pos == std::string::npos) | 
|  | return nullptr; | 
|  |  | 
|  | // TODO(juliatuttle): Make sure parent is not in PSL before using. | 
|  |  | 
|  | std::string parent_host = host.substr(dot_pos + 1); | 
|  | context_it = contexts_.find(parent_host); | 
|  | if (context_it != contexts_.end() | 
|  | && context_it->second->config().include_subdomains) { | 
|  | return context_it->second; | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | }  // namespace domain_reliability |