| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/resource_prefetch_predictor.h" | 5 #include "chrome/browser/predictors/resource_prefetch_predictor.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| 11 #include "base/macros.h" | 11 #include "base/macros.h" |
| 12 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 13 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
| 14 #include "base/rand_util.h" | 14 #include "base/rand_util.h" |
| 15 #include "base/time/time.h" | 15 #include "base/time/time.h" |
| 16 #include "base/trace_event/trace_event.h" | 16 #include "base/trace_event/trace_event.h" |
| 17 #include "chrome/browser/history/history_service_factory.h" | 17 #include "chrome/browser/history/history_service_factory.h" |
| 18 #include "chrome/browser/predictors/loading_data_collector.h" |
| 18 #include "chrome/browser/predictors/loading_stats_collector.h" | 19 #include "chrome/browser/predictors/loading_stats_collector.h" |
| 19 #include "chrome/browser/predictors/predictor_database.h" | 20 #include "chrome/browser/predictors/predictor_database.h" |
| 20 #include "chrome/browser/predictors/predictor_database_factory.h" | 21 #include "chrome/browser/predictors/predictor_database_factory.h" |
| 21 #include "chrome/browser/predictors/resource_prefetcher_manager.h" | 22 #include "chrome/browser/predictors/resource_prefetcher_manager.h" |
| 22 #include "chrome/browser/profiles/profile.h" | 23 #include "chrome/browser/profiles/profile.h" |
| 23 #include "components/history/core/browser/history_database.h" | 24 #include "components/history/core/browser/history_database.h" |
| 24 #include "components/history/core/browser/history_service.h" | 25 #include "components/history/core/browser/history_service.h" |
| 25 #include "components/history/core/browser/url_utils.h" | 26 #include "components/history/core/browser/url_utils.h" |
| 26 #include "components/mime_util/mime_util.h" | 27 #include "components/mime_util/mime_util.h" |
| 27 #include "components/precache/core/precache_manifest_util.h" | 28 #include "components/precache/core/precache_manifest_util.h" |
| 28 #include "content/public/browser/browser_thread.h" | 29 #include "content/public/browser/browser_thread.h" |
| 29 #include "content/public/browser/resource_request_info.h" | 30 #include "content/public/browser/resource_request_info.h" |
| 30 #include "content/public/browser/web_contents.h" | 31 #include "content/public/browser/web_contents.h" |
| 31 #include "net/http/http_response_headers.h" | |
| 32 #include "net/url_request/url_request.h" | |
| 33 | 32 |
| 34 using content::BrowserThread; | 33 using content::BrowserThread; |
| 35 | 34 |
| 36 namespace predictors { | 35 namespace predictors { |
| 37 | 36 |
| 38 namespace { | 37 namespace { |
| 39 | 38 |
| 40 // Sorted by decreasing likelihood according to HTTP archive. | |
| 41 const char* kFontMimeTypes[] = {"font/woff2", | |
| 42 "application/x-font-woff", | |
| 43 "application/font-woff", | |
| 44 "application/font-woff2", | |
| 45 "font/x-woff", | |
| 46 "application/x-font-ttf", | |
| 47 "font/woff", | |
| 48 "font/ttf", | |
| 49 "application/x-font-otf", | |
| 50 "x-font/woff", | |
| 51 "application/font-sfnt", | |
| 52 "application/font-ttf"}; | |
| 53 | |
| 54 const size_t kMaxManifestByteSize = 16 * 1024; | 39 const size_t kMaxManifestByteSize = 16 * 1024; |
| 55 const size_t kNumSampleHosts = 50; | 40 const size_t kNumSampleHosts = 50; |
| 56 const size_t kReportReadinessThreshold = 50; | 41 const size_t kReportReadinessThreshold = 50; |
| 57 const float kMinOriginConfidenceToTriggerPreconnect = 0.75; | 42 const float kMinOriginConfidenceToTriggerPreconnect = 0.75; |
| 58 const float kMinOriginConfidenceToTriggerPreresolve = 0.2; | 43 const float kMinOriginConfidenceToTriggerPreresolve = 0.2; |
| 59 | 44 |
| 60 // For reporting events of interest that are not tied to any navigation. | 45 // For reporting events of interest that are not tied to any navigation. |
| 61 enum ReportingEvent { | 46 enum ReportingEvent { |
| 62 REPORTING_EVENT_ALL_HISTORY_CLEARED = 0, | 47 REPORTING_EVENT_ALL_HISTORY_CLEARED = 0, |
| 63 REPORTING_EVENT_PARTIAL_HISTORY_CLEARED = 1, | 48 REPORTING_EVENT_PARTIAL_HISTORY_CLEARED = 1, |
| 64 REPORTING_EVENT_COUNT = 2 | 49 REPORTING_EVENT_COUNT = 2 |
| 65 }; | 50 }; |
| 66 | 51 |
| 67 float ComputeRedirectConfidence(const predictors::RedirectStat& redirect) { | 52 float ComputeRedirectConfidence(const predictors::RedirectStat& redirect) { |
| 68 return (redirect.number_of_hits() + 0.0) / | 53 return (redirect.number_of_hits() + 0.0) / |
| 69 (redirect.number_of_hits() + redirect.number_of_misses()); | 54 (redirect.number_of_hits() + redirect.number_of_misses()); |
| 70 } | 55 } |
| 71 | 56 |
| 72 void UpdateOrAddToOrigins( | |
| 73 std::map<GURL, ResourcePrefetchPredictor::OriginRequestSummary>* summaries, | |
| 74 const ResourcePrefetchPredictor::URLRequestSummary& request_summary) { | |
| 75 const GURL& request_url = request_summary.request_url; | |
| 76 DCHECK(request_url.is_valid()); | |
| 77 if (!request_url.is_valid()) | |
| 78 return; | |
| 79 | |
| 80 GURL origin = request_url.GetOrigin(); | |
| 81 auto it = summaries->find(origin); | |
| 82 if (it == summaries->end()) { | |
| 83 ResourcePrefetchPredictor::OriginRequestSummary summary; | |
| 84 summary.origin = origin; | |
| 85 summary.first_occurrence = summaries->size(); | |
| 86 it = summaries->insert({origin, summary}).first; | |
| 87 } | |
| 88 | |
| 89 it->second.always_access_network |= | |
| 90 request_summary.always_revalidate || request_summary.is_no_store; | |
| 91 it->second.accessed_network |= request_summary.network_accessed; | |
| 92 } | |
| 93 | |
| 94 void InitializeOriginStatFromOriginRequestSummary( | 57 void InitializeOriginStatFromOriginRequestSummary( |
| 95 OriginStat* origin, | 58 OriginStat* origin, |
| 96 const ResourcePrefetchPredictor::OriginRequestSummary& summary) { | 59 const OriginRequestSummary& summary) { |
| 97 origin->set_origin(summary.origin.spec()); | 60 origin->set_origin(summary.origin.spec()); |
| 98 origin->set_number_of_hits(1); | 61 origin->set_number_of_hits(1); |
| 99 origin->set_average_position(summary.first_occurrence + 1); | 62 origin->set_average_position(summary.first_occurrence + 1); |
| 100 origin->set_always_access_network(summary.always_access_network); | 63 origin->set_always_access_network(summary.always_access_network); |
| 101 origin->set_accessed_network(summary.accessed_network); | 64 origin->set_accessed_network(summary.accessed_network); |
| 102 } | 65 } |
| 103 | 66 |
| 104 bool IsManifestTooOld(const precache::PrecacheManifest& manifest) { | 67 bool IsManifestTooOld(const precache::PrecacheManifest& manifest) { |
| 105 const base::TimeDelta kMaxManifestAge = base::TimeDelta::FromDays(5); | 68 const base::TimeDelta kMaxManifestAge = base::TimeDelta::FromDays(5); |
| 106 return base::Time::Now() - base::Time::FromDoubleT(manifest.id().id()) > | 69 return base::Time::Now() - base::Time::FromDoubleT(manifest.id().id()) > |
| 107 kMaxManifestAge; | 70 kMaxManifestAge; |
| 108 } | 71 } |
| 109 | 72 |
| 110 // Used to fetch the visit count for a URL from the History database. | 73 // Used to fetch the visit count for a URL from the History database. |
| 111 class GetUrlVisitCountTask : public history::HistoryDBTask { | 74 class GetUrlVisitCountTask : public history::HistoryDBTask { |
| 112 public: | 75 public: |
| 113 using URLRequestSummary = ResourcePrefetchPredictor::URLRequestSummary; | |
| 114 using PageRequestSummary = ResourcePrefetchPredictor::PageRequestSummary; | |
| 115 typedef base::OnceCallback<void(size_t, // URL visit count. | 76 typedef base::OnceCallback<void(size_t, // URL visit count. |
| 116 const PageRequestSummary&)> | 77 const PageRequestSummary&)> |
| 117 VisitInfoCallback; | 78 VisitInfoCallback; |
| 118 | 79 |
| 119 GetUrlVisitCountTask(std::unique_ptr<PageRequestSummary> summary, | 80 GetUrlVisitCountTask(std::unique_ptr<PageRequestSummary> summary, |
| 120 VisitInfoCallback callback); | 81 VisitInfoCallback callback); |
| 121 | 82 |
| 122 bool RunOnDBThread(history::HistoryBackend* backend, | 83 bool RunOnDBThread(history::HistoryBackend* backend, |
| 123 history::HistoryDatabase* db) override; | 84 history::HistoryDatabase* db) override; |
| 124 | 85 |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 } // namespace internal | 145 } // namespace internal |
| 185 | 146 |
| 186 PreconnectPrediction::PreconnectPrediction() = default; | 147 PreconnectPrediction::PreconnectPrediction() = default; |
| 187 PreconnectPrediction::PreconnectPrediction( | 148 PreconnectPrediction::PreconnectPrediction( |
| 188 const PreconnectPrediction& prediction) = default; | 149 const PreconnectPrediction& prediction) = default; |
| 189 PreconnectPrediction::~PreconnectPrediction() = default; | 150 PreconnectPrediction::~PreconnectPrediction() = default; |
| 190 | 151 |
| 191 //////////////////////////////////////////////////////////////////////////////// | 152 //////////////////////////////////////////////////////////////////////////////// |
| 192 // ResourcePrefetchPredictor static functions. | 153 // ResourcePrefetchPredictor static functions. |
| 193 | 154 |
| 194 // static | |
| 195 content::ResourceType ResourcePrefetchPredictor::GetResourceType( | |
| 196 content::ResourceType resource_type, | |
| 197 const std::string& mime_type) { | |
| 198 // Restricts content::RESOURCE_TYPE_{PREFETCH,SUB_RESOURCE,XHR} to a small set | |
| 199 // of mime types, because these resource types don't communicate how the | |
| 200 // resources will be used. | |
| 201 if (resource_type == content::RESOURCE_TYPE_PREFETCH || | |
| 202 resource_type == content::RESOURCE_TYPE_SUB_RESOURCE || | |
| 203 resource_type == content::RESOURCE_TYPE_XHR) { | |
| 204 return GetResourceTypeFromMimeType(mime_type, | |
| 205 content::RESOURCE_TYPE_LAST_TYPE); | |
| 206 } | |
| 207 return resource_type; | |
| 208 } | |
| 209 | |
| 210 // static | |
| 211 bool ResourcePrefetchPredictor::IsNoStore(const net::URLRequest& response) { | |
| 212 if (response.was_cached()) | |
| 213 return false; | |
| 214 | |
| 215 const net::HttpResponseInfo& response_info = response.response_info(); | |
| 216 if (!response_info.headers.get()) | |
| 217 return false; | |
| 218 return response_info.headers->HasHeaderValue("cache-control", "no-store"); | |
| 219 } | |
| 220 | |
| 221 // static | |
| 222 content::ResourceType ResourcePrefetchPredictor::GetResourceTypeFromMimeType( | |
| 223 const std::string& mime_type, | |
| 224 content::ResourceType fallback) { | |
| 225 if (mime_type.empty()) { | |
| 226 return fallback; | |
| 227 } else if (mime_util::IsSupportedImageMimeType(mime_type)) { | |
| 228 return content::RESOURCE_TYPE_IMAGE; | |
| 229 } else if (mime_util::IsSupportedJavascriptMimeType(mime_type)) { | |
| 230 return content::RESOURCE_TYPE_SCRIPT; | |
| 231 } else if (net::MatchesMimeType("text/css", mime_type)) { | |
| 232 return content::RESOURCE_TYPE_STYLESHEET; | |
| 233 } else { | |
| 234 bool found = | |
| 235 std::any_of(std::begin(kFontMimeTypes), std::end(kFontMimeTypes), | |
| 236 [&mime_type](const std::string& mime) { | |
| 237 return net::MatchesMimeType(mime, mime_type); | |
| 238 }); | |
| 239 if (found) | |
| 240 return content::RESOURCE_TYPE_FONT_RESOURCE; | |
| 241 } | |
| 242 return fallback; | |
| 243 } | |
| 244 | |
| 245 bool ResourcePrefetchPredictor::GetRedirectEndpoint( | 155 bool ResourcePrefetchPredictor::GetRedirectEndpoint( |
| 246 const std::string& entry_point, | 156 const std::string& entry_point, |
| 247 const RedirectDataMap& redirect_data, | 157 const RedirectDataMap& redirect_data, |
| 248 std::string* redirect_endpoint) const { | 158 std::string* redirect_endpoint) const { |
| 249 DCHECK(redirect_endpoint); | 159 DCHECK(redirect_endpoint); |
| 250 | 160 |
| 251 RedirectData data; | 161 RedirectData data; |
| 252 bool exists = redirect_data.TryGetData(entry_point, &data); | 162 bool exists = redirect_data.TryGetData(entry_point, &data); |
| 253 if (!exists) { | 163 if (!exists) { |
| 254 // Fallback to fetching URLs based on the incoming URL/host. By default | 164 // Fallback to fetching URLs based on the incoming URL/host. By default |
| (...skipping 26 matching lines...) Expand all Loading... |
| 281 return false; | 191 return false; |
| 282 } | 192 } |
| 283 | 193 |
| 284 *redirect_endpoint = redirect.url(); | 194 *redirect_endpoint = redirect.url(); |
| 285 return true; | 195 return true; |
| 286 } | 196 } |
| 287 | 197 |
| 288 //////////////////////////////////////////////////////////////////////////////// | 198 //////////////////////////////////////////////////////////////////////////////// |
| 289 // ResourcePrefetchPredictor nested types. | 199 // ResourcePrefetchPredictor nested types. |
| 290 | 200 |
| 291 ResourcePrefetchPredictor::OriginRequestSummary::OriginRequestSummary() | |
| 292 : origin(), | |
| 293 always_access_network(false), | |
| 294 accessed_network(false), | |
| 295 first_occurrence(0) {} | |
| 296 | |
| 297 ResourcePrefetchPredictor::OriginRequestSummary::OriginRequestSummary( | |
| 298 const OriginRequestSummary& other) = default; | |
| 299 | |
| 300 ResourcePrefetchPredictor::OriginRequestSummary::~OriginRequestSummary() {} | |
| 301 | |
| 302 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary() | |
| 303 : resource_type(content::RESOURCE_TYPE_LAST_TYPE), | |
| 304 priority(net::IDLE), | |
| 305 before_first_contentful_paint(false), | |
| 306 was_cached(false), | |
| 307 has_validators(false), | |
| 308 always_revalidate(false), | |
| 309 is_no_store(false), | |
| 310 network_accessed(false) {} | |
| 311 | |
| 312 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary( | |
| 313 const URLRequestSummary& other) = default; | |
| 314 | |
| 315 ResourcePrefetchPredictor::URLRequestSummary::~URLRequestSummary() { | |
| 316 } | |
| 317 | |
| 318 // static | |
| 319 bool ResourcePrefetchPredictor::URLRequestSummary::SummarizeResponse( | |
| 320 const net::URLRequest& request, | |
| 321 URLRequestSummary* summary) { | |
| 322 const content::ResourceRequestInfo* request_info = | |
| 323 content::ResourceRequestInfo::ForRequest(&request); | |
| 324 if (!request_info) | |
| 325 return false; | |
| 326 | |
| 327 // This method is called when the response is started, so this field reflects | |
| 328 // the time at which the response began, not when it finished, as would | |
| 329 // arguably be ideal. This means if firstContentfulPaint happens after the | |
| 330 // response has started, but before it's finished, we will erroneously mark | |
| 331 // the resource as having been loaded before firstContentfulPaint. This is | |
| 332 // a rare and insignificant enough occurrence that we opt to record the time | |
| 333 // here for the sake of simplicity. | |
| 334 summary->response_time = base::TimeTicks::Now(); | |
| 335 summary->resource_url = request.original_url(); | |
| 336 summary->request_url = request.url(); | |
| 337 content::ResourceType resource_type_from_request = | |
| 338 request_info->GetResourceType(); | |
| 339 summary->priority = request.priority(); | |
| 340 request.GetMimeType(&summary->mime_type); | |
| 341 summary->was_cached = request.was_cached(); | |
| 342 summary->resource_type = | |
| 343 GetResourceType(resource_type_from_request, summary->mime_type); | |
| 344 | |
| 345 scoped_refptr<net::HttpResponseHeaders> headers = | |
| 346 request.response_info().headers; | |
| 347 if (headers.get()) { | |
| 348 summary->has_validators = headers->HasValidators(); | |
| 349 // RFC 2616, section 14.9. | |
| 350 summary->always_revalidate = | |
| 351 headers->HasHeaderValue("cache-control", "no-cache") || | |
| 352 headers->HasHeaderValue("pragma", "no-cache") || | |
| 353 headers->HasHeaderValue("vary", "*"); | |
| 354 summary->is_no_store = IsNoStore(request); | |
| 355 } | |
| 356 summary->network_accessed = request.response_info().network_accessed; | |
| 357 return true; | |
| 358 } | |
| 359 | |
| 360 ResourcePrefetchPredictor::PageRequestSummary::PageRequestSummary( | |
| 361 const GURL& i_main_frame_url) | |
| 362 : main_frame_url(i_main_frame_url), | |
| 363 initial_url(i_main_frame_url), | |
| 364 first_contentful_paint(base::TimeTicks::Max()) {} | |
| 365 | |
| 366 ResourcePrefetchPredictor::PageRequestSummary::PageRequestSummary( | |
| 367 const PageRequestSummary& other) = default; | |
| 368 | |
| 369 ResourcePrefetchPredictor::PageRequestSummary::~PageRequestSummary() {} | |
| 370 | |
| 371 ResourcePrefetchPredictor::Prediction::Prediction() = default; | 201 ResourcePrefetchPredictor::Prediction::Prediction() = default; |
| 372 | 202 |
| 373 ResourcePrefetchPredictor::Prediction::Prediction( | 203 ResourcePrefetchPredictor::Prediction::Prediction( |
| 374 const ResourcePrefetchPredictor::Prediction& other) = default; | 204 const ResourcePrefetchPredictor::Prediction& other) = default; |
| 375 | 205 |
| 376 ResourcePrefetchPredictor::Prediction::~Prediction() = default; | 206 ResourcePrefetchPredictor::Prediction::~Prediction() = default; |
| 377 | 207 |
| 378 //////////////////////////////////////////////////////////////////////////////// | 208 //////////////////////////////////////////////////////////////////////////////// |
| 379 // ResourcePrefetchPredictor. | 209 // ResourcePrefetchPredictor. |
| 380 | 210 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 428 auto reply = base::BindOnce( | 258 auto reply = base::BindOnce( |
| 429 &ResourcePrefetchPredictor::CreateCaches, weak_factory_.GetWeakPtr(), | 259 &ResourcePrefetchPredictor::CreateCaches, weak_factory_.GetWeakPtr(), |
| 430 std::move(url_resource_data), std::move(host_resource_data), | 260 std::move(url_resource_data), std::move(host_resource_data), |
| 431 std::move(url_redirect_data), std::move(host_redirect_data), | 261 std::move(url_redirect_data), std::move(host_redirect_data), |
| 432 std::move(manifest_data), std::move(origin_data)); | 262 std::move(manifest_data), std::move(origin_data)); |
| 433 | 263 |
| 434 tables_->GetTaskRunner()->PostTaskAndReply(FROM_HERE, std::move(task), | 264 tables_->GetTaskRunner()->PostTaskAndReply(FROM_HERE, std::move(task), |
| 435 std::move(reply)); | 265 std::move(reply)); |
| 436 } | 266 } |
| 437 | 267 |
| 438 void ResourcePrefetchPredictor::RecordURLRequest( | |
| 439 const URLRequestSummary& request) { | |
| 440 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 441 if (initialization_state_ != INITIALIZED) | |
| 442 return; | |
| 443 | |
| 444 DCHECK_EQ(request.resource_type, content::RESOURCE_TYPE_MAIN_FRAME); | |
| 445 OnMainFrameRequest(request); | |
| 446 } | |
| 447 | |
| 448 void ResourcePrefetchPredictor::RecordURLResponse( | |
| 449 const URLRequestSummary& response) { | |
| 450 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 451 if (initialization_state_ != INITIALIZED) | |
| 452 return; | |
| 453 | |
| 454 if (response.resource_type != content::RESOURCE_TYPE_MAIN_FRAME) | |
| 455 OnSubresourceResponse(response); | |
| 456 } | |
| 457 | |
| 458 void ResourcePrefetchPredictor::RecordURLRedirect( | |
| 459 const URLRequestSummary& response) { | |
| 460 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 461 if (initialization_state_ != INITIALIZED) | |
| 462 return; | |
| 463 | |
| 464 if (response.resource_type == content::RESOURCE_TYPE_MAIN_FRAME) | |
| 465 OnMainFrameRedirect(response); | |
| 466 else | |
| 467 OnSubresourceRedirect(response); | |
| 468 } | |
| 469 | |
| 470 void ResourcePrefetchPredictor::RecordMainFrameLoadComplete( | |
| 471 const NavigationID& navigation_id) { | |
| 472 switch (initialization_state_) { | |
| 473 case NOT_INITIALIZED: | |
| 474 StartInitialization(); | |
| 475 break; | |
| 476 case INITIALIZING: | |
| 477 break; | |
| 478 case INITIALIZED: | |
| 479 // WebContents can return an empty URL if the navigation entry | |
| 480 // corresponding to the navigation has not been created yet. | |
| 481 if (!navigation_id.main_frame_url.is_empty()) | |
| 482 OnNavigationComplete(navigation_id); | |
| 483 break; | |
| 484 default: | |
| 485 NOTREACHED() << "Unexpected initialization_state_: " | |
| 486 << initialization_state_; | |
| 487 } | |
| 488 } | |
| 489 | |
| 490 void ResourcePrefetchPredictor::RecordFirstContentfulPaint( | |
| 491 const NavigationID& navigation_id, | |
| 492 const base::TimeTicks& first_contentful_paint) { | |
| 493 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 494 if (initialization_state_ != INITIALIZED) | |
| 495 return; | |
| 496 | |
| 497 NavigationMap::iterator nav_it = inflight_navigations_.find(navigation_id); | |
| 498 if (nav_it != inflight_navigations_.end()) | |
| 499 nav_it->second->first_contentful_paint = first_contentful_paint; | |
| 500 } | |
| 501 | |
| 502 void ResourcePrefetchPredictor::OnPrefetchingFinished( | 268 void ResourcePrefetchPredictor::OnPrefetchingFinished( |
| 503 const GURL& main_frame_url, | 269 const GURL& main_frame_url, |
| 504 std::unique_ptr<ResourcePrefetcher::PrefetcherStats> stats) { | 270 std::unique_ptr<ResourcePrefetcher::PrefetcherStats> stats) { |
| 505 if (observer_) | 271 if (observer_) |
| 506 observer_->OnPrefetchingFinished(main_frame_url); | 272 observer_->OnPrefetchingFinished(main_frame_url); |
| 507 | 273 |
| 508 if (stats_collector_) | 274 if (stats_collector_) |
| 509 stats_collector_->RecordPrefetcherStats(std::move(stats)); | 275 stats_collector_->RecordPrefetcherStats(std::move(stats)); |
| 510 } | 276 } |
| 511 | 277 |
| (...skipping 21 matching lines...) Expand all Loading... |
| 533 } | 299 } |
| 534 | 300 |
| 535 void ResourcePrefetchPredictor::Shutdown() { | 301 void ResourcePrefetchPredictor::Shutdown() { |
| 536 if (prefetch_manager_.get()) { | 302 if (prefetch_manager_.get()) { |
| 537 prefetch_manager_->ShutdownOnUIThread(); | 303 prefetch_manager_->ShutdownOnUIThread(); |
| 538 prefetch_manager_ = nullptr; | 304 prefetch_manager_ = nullptr; |
| 539 } | 305 } |
| 540 history_service_observer_.RemoveAll(); | 306 history_service_observer_.RemoveAll(); |
| 541 } | 307 } |
| 542 | 308 |
| 543 void ResourcePrefetchPredictor::OnMainFrameRequest( | 309 void ResourcePrefetchPredictor::RecordPageRequestSummary( |
| 544 const URLRequestSummary& request) { | 310 std::unique_ptr<PageRequestSummary> summary) { |
| 545 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 311 // Make sure initialization is done or start initialization if necessary. |
| 546 DCHECK_EQ(INITIALIZED, initialization_state_); | 312 if (initialization_state_ == NOT_INITIALIZED) { |
| 547 | 313 StartInitialization(); |
| 548 CleanupAbandonedNavigations(request.navigation_id); | 314 return; |
| 549 | 315 } else if (initialization_state_ == INITIALIZING) { |
| 550 // New empty navigation entry. | 316 return; |
| 551 const GURL& main_frame_url = request.navigation_id.main_frame_url; | 317 } else if (initialization_state_ != INITIALIZED) { |
| 552 inflight_navigations_.emplace( | 318 NOTREACHED() << "Unexpected initialization_state_: " |
| 553 request.navigation_id, | 319 << initialization_state_; |
| 554 base::MakeUnique<PageRequestSummary>(main_frame_url)); | 320 return; |
| 555 } | |
| 556 | |
| 557 void ResourcePrefetchPredictor::OnMainFrameRedirect( | |
| 558 const URLRequestSummary& response) { | |
| 559 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 560 DCHECK_EQ(INITIALIZED, initialization_state_); | |
| 561 | |
| 562 const GURL& main_frame_url = response.navigation_id.main_frame_url; | |
| 563 std::unique_ptr<PageRequestSummary> summary; | |
| 564 NavigationMap::iterator nav_it = | |
| 565 inflight_navigations_.find(response.navigation_id); | |
| 566 if (nav_it != inflight_navigations_.end()) { | |
| 567 summary = std::move(nav_it->second); | |
| 568 inflight_navigations_.erase(nav_it); | |
| 569 } | 321 } |
| 570 | 322 |
| 571 // The redirect url may be empty if the URL was invalid. | |
| 572 if (response.redirect_url.is_empty()) | |
| 573 return; | |
| 574 | |
| 575 // If we lost the information about the first hop for some reason. | |
| 576 if (!summary) { | |
| 577 summary = base::MakeUnique<PageRequestSummary>(main_frame_url); | |
| 578 } | |
| 579 | |
| 580 // A redirect will not lead to another OnMainFrameRequest call, so record the | |
| 581 // redirect url as a new navigation id and save the initial url. | |
| 582 NavigationID navigation_id(response.navigation_id); | |
| 583 navigation_id.main_frame_url = response.redirect_url; | |
| 584 summary->main_frame_url = response.redirect_url; | |
| 585 inflight_navigations_.emplace(navigation_id, std::move(summary)); | |
| 586 } | |
| 587 | |
| 588 void ResourcePrefetchPredictor::OnSubresourceResponse( | |
| 589 const URLRequestSummary& response) { | |
| 590 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 591 DCHECK_EQ(INITIALIZED, initialization_state_); | |
| 592 | |
| 593 NavigationMap::const_iterator nav_it = | |
| 594 inflight_navigations_.find(response.navigation_id); | |
| 595 if (nav_it == inflight_navigations_.end()) | |
| 596 return; | |
| 597 auto& page_request_summary = *nav_it->second; | |
| 598 | |
| 599 if (!response.is_no_store) | |
| 600 page_request_summary.subresource_requests.push_back(response); | |
| 601 | |
| 602 if (config_.is_origin_learning_enabled) | |
| 603 UpdateOrAddToOrigins(&page_request_summary.origins, response); | |
| 604 } | |
| 605 | |
| 606 void ResourcePrefetchPredictor::OnSubresourceRedirect( | |
| 607 const URLRequestSummary& response) { | |
| 608 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 609 DCHECK_EQ(INITIALIZED, initialization_state_); | |
| 610 | |
| 611 if (!config_.is_origin_learning_enabled) | |
| 612 return; | |
| 613 | |
| 614 NavigationMap::const_iterator nav_it = | |
| 615 inflight_navigations_.find(response.navigation_id); | |
| 616 if (nav_it == inflight_navigations_.end()) | |
| 617 return; | |
| 618 auto& page_request_summary = *nav_it->second; | |
| 619 UpdateOrAddToOrigins(&page_request_summary.origins, response); | |
| 620 } | |
| 621 | |
| 622 void ResourcePrefetchPredictor::OnNavigationComplete( | |
| 623 const NavigationID& nav_id_without_timing_info) { | |
| 624 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 625 DCHECK_EQ(INITIALIZED, initialization_state_); | |
| 626 | |
| 627 NavigationMap::iterator nav_it = | |
| 628 inflight_navigations_.find(nav_id_without_timing_info); | |
| 629 if (nav_it == inflight_navigations_.end()) | |
| 630 return; | |
| 631 | |
| 632 // Remove the navigation from the inflight navigations. | |
| 633 std::unique_ptr<PageRequestSummary> summary = std::move(nav_it->second); | |
| 634 inflight_navigations_.erase(nav_it); | |
| 635 | |
| 636 // Set before_first_contentful paint for each resource. | |
| 637 for (auto& request_summary : summary->subresource_requests) { | |
| 638 request_summary.before_first_contentful_paint = | |
| 639 request_summary.response_time < summary->first_contentful_paint; | |
| 640 } | |
| 641 | |
| 642 if (stats_collector_) | |
| 643 stats_collector_->RecordPageRequestSummary(*summary); | |
| 644 | |
| 645 // Kick off history lookup to determine if we should record the URL. | 323 // Kick off history lookup to determine if we should record the URL. |
| 646 history::HistoryService* history_service = | 324 history::HistoryService* history_service = |
| 647 HistoryServiceFactory::GetForProfile(profile_, | 325 HistoryServiceFactory::GetForProfile(profile_, |
| 648 ServiceAccessType::EXPLICIT_ACCESS); | 326 ServiceAccessType::EXPLICIT_ACCESS); |
| 649 DCHECK(history_service); | 327 DCHECK(history_service); |
| 650 history_service->ScheduleDBTask( | 328 history_service->ScheduleDBTask( |
| 651 std::unique_ptr<history::HistoryDBTask>(new GetUrlVisitCountTask( | 329 std::unique_ptr<history::HistoryDBTask>(new GetUrlVisitCountTask( |
| 652 std::move(summary), | 330 std::move(summary), |
| 653 base::BindOnce(&ResourcePrefetchPredictor::OnVisitCountLookup, | 331 base::BindOnce(&ResourcePrefetchPredictor::OnVisitCountLookup, |
| 654 weak_factory_.GetWeakPtr()))), | 332 weak_factory_.GetWeakPtr()))), |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 867 if (config_.IsPrefetchingEnabledForSomeOrigin(profile_)) { | 545 if (config_.IsPrefetchingEnabledForSomeOrigin(profile_)) { |
| 868 prefetch_manager_ = new ResourcePrefetcherManager( | 546 prefetch_manager_ = new ResourcePrefetcherManager( |
| 869 this, config_, profile_->GetRequestContext()); | 547 this, config_, profile_->GetRequestContext()); |
| 870 } | 548 } |
| 871 initialization_state_ = INITIALIZED; | 549 initialization_state_ = INITIALIZED; |
| 872 | 550 |
| 873 if (observer_) | 551 if (observer_) |
| 874 observer_->OnPredictorInitialized(); | 552 observer_->OnPredictorInitialized(); |
| 875 } | 553 } |
| 876 | 554 |
| 877 void ResourcePrefetchPredictor::CleanupAbandonedNavigations( | |
| 878 const NavigationID& navigation_id) { | |
| 879 if (stats_collector_) | |
| 880 stats_collector_->CleanupAbandonedStats(); | |
| 881 | |
| 882 static const base::TimeDelta max_navigation_age = | |
| 883 base::TimeDelta::FromSeconds(config_.max_navigation_lifetime_seconds); | |
| 884 | |
| 885 base::TimeTicks time_now = base::TimeTicks::Now(); | |
| 886 for (NavigationMap::iterator it = inflight_navigations_.begin(); | |
| 887 it != inflight_navigations_.end();) { | |
| 888 if ((it->first.tab_id == navigation_id.tab_id) || | |
| 889 (time_now - it->first.creation_time > max_navigation_age)) { | |
| 890 inflight_navigations_.erase(it++); | |
| 891 } else { | |
| 892 ++it; | |
| 893 } | |
| 894 } | |
| 895 } | |
| 896 | |
| 897 void ResourcePrefetchPredictor::DeleteAllUrls() { | 555 void ResourcePrefetchPredictor::DeleteAllUrls() { |
| 898 inflight_navigations_.clear(); | |
| 899 | |
| 900 url_resource_data_->DeleteAllData(); | 556 url_resource_data_->DeleteAllData(); |
| 901 host_resource_data_->DeleteAllData(); | 557 host_resource_data_->DeleteAllData(); |
| 902 url_redirect_data_->DeleteAllData(); | 558 url_redirect_data_->DeleteAllData(); |
| 903 host_redirect_data_->DeleteAllData(); | 559 host_redirect_data_->DeleteAllData(); |
| 904 manifest_data_->DeleteAllData(); | 560 manifest_data_->DeleteAllData(); |
| 905 origin_data_->DeleteAllData(); | 561 origin_data_->DeleteAllData(); |
| 906 } | 562 } |
| 907 | 563 |
| 908 void ResourcePrefetchPredictor::DeleteUrls(const history::URLRows& urls) { | 564 void ResourcePrefetchPredictor::DeleteUrls(const history::URLRows& urls) { |
| 909 std::vector<std::string> urls_to_delete; | 565 std::vector<std::string> urls_to_delete; |
| (...skipping 453 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1363 if (data.resources_size() == 0) | 1019 if (data.resources_size() == 0) |
| 1364 resource_data->DeleteData({key}); | 1020 resource_data->DeleteData({key}); |
| 1365 else | 1021 else |
| 1366 resource_data->UpdateData(key, data); | 1022 resource_data->UpdateData(key, data); |
| 1367 } | 1023 } |
| 1368 } | 1024 } |
| 1369 | 1025 |
| 1370 void ResourcePrefetchPredictor::ConnectToHistoryService() { | 1026 void ResourcePrefetchPredictor::ConnectToHistoryService() { |
| 1371 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 1027 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 1372 DCHECK_EQ(INITIALIZING, initialization_state_); | 1028 DCHECK_EQ(INITIALIZING, initialization_state_); |
| 1373 DCHECK(inflight_navigations_.empty()); | |
| 1374 | 1029 |
| 1375 // Register for HistoryServiceLoading if it is not ready. | 1030 // Register for HistoryServiceLoading if it is not ready. |
| 1376 history::HistoryService* history_service = | 1031 history::HistoryService* history_service = |
| 1377 HistoryServiceFactory::GetForProfile(profile_, | 1032 HistoryServiceFactory::GetForProfile(profile_, |
| 1378 ServiceAccessType::EXPLICIT_ACCESS); | 1033 ServiceAccessType::EXPLICIT_ACCESS); |
| 1379 if (!history_service) | 1034 if (!history_service) |
| 1380 return; | 1035 return; |
| 1381 DCHECK(!history_service_observer_.IsObserving(history_service)); | 1036 DCHECK(!history_service_observer_.IsObserving(history_service)); |
| 1382 history_service_observer_.Add(history_service); | 1037 history_service_observer_.Add(history_service); |
| 1383 if (history_service->BackendLoaded()) { | 1038 if (history_service->BackendLoaded()) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1424 TestObserver::~TestObserver() { | 1079 TestObserver::~TestObserver() { |
| 1425 predictor_->SetObserverForTesting(nullptr); | 1080 predictor_->SetObserverForTesting(nullptr); |
| 1426 } | 1081 } |
| 1427 | 1082 |
| 1428 TestObserver::TestObserver(ResourcePrefetchPredictor* predictor) | 1083 TestObserver::TestObserver(ResourcePrefetchPredictor* predictor) |
| 1429 : predictor_(predictor) { | 1084 : predictor_(predictor) { |
| 1430 predictor_->SetObserverForTesting(this); | 1085 predictor_->SetObserverForTesting(this); |
| 1431 } | 1086 } |
| 1432 | 1087 |
| 1433 } // namespace predictors | 1088 } // namespace predictors |
| OLD | NEW |