Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(94)

Side by Side Diff: chrome/browser/predictors/loading_data_collector.cc

Issue 2937623007: predictors: Move more methods from ResourcePrefetchPredictor into LoadingDataCollector. (Closed)
Patch Set: Undo unneeded added mock class. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 <map>
6 #include <memory>
5 #include <string> 7 #include <string>
8 #include <utility>
6 9
7 #include "chrome/browser/predictors/loading_data_collector.h" 10 #include "chrome/browser/predictors/loading_data_collector.h"
11 #include "chrome/browser/predictors/loading_stats_collector.h"
12 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
13 #include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
8 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/profiles/profile.h"
15 #include "components/history/core/browser/history_service.h"
9 #include "components/mime_util/mime_util.h" 16 #include "components/mime_util/mime_util.h"
17 #include "content/public/browser/browser_thread.h"
10 #include "content/public/browser/resource_request_info.h" 18 #include "content/public/browser/resource_request_info.h"
11 #include "content/public/common/resource_type.h" 19 #include "content/public/common/resource_type.h"
20 #include "net/http/http_response_headers.h"
12 #include "net/url_request/url_request.h" 21 #include "net/url_request/url_request.h"
13 22
23 using content::BrowserThread;
24
14 namespace predictors { 25 namespace predictors {
15 26
16 namespace { 27 namespace {
17 28
18 bool g_allow_port_in_urls = false; 29 bool g_allow_port_in_urls = false;
19 30
31 // Sorted by decreasing likelihood according to HTTP archive.
32 const char* kFontMimeTypes[] = {"font/woff2",
33 "application/x-font-woff",
34 "application/font-woff",
35 "application/font-woff2",
36 "font/x-woff",
37 "application/x-font-ttf",
38 "font/woff",
39 "font/ttf",
40 "application/x-font-otf",
41 "x-font/woff",
42 "application/font-sfnt",
43 "application/font-ttf"};
44
45 bool IsNoStore(const net::URLRequest& response) {
46 if (response.was_cached())
47 return false;
48
49 const net::HttpResponseInfo& response_info = response.response_info();
50 if (!response_info.headers.get())
51 return false;
52 return response_info.headers->HasHeaderValue("cache-control", "no-store");
53 }
54
20 } // namespace 55 } // namespace
21 56
57 OriginRequestSummary::OriginRequestSummary()
58 : origin(),
59 always_access_network(false),
60 accessed_network(false),
61 first_occurrence(0) {}
62
63 OriginRequestSummary::OriginRequestSummary(const OriginRequestSummary& other) =
64 default;
65
66 OriginRequestSummary::~OriginRequestSummary() {}
67
68 URLRequestSummary::URLRequestSummary()
69 : resource_type(content::RESOURCE_TYPE_LAST_TYPE),
70 priority(net::IDLE),
71 before_first_contentful_paint(false),
72 was_cached(false),
73 has_validators(false),
74 always_revalidate(false),
75 is_no_store(false),
76 network_accessed(false) {}
77
78 URLRequestSummary::URLRequestSummary(const URLRequestSummary& other) = default;
79
80 URLRequestSummary::~URLRequestSummary() {}
81
82 // static
83 bool URLRequestSummary::SummarizeResponse(const net::URLRequest& request,
84 URLRequestSummary* summary) {
85 const content::ResourceRequestInfo* request_info =
86 content::ResourceRequestInfo::ForRequest(&request);
87 if (!request_info)
88 return false;
89
90 // This method is called when the response is started, so this field reflects
91 // the time at which the response began, not when it finished, as would
92 // arguably be ideal. This means if firstContentfulPaint happens after the
93 // response has started, but before it's finished, we will erroneously mark
94 // the resource as having been loaded before firstContentfulPaint. This is
95 // a rare and insignificant enough occurrence that we opt to record the time
96 // here for the sake of simplicity.
97 summary->response_time = base::TimeTicks::Now();
98 summary->resource_url = request.original_url();
99 summary->request_url = request.url();
100 content::ResourceType resource_type_from_request =
101 request_info->GetResourceType();
102 summary->priority = request.priority();
103 request.GetMimeType(&summary->mime_type);
104 summary->was_cached = request.was_cached();
105 summary->resource_type = LoadingDataCollector::GetResourceType(
106 resource_type_from_request, summary->mime_type);
107
108 scoped_refptr<net::HttpResponseHeaders> headers =
109 request.response_info().headers;
110 if (headers.get()) {
111 summary->has_validators = headers->HasValidators();
112 // RFC 2616, section 14.9.
113 summary->always_revalidate =
114 headers->HasHeaderValue("cache-control", "no-cache") ||
115 headers->HasHeaderValue("pragma", "no-cache") ||
116 headers->HasHeaderValue("vary", "*");
117 summary->is_no_store = IsNoStore(request);
118 }
119 summary->network_accessed = request.response_info().network_accessed;
120 return true;
121 }
122
123 PageRequestSummary::PageRequestSummary(const GURL& i_main_frame_url)
124 : main_frame_url(i_main_frame_url),
125 initial_url(i_main_frame_url),
126 first_contentful_paint(base::TimeTicks::Max()) {}
127
128 PageRequestSummary::PageRequestSummary(const PageRequestSummary& other) =
129 default;
130
131 void PageRequestSummary::UpdateOrAddToOrigins(
132 const URLRequestSummary& request_summary) {
133 const GURL& request_url = request_summary.request_url;
134 DCHECK(request_url.is_valid());
135 if (!request_url.is_valid())
136 return;
137
138 GURL origin = request_url.GetOrigin();
139 auto it = origins.find(origin);
140 if (it == origins.end()) {
141 OriginRequestSummary summary;
142 summary.origin = origin;
143 summary.first_occurrence = origins.size();
144 it = origins.insert({origin, summary}).first;
145 }
146
147 it->second.always_access_network |=
148 request_summary.always_revalidate || request_summary.is_no_store;
149 it->second.accessed_network |= request_summary.network_accessed;
150 }
151
152 PageRequestSummary::~PageRequestSummary() {}
153
154 content::ResourceType LoadingDataCollector::GetResourceTypeFromMimeType(
155 const std::string& mime_type,
156 content::ResourceType fallback) {
157 if (mime_type.empty()) {
158 return fallback;
159 } else if (mime_util::IsSupportedImageMimeType(mime_type)) {
160 return content::RESOURCE_TYPE_IMAGE;
161 } else if (mime_util::IsSupportedJavascriptMimeType(mime_type)) {
162 return content::RESOURCE_TYPE_SCRIPT;
163 } else if (net::MatchesMimeType("text/css", mime_type)) {
164 return content::RESOURCE_TYPE_STYLESHEET;
165 } else {
166 bool found =
167 std::any_of(std::begin(kFontMimeTypes), std::end(kFontMimeTypes),
168 [&mime_type](const std::string& mime) {
169 return net::MatchesMimeType(mime, mime_type);
170 });
171 if (found)
172 return content::RESOURCE_TYPE_FONT_RESOURCE;
173 }
174 return fallback;
175 }
176
177 content::ResourceType LoadingDataCollector::GetResourceType(
178 content::ResourceType resource_type,
179 const std::string& mime_type) {
180 // Restricts content::RESOURCE_TYPE_{PREFETCH,SUB_RESOURCE,XHR} to a small set
181 // of mime types, because these resource types don't communicate how the
182 // resources will be used.
183 if (resource_type == content::RESOURCE_TYPE_PREFETCH ||
184 resource_type == content::RESOURCE_TYPE_SUB_RESOURCE ||
185 resource_type == content::RESOURCE_TYPE_XHR) {
186 return GetResourceTypeFromMimeType(mime_type,
187 content::RESOURCE_TYPE_LAST_TYPE);
188 }
189 return resource_type;
190 }
191
22 // static 192 // static
23 bool LoadingDataCollector::ShouldRecordRequest( 193 bool LoadingDataCollector::ShouldRecordRequest(
24 net::URLRequest* request, 194 net::URLRequest* request,
25 content::ResourceType resource_type) { 195 content::ResourceType resource_type) {
26 const content::ResourceRequestInfo* request_info = 196 const content::ResourceRequestInfo* request_info =
27 content::ResourceRequestInfo::ForRequest(request); 197 content::ResourceRequestInfo::ForRequest(request);
28 if (!request_info) 198 if (!request_info)
29 return false; 199 return false;
30 200
31 if (!request_info->IsMainFrame()) 201 if (!request_info->IsMainFrame())
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 return false; 261 return false;
92 262
93 return true; 263 return true;
94 } 264 }
95 265
96 // static 266 // static
97 bool LoadingDataCollector::IsHandledResourceType( 267 bool LoadingDataCollector::IsHandledResourceType(
98 content::ResourceType resource_type, 268 content::ResourceType resource_type,
99 const std::string& mime_type) { 269 const std::string& mime_type) {
100 content::ResourceType actual_resource_type = 270 content::ResourceType actual_resource_type =
101 ResourcePrefetchPredictor::GetResourceType(resource_type, mime_type); 271 GetResourceType(resource_type, mime_type);
102 return actual_resource_type == content::RESOURCE_TYPE_STYLESHEET || 272 return actual_resource_type == content::RESOURCE_TYPE_STYLESHEET ||
103 actual_resource_type == content::RESOURCE_TYPE_SCRIPT || 273 actual_resource_type == content::RESOURCE_TYPE_SCRIPT ||
104 actual_resource_type == content::RESOURCE_TYPE_IMAGE || 274 actual_resource_type == content::RESOURCE_TYPE_IMAGE ||
105 actual_resource_type == content::RESOURCE_TYPE_FONT_RESOURCE; 275 actual_resource_type == content::RESOURCE_TYPE_FONT_RESOURCE;
106 } 276 }
107 277
108 // static 278 // static
109 void LoadingDataCollector::SetAllowPortInUrlsForTesting(bool state) { 279 void LoadingDataCollector::SetAllowPortInUrlsForTesting(bool state) {
110 g_allow_port_in_urls = state; 280 g_allow_port_in_urls = state;
111 } 281 }
112 282
113 LoadingDataCollector::LoadingDataCollector(ResourcePrefetchPredictor* predictor) 283 LoadingDataCollector::LoadingDataCollector(
114 : predictor_(predictor) {} 284 ResourcePrefetchPredictor* predictor,
285 predictors::LoadingStatsCollector* stats_collector,
286 const LoadingPredictorConfig& config)
287 : predictor_(predictor),
288 stats_collector_(stats_collector),
289 config_(config) {}
115 290
116 LoadingDataCollector::~LoadingDataCollector() {} 291 LoadingDataCollector::~LoadingDataCollector() {}
117 292
118 void LoadingDataCollector::RecordURLRequest( 293 void LoadingDataCollector::RecordURLRequest(const URLRequestSummary& request) {
119 const ResourcePrefetchPredictor::URLRequestSummary& request) { 294 DCHECK_CURRENTLY_ON(BrowserThread::UI);
120 predictor_->RecordURLRequest(request); 295 CHECK_EQ(request.resource_type, content::RESOURCE_TYPE_MAIN_FRAME);
296
297 CleanupAbandonedNavigations(request.navigation_id);
298
299 // New empty navigation entry.
300 const GURL& main_frame_url = request.navigation_id.main_frame_url;
301 inflight_navigations_.emplace(
302 request.navigation_id,
303 base::MakeUnique<PageRequestSummary>(main_frame_url));
121 } 304 }
122 305
123 void LoadingDataCollector::RecordURLResponse( 306 void LoadingDataCollector::RecordURLResponse(
124 const ResourcePrefetchPredictor::URLRequestSummary& response) { 307 const URLRequestSummary& response) {
125 predictor_->RecordURLResponse(response); 308 DCHECK_CURRENTLY_ON(BrowserThread::UI);
309
310 if (response.resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
311 return;
312
313 NavigationMap::const_iterator nav_it =
314 inflight_navigations_.find(response.navigation_id);
315 if (nav_it == inflight_navigations_.end())
316 return;
317 auto& page_request_summary = *nav_it->second;
318
319 if (!response.is_no_store)
320 page_request_summary.subresource_requests.push_back(response);
321
322 if (config_.is_origin_learning_enabled)
323 page_request_summary.UpdateOrAddToOrigins(response);
126 } 324 }
127 325
128 void LoadingDataCollector::RecordURLRedirect( 326 void LoadingDataCollector::RecordURLRedirect(
129 const ResourcePrefetchPredictor::URLRequestSummary& response) { 327 const URLRequestSummary& response) {
130 predictor_->RecordURLRedirect(response); 328 DCHECK_CURRENTLY_ON(BrowserThread::UI);
329
330 if (response.resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
331 OnMainFrameRedirect(response);
332 else
333 OnSubresourceRedirect(response);
131 } 334 }
132 335
133 void LoadingDataCollector::RecordMainFrameLoadComplete( 336 void LoadingDataCollector::RecordMainFrameLoadComplete(
134 const NavigationID& navigation_id) { 337 const NavigationID& navigation_id) {
135 predictor_->RecordMainFrameLoadComplete(navigation_id); 338 DCHECK_CURRENTLY_ON(BrowserThread::UI);
339
340 // WebContents can return an empty URL if the navigation entry corresponding
341 // to the navigation has not been created yet.
342 LOG(WARNING) << "navigation_id.main_frame_url.is_empty(): "
343 << navigation_id.main_frame_url.is_empty();
344 if (navigation_id.main_frame_url.is_empty())
345 return;
346
347 NavigationMap::iterator nav_it = inflight_navigations_.find(navigation_id);
348 LOG(WARNING) << "nav_it == inflight_navigations_.end(): "
349 << (nav_it == inflight_navigations_.end());
350 if (nav_it == inflight_navigations_.end())
351 return;
352
353 // Remove the navigation from the inflight navigations.
354 std::unique_ptr<PageRequestSummary> summary = std::move(nav_it->second);
355 inflight_navigations_.erase(nav_it);
356
357 // Set before_first_contentful paint for each resource.
358 for (auto& request_summary : summary->subresource_requests) {
359 request_summary.before_first_contentful_paint =
360 request_summary.response_time < summary->first_contentful_paint;
361 }
362
363 if (stats_collector_)
364 stats_collector_->RecordPageRequestSummary(*summary);
365
366 LOG(WARNING) << "predictor_: " << predictor_;
alexilin 2017/06/22 19:39:05 Please, remove all logging statements.
trevordixon 2017/06/27 21:28:55 Done.
367 if (predictor_) {
368 LOG(WARNING) << "CALLING";
369 predictor_->RecordPageRequestSummary(std::move(summary));
370 LOG(WARNING) << "CALLED";
371 }
136 } 372 }
137 373
138 void LoadingDataCollector::RecordFirstContentfulPaint( 374 void LoadingDataCollector::RecordFirstContentfulPaint(
139 const NavigationID& navigation_id, 375 const NavigationID& navigation_id,
140 const base::TimeTicks& first_contentful_paint) { 376 const base::TimeTicks& first_contentful_paint) {
141 predictor_->RecordFirstContentfulPaint(navigation_id, first_contentful_paint); 377 DCHECK_CURRENTLY_ON(BrowserThread::UI);
378
379 NavigationMap::iterator nav_it = inflight_navigations_.find(navigation_id);
380 if (nav_it != inflight_navigations_.end())
381 nav_it->second->first_contentful_paint = first_contentful_paint;
382 }
383
384 void LoadingDataCollector::OnMainFrameRedirect(
385 const URLRequestSummary& response) {
386 DCHECK_CURRENTLY_ON(BrowserThread::UI);
387
388 const GURL& main_frame_url = response.navigation_id.main_frame_url;
389 std::unique_ptr<PageRequestSummary> summary;
390 NavigationMap::iterator nav_it =
391 inflight_navigations_.find(response.navigation_id);
392 if (nav_it != inflight_navigations_.end()) {
393 summary = std::move(nav_it->second);
394 inflight_navigations_.erase(nav_it);
395 }
396
397 // The redirect url may be empty if the URL was invalid.
398 if (response.redirect_url.is_empty())
399 return;
400
401 // If we lost the information about the first hop for some reason.
402 if (!summary) {
403 summary = base::MakeUnique<PageRequestSummary>(main_frame_url);
404 }
405
406 // A redirect will not lead to another OnMainFrameRequest call, so record the
407 // redirect url as a new navigation id and save the initial url.
408 NavigationID navigation_id(response.navigation_id);
409 navigation_id.main_frame_url = response.redirect_url;
410 summary->main_frame_url = response.redirect_url;
411 inflight_navigations_.emplace(navigation_id, std::move(summary));
412 }
413
414 void LoadingDataCollector::OnSubresourceRedirect(
415 const URLRequestSummary& response) {
416 DCHECK_CURRENTLY_ON(BrowserThread::UI);
417
418 if (!config_.is_origin_learning_enabled)
419 return;
420
421 NavigationMap::const_iterator nav_it =
422 inflight_navigations_.find(response.navigation_id);
423 if (nav_it == inflight_navigations_.end())
424 return;
425 auto& page_request_summary = *nav_it->second;
426 page_request_summary.UpdateOrAddToOrigins(response);
427 }
428
429 void LoadingDataCollector::CleanupAbandonedNavigations(
430 const NavigationID& navigation_id) {
431 if (stats_collector_)
432 stats_collector_->CleanupAbandonedStats();
433
434 static const base::TimeDelta max_navigation_age =
435 base::TimeDelta::FromSeconds(config_.max_navigation_lifetime_seconds);
436
437 base::TimeTicks time_now = base::TimeTicks::Now();
438 for (NavigationMap::iterator it = inflight_navigations_.begin();
439 it != inflight_navigations_.end();) {
440 if ((it->first.tab_id == navigation_id.tab_id) ||
441 (time_now - it->first.creation_time > max_navigation_age)) {
442 inflight_navigations_.erase(it++);
443 } else {
444 ++it;
445 }
446 }
142 } 447 }
143 448
144 } // namespace predictors 449 } // namespace predictors
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698