| 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 <algorithm> |
| 7 #include <map> | 8 #include <map> |
| 8 #include <set> | 9 #include <set> |
| 9 #include <utility> | 10 #include <utility> |
| 10 | 11 |
| 11 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 12 #include "base/macros.h" | 13 #include "base/macros.h" |
| 13 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
| 14 #include "base/metrics/sparse_histogram.h" | 15 #include "base/metrics/sparse_histogram.h" |
| 15 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
| 16 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
| (...skipping 16 matching lines...) Expand all Loading... |
| 33 #include "net/base/mime_util.h" | 34 #include "net/base/mime_util.h" |
| 34 #include "net/base/network_change_notifier.h" | 35 #include "net/base/network_change_notifier.h" |
| 35 #include "net/http/http_response_headers.h" | 36 #include "net/http/http_response_headers.h" |
| 36 #include "net/url_request/url_request.h" | 37 #include "net/url_request/url_request.h" |
| 37 #include "net/url_request/url_request_context_getter.h" | 38 #include "net/url_request/url_request_context_getter.h" |
| 38 | 39 |
| 39 using content::BrowserThread; | 40 using content::BrowserThread; |
| 40 | 41 |
| 41 namespace { | 42 namespace { |
| 42 | 43 |
| 44 // Sorted by decreasing likelihood according to HTTP archive. |
| 45 const char* kFontMimeTypes[] = {"font/woff2", |
| 46 "application/x-font-woff", |
| 47 "application/font-woff", |
| 48 "application/font-woff2", |
| 49 "font/x-woff", |
| 50 "application/x-font-ttf", |
| 51 "font/woff", |
| 52 "font/ttf", |
| 53 "application/x-font-otf", |
| 54 "x-font/woff", |
| 55 "application/font-sfnt", |
| 56 "application/font-ttf"}; |
| 57 |
| 43 // For reporting whether a subresource is handled or not, and for what reasons. | 58 // For reporting whether a subresource is handled or not, and for what reasons. |
| 44 enum ResourceStatus { | 59 enum ResourceStatus { |
| 45 RESOURCE_STATUS_HANDLED = 0, | 60 RESOURCE_STATUS_HANDLED = 0, |
| 46 RESOURCE_STATUS_NOT_HTTP_OR_HTTPS_PAGE = 1, | 61 RESOURCE_STATUS_NOT_HTTP_OR_HTTPS_PAGE = 1, |
| 47 RESOURCE_STATUS_NOT_HTTP_OR_HTTPS_RESOURCE = 2, | 62 RESOURCE_STATUS_NOT_HTTP_OR_HTTPS_RESOURCE = 2, |
| 48 RESOURCE_STATUS_UNSUPPORTED_MIME_TYPE = 4, | 63 RESOURCE_STATUS_UNSUPPORTED_RESOURCE_TYPE = 4, |
| 49 RESOURCE_STATUS_NOT_GET = 8, | 64 RESOURCE_STATUS_NOT_GET = 8, |
| 50 RESOURCE_STATUS_URL_TOO_LONG = 16, | 65 RESOURCE_STATUS_URL_TOO_LONG = 16, |
| 51 RESOURCE_STATUS_NOT_CACHEABLE = 32, | 66 RESOURCE_STATUS_NOT_CACHEABLE = 32, |
| 52 RESOURCE_STATUS_HEADERS_MISSING = 64, | 67 RESOURCE_STATUS_HEADERS_MISSING = 64, |
| 53 RESOURCE_STATUS_MAX = 128, | 68 RESOURCE_STATUS_MAX = 128, |
| 54 }; | 69 }; |
| 55 | 70 |
| 56 // For reporting various interesting events that occur during the loading of a | 71 // For reporting various interesting events that occur during the loading of a |
| 57 // single main frame. | 72 // single main frame. |
| 58 enum NavigationEvent { | 73 enum NavigationEvent { |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 204 bool ResourcePrefetchPredictor::ShouldRecordResponse( | 219 bool ResourcePrefetchPredictor::ShouldRecordResponse( |
| 205 net::URLRequest* response) { | 220 net::URLRequest* response) { |
| 206 const content::ResourceRequestInfo* request_info = | 221 const content::ResourceRequestInfo* request_info = |
| 207 content::ResourceRequestInfo::ForRequest(response); | 222 content::ResourceRequestInfo::ForRequest(response); |
| 208 if (!request_info) | 223 if (!request_info) |
| 209 return false; | 224 return false; |
| 210 | 225 |
| 211 if (!request_info->IsMainFrame()) | 226 if (!request_info->IsMainFrame()) |
| 212 return false; | 227 return false; |
| 213 | 228 |
| 214 return request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME ? | 229 content::ResourceType resource_type = request_info->GetResourceType(); |
| 215 IsHandledMainPage(response) : IsHandledSubresource(response); | 230 return resource_type == content::RESOURCE_TYPE_MAIN_FRAME |
| 231 ? IsHandledMainPage(response) |
| 232 : IsHandledSubresource(response, resource_type); |
| 216 } | 233 } |
| 217 | 234 |
| 218 // static | 235 // static |
| 219 bool ResourcePrefetchPredictor::ShouldRecordRedirect( | 236 bool ResourcePrefetchPredictor::ShouldRecordRedirect( |
| 220 net::URLRequest* response) { | 237 net::URLRequest* response) { |
| 221 const content::ResourceRequestInfo* request_info = | 238 const content::ResourceRequestInfo* request_info = |
| 222 content::ResourceRequestInfo::ForRequest(response); | 239 content::ResourceRequestInfo::ForRequest(response); |
| 223 if (!request_info) | 240 if (!request_info) |
| 224 return false; | 241 return false; |
| 225 | 242 |
| 226 if (!request_info->IsMainFrame()) | 243 if (!request_info->IsMainFrame()) |
| 227 return false; | 244 return false; |
| 228 | 245 |
| 229 return request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME && | 246 return request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME && |
| 230 IsHandledMainPage(response); | 247 IsHandledMainPage(response); |
| 231 } | 248 } |
| 232 | 249 |
| 233 // static | 250 // static |
| 234 bool ResourcePrefetchPredictor::IsHandledMainPage(net::URLRequest* request) { | 251 bool ResourcePrefetchPredictor::IsHandledMainPage(net::URLRequest* request) { |
| 235 return request->url().SchemeIsHTTPOrHTTPS(); | 252 return request->url().SchemeIsHTTPOrHTTPS(); |
| 236 } | 253 } |
| 237 | 254 |
| 238 // static | 255 // static |
| 239 bool ResourcePrefetchPredictor::IsHandledSubresource( | 256 bool ResourcePrefetchPredictor::IsHandledSubresource( |
| 240 net::URLRequest* response) { | 257 net::URLRequest* response, |
| 258 content::ResourceType resource_type) { |
| 241 int resource_status = 0; | 259 int resource_status = 0; |
| 242 | 260 |
| 243 if (!response->first_party_for_cookies().SchemeIsHTTPOrHTTPS()) | 261 if (!response->first_party_for_cookies().SchemeIsHTTPOrHTTPS()) |
| 244 resource_status |= RESOURCE_STATUS_NOT_HTTP_OR_HTTPS_PAGE; | 262 resource_status |= RESOURCE_STATUS_NOT_HTTP_OR_HTTPS_PAGE; |
| 245 | 263 |
| 246 if (!response->url().SchemeIsHTTPOrHTTPS()) | 264 if (!response->url().SchemeIsHTTPOrHTTPS()) |
| 247 resource_status |= RESOURCE_STATUS_NOT_HTTP_OR_HTTPS_RESOURCE; | 265 resource_status |= RESOURCE_STATUS_NOT_HTTP_OR_HTTPS_RESOURCE; |
| 248 | 266 |
| 249 std::string mime_type; | 267 std::string mime_type; |
| 250 response->GetMimeType(&mime_type); | 268 response->GetMimeType(&mime_type); |
| 251 if (!mime_type.empty() && !mime_util::IsSupportedImageMimeType(mime_type) && | 269 if (!IsHandledResourceType(resource_type, mime_type)) |
| 252 !mime_util::IsSupportedJavascriptMimeType(mime_type) && | 270 resource_status |= RESOURCE_STATUS_UNSUPPORTED_RESOURCE_TYPE; |
| 253 !net::MatchesMimeType("text/css", mime_type)) { | |
| 254 resource_status |= RESOURCE_STATUS_UNSUPPORTED_MIME_TYPE; | |
| 255 } | |
| 256 | 271 |
| 257 if (response->method() != "GET") | 272 if (response->method() != "GET") |
| 258 resource_status |= RESOURCE_STATUS_NOT_GET; | 273 resource_status |= RESOURCE_STATUS_NOT_GET; |
| 259 | 274 |
| 260 if (response->original_url().spec().length() > | 275 if (response->original_url().spec().length() > |
| 261 ResourcePrefetchPredictorTables::kMaxStringLength) { | 276 ResourcePrefetchPredictorTables::kMaxStringLength) { |
| 262 resource_status |= RESOURCE_STATUS_URL_TOO_LONG; | 277 resource_status |= RESOURCE_STATUS_URL_TOO_LONG; |
| 263 } | 278 } |
| 264 | 279 |
| 265 if (!response->response_info().headers.get()) | 280 if (!response->response_info().headers.get()) |
| 266 resource_status |= RESOURCE_STATUS_HEADERS_MISSING; | 281 resource_status |= RESOURCE_STATUS_HEADERS_MISSING; |
| 267 | 282 |
| 268 if (!IsCacheable(response)) | 283 if (!IsCacheable(response)) |
| 269 resource_status |= RESOURCE_STATUS_NOT_CACHEABLE; | 284 resource_status |= RESOURCE_STATUS_NOT_CACHEABLE; |
| 270 | 285 |
| 271 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ResourceStatus", | 286 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ResourceStatus", |
| 272 resource_status, | 287 resource_status, |
| 273 RESOURCE_STATUS_MAX); | 288 RESOURCE_STATUS_MAX); |
| 274 | 289 |
| 275 return resource_status == 0; | 290 return resource_status == 0; |
| 276 } | 291 } |
| 277 | 292 |
| 278 // static | 293 // static |
| 294 bool ResourcePrefetchPredictor::IsHandledResourceType( |
| 295 content::ResourceType resource_type, |
| 296 const std::string& mime_type) { |
| 297 bool handled = resource_type == content::RESOURCE_TYPE_STYLESHEET || |
| 298 resource_type == content::RESOURCE_TYPE_SCRIPT || |
| 299 resource_type == content::RESOURCE_TYPE_IMAGE || |
| 300 resource_type == content::RESOURCE_TYPE_FONT_RESOURCE; |
| 301 // Restricts content::RESOURCE_TYPE_{PREFETCH,SUB_RESOURCE} to a small set of |
| 302 // mime types, because these resource types don't communicate how the |
| 303 // resources |
| 304 // will be used. |
| 305 if ((resource_type == content::RESOURCE_TYPE_PREFETCH || |
| 306 resource_type == content::RESOURCE_TYPE_SUB_RESOURCE)) { |
| 307 content::ResourceType resource_type_from_mime = GetResourceTypeFromMimeType( |
| 308 mime_type, content::RESOURCE_TYPE_LAST_TYPE); |
| 309 handled = resource_type_from_mime != content::RESOURCE_TYPE_LAST_TYPE; |
| 310 } |
| 311 return handled; |
| 312 } |
| 313 |
| 314 // static |
| 279 bool ResourcePrefetchPredictor::IsCacheable(const net::URLRequest* response) { | 315 bool ResourcePrefetchPredictor::IsCacheable(const net::URLRequest* response) { |
| 280 if (response->was_cached()) | 316 if (response->was_cached()) |
| 281 return true; | 317 return true; |
| 282 | 318 |
| 283 // For non cached responses, we will ensure that the freshness lifetime is | 319 // For non cached responses, we will ensure that the freshness lifetime is |
| 284 // some sane value. | 320 // some sane value. |
| 285 const net::HttpResponseInfo& response_info = response->response_info(); | 321 const net::HttpResponseInfo& response_info = response->response_info(); |
| 286 if (!response_info.headers.get()) | 322 if (!response_info.headers.get()) |
| 287 return false; | 323 return false; |
| 288 base::Time response_time(response_info.response_time); | 324 base::Time response_time(response_info.response_time); |
| 289 response_time += base::TimeDelta::FromSeconds(1); | 325 response_time += base::TimeDelta::FromSeconds(1); |
| 290 base::TimeDelta freshness = | 326 base::TimeDelta freshness = |
| 291 response_info.headers->GetFreshnessLifetimes(response_time).freshness; | 327 response_info.headers->GetFreshnessLifetimes(response_time).freshness; |
| 292 return freshness > base::TimeDelta(); | 328 return freshness > base::TimeDelta(); |
| 293 } | 329 } |
| 294 | 330 |
| 295 // static | 331 // static |
| 296 content::ResourceType ResourcePrefetchPredictor::GetResourceTypeFromMimeType( | 332 content::ResourceType ResourcePrefetchPredictor::GetResourceTypeFromMimeType( |
| 297 const std::string& mime_type, | 333 const std::string& mime_type, |
| 298 content::ResourceType fallback) { | 334 content::ResourceType fallback) { |
| 299 if (mime_util::IsSupportedImageMimeType(mime_type)) | 335 if (mime_type.empty()) { |
| 336 return fallback; |
| 337 } else if (mime_util::IsSupportedImageMimeType(mime_type)) { |
| 300 return content::RESOURCE_TYPE_IMAGE; | 338 return content::RESOURCE_TYPE_IMAGE; |
| 301 else if (mime_util::IsSupportedJavascriptMimeType(mime_type)) | 339 } else if (mime_util::IsSupportedJavascriptMimeType(mime_type)) { |
| 302 return content::RESOURCE_TYPE_SCRIPT; | 340 return content::RESOURCE_TYPE_SCRIPT; |
| 303 else if (net::MatchesMimeType("text/css", mime_type)) | 341 } else if (net::MatchesMimeType("text/css", mime_type)) { |
| 304 return content::RESOURCE_TYPE_STYLESHEET; | 342 return content::RESOURCE_TYPE_STYLESHEET; |
| 305 else | 343 } else { |
| 306 return fallback; | 344 bool found = |
| 345 std::any_of(std::begin(kFontMimeTypes), std::end(kFontMimeTypes), |
| 346 [&mime_type](const std::string& mime) { |
| 347 return net::MatchesMimeType(mime, mime_type); |
| 348 }); |
| 349 if (found) |
| 350 return content::RESOURCE_TYPE_FONT_RESOURCE; |
| 351 } |
| 352 return fallback; |
| 307 } | 353 } |
| 308 | 354 |
| 309 //////////////////////////////////////////////////////////////////////////////// | 355 //////////////////////////////////////////////////////////////////////////////// |
| 310 // ResourcePrefetchPredictor structs. | 356 // ResourcePrefetchPredictor structs. |
| 311 | 357 |
| 312 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary() | 358 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary() |
| 313 : resource_type(content::RESOURCE_TYPE_LAST_TYPE), | 359 : resource_type(content::RESOURCE_TYPE_LAST_TYPE), |
| 314 was_cached(false) { | 360 was_cached(false) { |
| 315 } | 361 } |
| 316 | 362 |
| (...skipping 1018 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1335 // HistoryService is already loaded. Continue with Initialization. | 1381 // HistoryService is already loaded. Continue with Initialization. |
| 1336 OnHistoryAndCacheLoaded(); | 1382 OnHistoryAndCacheLoaded(); |
| 1337 return; | 1383 return; |
| 1338 } | 1384 } |
| 1339 DCHECK(!history_service_observer_.IsObserving(history_service)); | 1385 DCHECK(!history_service_observer_.IsObserving(history_service)); |
| 1340 history_service_observer_.Add(history_service); | 1386 history_service_observer_.Add(history_service); |
| 1341 return; | 1387 return; |
| 1342 } | 1388 } |
| 1343 | 1389 |
| 1344 } // namespace predictors | 1390 } // namespace predictors |
| OLD | NEW |