| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/predictors/loading_predictor.h" | 5 #include "chrome/browser/predictors/loading_predictor.h" |
| 6 | 6 |
| 7 #include <vector> |
| 8 |
| 7 #include "base/memory/ptr_util.h" | 9 #include "base/memory/ptr_util.h" |
| 8 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
| 9 #include "chrome/browser/predictors/loading_data_collector.h" | 11 #include "chrome/browser/predictors/loading_data_collector.h" |
| 10 #include "chrome/browser/predictors/loading_stats_collector.h" | 12 #include "chrome/browser/predictors/loading_stats_collector.h" |
| 11 #include "chrome/browser/predictors/resource_prefetch_common.h" | 13 #include "chrome/browser/predictors/resource_prefetch_common.h" |
| 12 #include "chrome/browser/predictors/resource_prefetch_predictor.h" | 14 #include "chrome/browser/predictors/resource_prefetch_predictor.h" |
| 13 | 15 |
| 14 namespace predictors { | 16 namespace predictors { |
| 15 | 17 |
| 16 using URLRequestSummary = ResourcePrefetchPredictor::URLRequestSummary; | 18 using URLRequestSummary = ResourcePrefetchPredictor::URLRequestSummary; |
| 17 | 19 |
| 18 LoadingPredictor::LoadingPredictor(const LoadingPredictorConfig& config, | 20 LoadingPredictor::LoadingPredictor(const LoadingPredictorConfig& config, |
| 19 Profile* profile) | 21 Profile* profile) |
| 20 : config_(config), | 22 : config_(config), |
| 21 profile_(profile), | 23 profile_(profile), |
| 22 resource_prefetch_predictor_( | 24 resource_prefetch_predictor_( |
| 23 base::MakeUnique<ResourcePrefetchPredictor>(config, profile)), | 25 base::MakeUnique<ResourcePrefetchPredictor>(config, profile)), |
| 24 stats_collector_(base::MakeUnique<LoadingStatsCollector>( | 26 stats_collector_(base::MakeUnique<LoadingStatsCollector>( |
| 25 resource_prefetch_predictor_.get(), | 27 resource_prefetch_predictor_.get(), |
| 26 config)), | 28 config)), |
| 27 loading_data_collector_(base::MakeUnique<LoadingDataCollector>( | 29 loading_data_collector_(base::MakeUnique<LoadingDataCollector>( |
| 28 resource_prefetch_predictor())), | 30 resource_prefetch_predictor())), |
| 31 observer_(nullptr), |
| 29 weak_factory_(this) { | 32 weak_factory_(this) { |
| 30 resource_prefetch_predictor_->SetStatsCollector(stats_collector_.get()); | 33 resource_prefetch_predictor_->SetStatsCollector(stats_collector_.get()); |
| 31 } | 34 } |
| 32 | 35 |
| 33 LoadingPredictor::~LoadingPredictor() = default; | 36 LoadingPredictor::~LoadingPredictor() = default; |
| 34 | 37 |
| 35 void LoadingPredictor::PrepareForPageLoad(const GURL& url, HintOrigin origin) { | 38 void LoadingPredictor::PrepareForPageLoad(const GURL& url, HintOrigin origin) { |
| 36 if (active_hints_.find(url) != active_hints_.end()) | 39 if (active_hints_.find(url) != active_hints_.end()) |
| 37 return; | 40 return; |
| 38 ResourcePrefetchPredictor::Prediction prediction; | 41 ResourcePrefetchPredictor::Prediction prediction; |
| 39 if (!resource_prefetch_predictor_->GetPrefetchData(url, &prediction)) | 42 if (!resource_prefetch_predictor_->GetPrefetchData(url, &prediction)) |
| 40 return; | 43 return; |
| 41 | 44 |
| 42 // To report hint durations. | 45 // To report hint durations. |
| 43 active_hints_.emplace(url, base::TimeTicks::Now()); | 46 active_hints_.emplace(url, base::TimeTicks::Now()); |
| 44 | 47 MaybeAddPrefetch(url, prediction, origin); |
| 45 if (config_.IsPrefetchingEnabledForOrigin(profile_, origin)) | |
| 46 resource_prefetch_predictor_->StartPrefetching(url, prediction); | |
| 47 } | 48 } |
| 48 | 49 |
| 49 void LoadingPredictor::CancelPageLoadHint(const GURL& url) { | 50 void LoadingPredictor::CancelPageLoadHint(const GURL& url) { |
| 50 CancelActiveHint(active_hints_.find(url)); | 51 CancelActiveHint(active_hints_.find(url)); |
| 51 } | 52 } |
| 52 | 53 |
| 53 void LoadingPredictor::StartInitialization() { | 54 void LoadingPredictor::StartInitialization() { |
| 54 resource_prefetch_predictor_->StartInitialization(); | 55 resource_prefetch_predictor_->StartInitialization(); |
| 55 } | 56 } |
| 56 | 57 |
| 57 LoadingDataCollector* LoadingPredictor::loading_data_collector() { | 58 LoadingDataCollector* LoadingPredictor::loading_data_collector() { |
| 58 return loading_data_collector_.get(); | 59 return loading_data_collector_.get(); |
| 59 } | 60 } |
| 60 | 61 |
| 61 ResourcePrefetchPredictor* LoadingPredictor::resource_prefetch_predictor() { | 62 ResourcePrefetchPredictor* LoadingPredictor::resource_prefetch_predictor() { |
| 62 return resource_prefetch_predictor_.get(); | 63 return resource_prefetch_predictor_.get(); |
| 63 } | 64 } |
| 64 | 65 |
| 65 void LoadingPredictor::Shutdown() { | 66 void LoadingPredictor::Shutdown() { |
| 66 resource_prefetch_predictor_->Shutdown(); | 67 resource_prefetch_predictor_->Shutdown(); |
| 68 |
| 69 std::vector<std::unique_ptr<ResourcePrefetcher>> prefetchers; |
| 70 for (auto& kv : prefetches_) |
| 71 prefetchers.push_back(std::move(kv.second.first)); |
| 72 |
| 73 // |prefetchers| must be destroyed on the IO thread. |
| 74 content::BrowserThread::PostTask( |
| 75 content::BrowserThread::IO, FROM_HERE, |
| 76 base::BindOnce( |
| 77 [](std::vector<std::unique_ptr<ResourcePrefetcher>> prefetchers) {}, |
| 78 std::move(prefetchers))); |
| 67 } | 79 } |
| 68 | 80 |
| 69 void LoadingPredictor::OnMainFrameRequest(const URLRequestSummary& summary) { | 81 void LoadingPredictor::OnMainFrameRequest(const URLRequestSummary& summary) { |
| 70 DCHECK(summary.resource_type == content::RESOURCE_TYPE_MAIN_FRAME); | 82 DCHECK(summary.resource_type == content::RESOURCE_TYPE_MAIN_FRAME); |
| 71 | 83 |
| 72 const NavigationID& navigation_id = summary.navigation_id; | 84 const NavigationID& navigation_id = summary.navigation_id; |
| 73 CleanupAbandonedHintsAndNavigations(navigation_id); | 85 CleanupAbandonedHintsAndNavigations(navigation_id); |
| 74 active_navigations_.emplace(navigation_id, navigation_id.main_frame_url); | 86 active_navigations_.emplace(navigation_id, navigation_id.main_frame_url); |
| 75 PrepareForPageLoad(navigation_id.main_frame_url, HintOrigin::NAVIGATION); | 87 PrepareForPageLoad(navigation_id.main_frame_url, HintOrigin::NAVIGATION); |
| 76 } | 88 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 96 auto it = active_navigations_.find(navigation_id); | 108 auto it = active_navigations_.find(navigation_id); |
| 97 if (it != active_navigations_.end()) { | 109 if (it != active_navigations_.end()) { |
| 98 const GURL& initial_url = it->second; | 110 const GURL& initial_url = it->second; |
| 99 CancelPageLoadHint(initial_url); | 111 CancelPageLoadHint(initial_url); |
| 100 active_navigations_.erase(it); | 112 active_navigations_.erase(it); |
| 101 } else { | 113 } else { |
| 102 CancelPageLoadHint(navigation_id.main_frame_url); | 114 CancelPageLoadHint(navigation_id.main_frame_url); |
| 103 } | 115 } |
| 104 } | 116 } |
| 105 | 117 |
| 118 void LoadingPredictor::SetObserverForTesting(TestLoadingObserver* observer) { |
| 119 observer_ = observer; |
| 120 } |
| 121 |
| 106 std::map<GURL, base::TimeTicks>::iterator LoadingPredictor::CancelActiveHint( | 122 std::map<GURL, base::TimeTicks>::iterator LoadingPredictor::CancelActiveHint( |
| 107 std::map<GURL, base::TimeTicks>::iterator hint_it) { | 123 std::map<GURL, base::TimeTicks>::iterator hint_it) { |
| 108 if (hint_it == active_hints_.end()) | 124 if (hint_it == active_hints_.end()) |
| 109 return hint_it; | 125 return hint_it; |
| 110 | 126 |
| 111 const GURL& url = hint_it->first; | 127 const GURL& url = hint_it->first; |
| 112 resource_prefetch_predictor_->StopPrefetching(url); | 128 MaybeRemovePrefetch(url); |
| 113 | 129 |
| 114 UMA_HISTOGRAM_TIMES( | 130 UMA_HISTOGRAM_TIMES( |
| 115 internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, | 131 internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, |
| 116 base::TimeTicks::Now() - hint_it->second); | 132 base::TimeTicks::Now() - hint_it->second); |
| 117 return active_hints_.erase(hint_it); | 133 return active_hints_.erase(hint_it); |
| 118 } | 134 } |
| 119 | 135 |
| 120 void LoadingPredictor::CleanupAbandonedHintsAndNavigations( | 136 void LoadingPredictor::CleanupAbandonedHintsAndNavigations( |
| 121 const NavigationID& navigation_id) { | 137 const NavigationID& navigation_id) { |
| 122 base::TimeTicks time_now = base::TimeTicks::Now(); | 138 base::TimeTicks time_now = base::TimeTicks::Now(); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 142 (time_now - it->first.creation_time > max_navigation_age)) { | 158 (time_now - it->first.creation_time > max_navigation_age)) { |
| 143 const GURL& initial_url = it->second; | 159 const GURL& initial_url = it->second; |
| 144 CancelActiveHint(active_hints_.find(initial_url)); | 160 CancelActiveHint(active_hints_.find(initial_url)); |
| 145 it = active_navigations_.erase(it); | 161 it = active_navigations_.erase(it); |
| 146 } else { | 162 } else { |
| 147 ++it; | 163 ++it; |
| 148 } | 164 } |
| 149 } | 165 } |
| 150 } | 166 } |
| 151 | 167 |
| 168 void LoadingPredictor::MaybeAddPrefetch( |
| 169 const GURL& url, |
| 170 const ResourcePrefetchPredictor::Prediction& prediction, |
| 171 HintOrigin origin) { |
| 172 if (!config_.IsPrefetchingEnabledForOrigin(profile_, origin)) |
| 173 return; |
| 174 std::string host = url.host(); |
| 175 if (prefetches_.find(host) != prefetches_.end()) |
| 176 return; |
| 177 |
| 178 auto prefetcher = base::MakeUnique<ResourcePrefetcher>( |
| 179 GetWeakPtr(), profile_->GetRequestContext(), |
| 180 config_.max_prefetches_inflight_per_navigation, |
| 181 config_.max_prefetches_inflight_per_host_per_navigation, url, |
| 182 prediction.subresource_urls); |
| 183 // base::Unretained(prefetcher.get()) is fine, as |prefetcher| is always |
| 184 // destructed on the IO thread, and the destruction task is posted from the |
| 185 // UI thread, after the current task. Since the IO thread is FIFO, then |
| 186 // ResourcePrefetcher::Start() will be called before ~ResourcePrefetcher. |
| 187 content::BrowserThread::PostTask( |
| 188 content::BrowserThread::IO, FROM_HERE, |
| 189 base::BindOnce(&ResourcePrefetcher::Start, |
| 190 base::Unretained(prefetcher.get()))); |
| 191 prefetches_.emplace(host, std::make_pair(std::move(prefetcher), false)); |
| 192 if (observer_) |
| 193 observer_->OnPrefetchingStarted(url); |
| 194 } |
| 195 |
| 196 void LoadingPredictor::MaybeRemovePrefetch(const GURL& url) { |
| 197 std::string host = url.host(); |
| 198 auto it = prefetches_.find(host); |
| 199 if (it == prefetches_.end()) |
| 200 return; |
| 201 |
| 202 auto& prefetcher_and_deleted = it->second; |
| 203 if (prefetcher_and_deleted.second) |
| 204 return; |
| 205 |
| 206 // Avoid to stop the same prefetcher twice, which can happen for instance with |
| 207 // multiple prediction origins for the same |url|. Prefetches are |
| 208 // de-duplicated, not their cancellation. |
| 209 prefetcher_and_deleted.second = true; |
| 210 content::BrowserThread::PostTask( |
| 211 content::BrowserThread::IO, FROM_HERE, |
| 212 base::BindOnce(&ResourcePrefetcher::Stop, |
| 213 base::Unretained(prefetcher_and_deleted.first.get()))); |
| 214 if (observer_) |
| 215 observer_->OnPrefetchingStopped(url); |
| 216 } |
| 217 |
| 218 void LoadingPredictor::ResourcePrefetcherFinished( |
| 219 ResourcePrefetcher* prefetcher, |
| 220 std::unique_ptr<ResourcePrefetcher::PrefetcherStats> stats) { |
| 221 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 222 std::string host = prefetcher->main_frame_url().host(); |
| 223 auto it = prefetches_.find(host); |
| 224 // Can happen after Shutdown() has been called, since it purges |prefetches_|. |
| 225 if (it == prefetches_.end()) |
| 226 return; |
| 227 |
| 228 DCHECK(it->second.first.get() == prefetcher); |
| 229 stats_collector_->RecordPrefetcherStats(std::move(stats)); |
| 230 |
| 231 if (observer_) |
| 232 observer_->OnPrefetchingFinished(prefetcher->main_frame_url()); |
| 233 |
| 234 // ResourcePrefetcher must be destroyed on the IO thread. |
| 235 content::BrowserThread::PostTask( |
| 236 content::BrowserThread::IO, FROM_HERE, |
| 237 base::BindOnce([](std::unique_ptr<ResourcePrefetcher>) {}, |
| 238 std::move(it->second.first))); |
| 239 |
| 240 prefetches_.erase(it); |
| 241 } |
| 242 |
| 243 TestLoadingObserver::~TestLoadingObserver() { |
| 244 predictor_->SetObserverForTesting(nullptr); |
| 245 } |
| 246 |
| 247 TestLoadingObserver::TestLoadingObserver(LoadingPredictor* predictor) |
| 248 : predictor_(predictor) { |
| 249 predictor_->SetObserverForTesting(this); |
| 250 } |
| 251 |
| 152 } // namespace predictors | 252 } // namespace predictors |
| OLD | NEW |