| // 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 |