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

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

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