Chromium Code Reviews| 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() { |
|
alexilin
2017/07/03 14:55:01
Could LoadingPredictor be destructed without a cal
Benoit L
2017/07/03 15:43:14
No, it can't.
Unless we crashed, in which case no
| |
| 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. | |
|
alexilin
2017/07/03 14:55:01
nit:
s/prefetchers/|prefetchers|/
Benoit L
2017/07/03 15:43:14
Done.
| |
| 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); | |
|
alexilin
2017/07/03 14:55:01
std::move(prediction.subresource_urls)
|prediction
Benoit L
2017/07/03 15:43:14
Thanks!
Done.
alexilin
2017/07/03 15:57:14
I'm very suspicious about moving an object (or its
| |
| 183 content::BrowserThread::PostTask( | |
| 184 content::BrowserThread::IO, FROM_HERE, | |
| 185 base::BindOnce(&ResourcePrefetcher::Start, | |
| 186 base::Unretained(prefetcher.get()))); | |
|
alexilin
2017/07/03 14:55:01
Can you add a comment explaining that passing unre
Benoit L
2017/07/03 15:43:14
Done.
| |
| 187 prefetches_.emplace(host, std::make_pair(std::move(prefetcher), false)); | |
| 188 if (observer_) | |
| 189 observer_->OnPrefetchingStarted(url); | |
| 190 } | |
| 191 | |
| 192 void LoadingPredictor::MaybeRemovePrefetch(const GURL& url) { | |
| 193 std::string host = url.host(); | |
| 194 auto it = prefetches_.find(host); | |
| 195 if (it == prefetches_.end()) | |
| 196 return; | |
| 197 | |
| 198 auto& prefetcher_and_deleted = it->second; | |
| 199 if (prefetcher_and_deleted.second) | |
| 200 return; | |
| 201 | |
| 202 // Avoid duplicates. | |
|
alexilin
2017/07/03 14:55:01
Can you make this comment more detailed? Something
Benoit L
2017/07/03 15:43:14
Done.
| |
| 203 prefetcher_and_deleted.second = true; | |
| 204 content::BrowserThread::PostTask( | |
| 205 content::BrowserThread::IO, FROM_HERE, | |
| 206 base::BindOnce(&ResourcePrefetcher::Stop, | |
| 207 base::Unretained(prefetcher_and_deleted.first.get()))); | |
| 208 if (observer_) | |
| 209 observer_->OnPrefetchingStopped(url); | |
| 210 } | |
| 211 | |
| 212 void LoadingPredictor::ResourcePrefetcherFinished( | |
| 213 ResourcePrefetcher* prefetcher, | |
|
alexilin
2017/07/03 14:55:01
nit:
Do we really need to pass a pointer to Resour
Benoit L
2017/07/03 15:43:14
The lifetime of a ResourcePrefetcher is entirely c
| |
| 214 std::unique_ptr<ResourcePrefetcher::PrefetcherStats> stats) { | |
| 215 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 216 std::string host = prefetcher->main_frame_url().host(); | |
| 217 auto it = prefetches_.find(host); | |
| 218 // Can happen after Shutdown() has been called, since it purges |prefetches_|. | |
| 219 if (it == prefetches_.end()) | |
| 220 return; | |
| 221 | |
| 222 DCHECK(it->second.first.get() == prefetcher); | |
| 223 stats_collector_->RecordPrefetcherStats(std::move(stats)); | |
| 224 | |
| 225 if (observer_) | |
| 226 observer_->OnPrefetchingFinished(prefetcher->main_frame_url()); | |
| 227 | |
| 228 // ResourcePrefetcher must be destroyed on the IO thread. | |
| 229 content::BrowserThread::PostTask( | |
| 230 content::BrowserThread::IO, FROM_HERE, | |
| 231 base::BindOnce([](std::unique_ptr<ResourcePrefetcher>) {}, | |
| 232 base::Passed(std::move(it->second.first)))); | |
|
alexilin
2017/07/03 14:55:01
Just curious, do you know whether we really need t
Benoit L
2017/07/03 15:43:14
Thanks for letting me know!
Done.
| |
| 233 | |
| 234 prefetches_.erase(it); | |
| 235 } | |
| 236 | |
| 237 TestLoadingObserver::~TestLoadingObserver() { | |
| 238 predictor_->SetObserverForTesting(nullptr); | |
| 239 } | |
| 240 | |
| 241 TestLoadingObserver::TestLoadingObserver(LoadingPredictor* predictor) | |
| 242 : predictor_(predictor) { | |
| 243 predictor_->SetObserverForTesting(this); | |
| 244 } | |
| 245 | |
| 152 } // namespace predictors | 246 } // namespace predictors |
| OLD | NEW |