1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
11 #include "base/command_line.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/stringprintf.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/time.h"
17 #include "chrome/browser/history/history_database.h"
18 #include "chrome/browser/history/history_db_task.h"
19 #include "chrome/browser/history/history_notifications.h"
20 #include "chrome/browser/history/history_service.h"
21 #include "chrome/browser/history/history_service_factory.h"
22 #include "chrome/browser/predictors/predictor_database.h"
23 #include "chrome/browser/predictors/predictor_database_factory.h"
24 #include "chrome/browser/predictors/resource_prefetcher_manager.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/chrome_notification_types.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/url_constants.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/load_from_memory_cache_details.h"
31 #include "content/public/browser/navigation_controller.h"
32 #include "content/public/browser/notification_service.h"
33 #include "content/public/browser/notification_source.h"
34 #include "content/public/browser/notification_types.h"
35 #include "content/public/browser/resource_request_info.h"
36 #include "content/public/browser/web_contents.h"
37 #include "net/base/mime_util.h"
38 #include "net/http/http_response_headers.h"
39 #include "net/url_request/url_request.h"
40 #include "net/url_request/url_request_context_getter.h"
42 using content::BrowserThread
;
46 // For reporting whether a subresource is handled or not, and for what reasons.
48 RESOURCE_STATUS_HANDLED
= 0,
49 RESOURCE_STATUS_NOT_HTTP_PAGE
= 1,
50 RESOURCE_STATUS_NOT_HTTP_RESOURCE
= 2,
51 RESOURCE_STATUS_UNSUPPORTED_MIME_TYPE
= 4,
52 RESOURCE_STATUS_NOT_GET
= 8,
53 RESOURCE_STATUS_URL_TOO_LONG
= 16,
54 RESOURCE_STATUS_NOT_CACHEABLE
= 32,
55 RESOURCE_STATUS_HEADERS_MISSING
= 64,
56 RESOURCE_STATUS_MAX
= 128,
59 // For reporting various interesting events that occur during the loading of a
61 enum NavigationEvent
{
62 NAVIGATION_EVENT_REQUEST_STARTED
= 0,
63 NAVIGATION_EVENT_REQUEST_REDIRECTED
= 1,
64 NAVIGATION_EVENT_REQUEST_REDIRECTED_EMPTY_URL
= 2,
65 NAVIGATION_EVENT_REQUEST_EXPIRED
= 3,
66 NAVIGATION_EVENT_RESPONSE_STARTED
= 4,
67 NAVIGATION_EVENT_ONLOAD
= 5,
68 NAVIGATION_EVENT_ONLOAD_EMPTY_URL
= 6,
69 NAVIGATION_EVENT_ONLOAD_UNTRACKED_URL
= 7,
70 NAVIGATION_EVENT_ONLOAD_TRACKED_URL
= 8,
71 NAVIGATION_EVENT_SHOULD_TRACK_URL
= 9,
72 NAVIGATION_EVENT_SHOULD_NOT_TRACK_URL
= 10,
73 NAVIGATION_EVENT_URL_TABLE_FULL
= 11,
74 NAVIGATION_EVENT_HAVE_PREDICTIONS_FOR_URL
= 12,
75 NAVIGATION_EVENT_NO_PREDICTIONS_FOR_URL
= 13,
76 NAVIGATION_EVENT_MAIN_FRAME_URL_TOO_LONG
= 14,
77 NAVIGATION_EVENT_HOST_TOO_LONG
= 15,
78 NAVIGATION_EVENT_COUNT
= 16,
81 // For reporting events of interest that are not tied to any navigation.
83 REPORTING_EVENT_ALL_HISTORY_CLEARED
= 0,
84 REPORTING_EVENT_PARTIAL_HISTORY_CLEARED
= 1,
85 REPORTING_EVENT_COUNT
= 2
88 void RecordNavigationEvent(NavigationEvent event
) {
89 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.NavigationEvent",
91 NAVIGATION_EVENT_COUNT
);
96 namespace predictors
{
98 ////////////////////////////////////////////////////////////////////////////////
99 // History lookup task.
101 // Used to fetch the visit count for a URL from the History database.
102 class GetUrlVisitCountTask
: public history::HistoryDBTask
{
104 typedef ResourcePrefetchPredictor::URLRequestSummary URLRequestSummary
;
105 typedef base::Callback
<void(
108 const std::vector
<URLRequestSummary
>&)> VisitInfoCallback
;
110 GetUrlVisitCountTask(
111 const NavigationID
& navigation_id
,
112 std::vector
<URLRequestSummary
>* requests
,
113 VisitInfoCallback callback
)
115 navigation_id_(navigation_id
),
117 callback_(callback
) {
118 DCHECK(requests_
.get());
121 virtual bool RunOnDBThread(history::HistoryBackend
* backend
,
122 history::HistoryDatabase
* db
) OVERRIDE
{
123 history::URLRow url_row
;
124 if (db
->GetRowForURL(navigation_id_
.main_frame_url
, &url_row
))
125 visit_count_
= url_row
.visit_count();
129 virtual void DoneRunOnMainThread() OVERRIDE
{
130 callback_
.Run(visit_count_
, navigation_id_
, *requests_
);
134 virtual ~GetUrlVisitCountTask() { }
137 NavigationID navigation_id_
;
138 scoped_ptr
<std::vector
<URLRequestSummary
> > requests_
;
139 VisitInfoCallback callback_
;
141 DISALLOW_COPY_AND_ASSIGN(GetUrlVisitCountTask
);
144 ////////////////////////////////////////////////////////////////////////////////
145 // ResourcePrefetchPredictor static functions.
148 bool ResourcePrefetchPredictor::ShouldRecordRequest(
149 net::URLRequest
* request
,
150 ResourceType::Type resource_type
) {
151 return resource_type
== ResourceType::MAIN_FRAME
&&
152 IsHandledMainPage(request
);
156 bool ResourcePrefetchPredictor::ShouldRecordResponse(
157 net::URLRequest
* response
) {
158 const content::ResourceRequestInfo
* request_info
=
159 content::ResourceRequestInfo::ForRequest(response
);
163 return request_info
->GetResourceType() == ResourceType::MAIN_FRAME
?
164 IsHandledMainPage(response
) : IsHandledSubresource(response
);
168 bool ResourcePrefetchPredictor::ShouldRecordRedirect(
169 net::URLRequest
* response
) {
170 const content::ResourceRequestInfo
* request_info
=
171 content::ResourceRequestInfo::ForRequest(response
);
175 return request_info
->GetResourceType() == ResourceType::MAIN_FRAME
&&
176 IsHandledMainPage(response
);
180 bool ResourcePrefetchPredictor::IsHandledMainPage(net::URLRequest
* request
) {
181 return request
->original_url().scheme() == chrome::kHttpScheme
;
185 bool ResourcePrefetchPredictor::IsHandledSubresource(
186 net::URLRequest
* response
) {
187 int resource_status
= 0;
188 if (response
->first_party_for_cookies().scheme() != chrome::kHttpScheme
)
189 resource_status
|= RESOURCE_STATUS_NOT_HTTP_PAGE
;
191 if (response
->original_url().scheme() != chrome::kHttpScheme
)
192 resource_status
|= RESOURCE_STATUS_NOT_HTTP_RESOURCE
;
194 std::string mime_type
;
195 response
->GetMimeType(&mime_type
);
196 if (!mime_type
.empty() &&
197 !net::IsSupportedImageMimeType(mime_type
.c_str()) &&
198 !net::IsSupportedJavascriptMimeType(mime_type
.c_str()) &&
199 !net::MatchesMimeType("text/css", mime_type
)) {
200 resource_status
|= RESOURCE_STATUS_UNSUPPORTED_MIME_TYPE
;
203 if (response
->method() != "GET")
204 resource_status
|= RESOURCE_STATUS_NOT_GET
;
206 if (response
->original_url().spec().length() >
207 ResourcePrefetchPredictorTables::kMaxStringLength
) {
208 resource_status
|= RESOURCE_STATUS_URL_TOO_LONG
;
211 if (!response
->response_info().headers
)
212 resource_status
|= RESOURCE_STATUS_HEADERS_MISSING
;
214 if (!IsCacheable(response
))
215 resource_status
|= RESOURCE_STATUS_NOT_CACHEABLE
;
217 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ResourceStatus",
219 RESOURCE_STATUS_MAX
);
221 return resource_status
== 0;
225 bool ResourcePrefetchPredictor::IsCacheable(const net::URLRequest
* response
) {
226 if (response
->was_cached())
229 // For non cached responses, we will ensure that the freshness lifetime is
231 const net::HttpResponseInfo
& response_info
= response
->response_info();
232 if (!response_info
.headers
)
234 base::Time
response_time(response_info
.response_time
);
235 response_time
+= base::TimeDelta::FromSeconds(1);
236 base::TimeDelta freshness
= response_info
.headers
->GetFreshnessLifetime(
238 return freshness
> base::TimeDelta();
242 ResourceType::Type
ResourcePrefetchPredictor::GetResourceTypeFromMimeType(
243 const std::string
& mime_type
,
244 ResourceType::Type fallback
) {
245 if (net::IsSupportedImageMimeType(mime_type
.c_str()))
246 return ResourceType::IMAGE
;
247 else if (net::IsSupportedJavascriptMimeType(mime_type
.c_str()))
248 return ResourceType::SCRIPT
;
249 else if (net::MatchesMimeType("text/css", mime_type
))
250 return ResourceType::STYLESHEET
;
255 ////////////////////////////////////////////////////////////////////////////////
256 // ResourcePrefetchPredictor structs.
258 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary()
259 : resource_type(ResourceType::LAST_TYPE
),
263 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary(
264 const URLRequestSummary
& other
)
265 : navigation_id(other
.navigation_id
),
266 resource_url(other
.resource_url
),
267 resource_type(other
.resource_type
),
268 mime_type(other
.mime_type
),
269 was_cached(other
.was_cached
),
270 redirect_url(other
.redirect_url
) {
273 ResourcePrefetchPredictor::URLRequestSummary::~URLRequestSummary() {
276 ResourcePrefetchPredictor::Result::Result(
277 PrefetchKeyType i_key_type
,
278 ResourcePrefetcher::RequestVector
* i_requests
)
279 : key_type(i_key_type
),
280 requests(i_requests
) {
283 ResourcePrefetchPredictor::Result::~Result() {
286 ////////////////////////////////////////////////////////////////////////////////
287 // ResourcePrefetchPredictor.
289 ResourcePrefetchPredictor::ResourcePrefetchPredictor(
290 const ResourcePrefetchPredictorConfig
& config
,
294 initialization_state_(NOT_INITIALIZED
),
295 tables_(PredictorDatabaseFactory::GetForProfile(
296 profile
)->resource_prefetch_tables()),
297 results_map_deleter_(&results_map_
) {
298 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
300 // Some form of learning has to be enabled.
301 DCHECK(config_
.IsLearningEnabled());
302 if (config_
.IsURLPrefetchingEnabled())
303 DCHECK(config_
.IsURLLearningEnabled());
304 if (config_
.IsHostPrefetchingEnabled())
305 DCHECK(config_
.IsHostLearningEnabled());
307 notification_registrar_
.Add(this,
308 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
,
309 content::NotificationService::AllSources());
312 ResourcePrefetchPredictor::~ResourcePrefetchPredictor() {
315 void ResourcePrefetchPredictor::RecordURLRequest(
316 const URLRequestSummary
& request
) {
317 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
318 if (initialization_state_
!= INITIALIZED
)
321 CHECK_EQ(request
.resource_type
, ResourceType::MAIN_FRAME
);
322 OnMainFrameRequest(request
);
325 void ResourcePrefetchPredictor::RecordUrlResponse(
326 const URLRequestSummary
& response
) {
327 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
328 if (initialization_state_
!= INITIALIZED
)
331 if (response
.resource_type
== ResourceType::MAIN_FRAME
)
332 OnMainFrameResponse(response
);
334 OnSubresourceResponse(response
);
337 void ResourcePrefetchPredictor::RecordUrlRedirect(
338 const URLRequestSummary
& response
) {
339 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
340 if (initialization_state_
!= INITIALIZED
)
343 CHECK_EQ(response
.resource_type
, ResourceType::MAIN_FRAME
);
344 OnMainFrameRedirect(response
);
347 void ResourcePrefetchPredictor::FinishedPrefetchForNavigation(
348 const NavigationID
& navigation_id
,
349 PrefetchKeyType key_type
,
350 ResourcePrefetcher::RequestVector
* requests
) {
351 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
353 Result
* result
= new Result(key_type
, requests
);
354 // Add the results to the results map.
355 if (!results_map_
.insert(std::make_pair(navigation_id
, result
)).second
) {
356 DLOG(FATAL
) << "Returning results for existing navigation.";
361 void ResourcePrefetchPredictor::Observe(
363 const content::NotificationSource
& source
,
364 const content::NotificationDetails
& details
) {
365 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
368 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
: {
369 switch (initialization_state_
) {
370 case NOT_INITIALIZED
:
371 StartInitialization();
376 RecordNavigationEvent(NAVIGATION_EVENT_ONLOAD
);
377 const content::WebContents
* web_contents
=
378 content::Source
<content::WebContents
>(source
).ptr();
379 NavigationID
navigation_id(*web_contents
);
380 // WebContents can return an empty URL if the navigation entry
381 // corresponding to the navigation has not been created yet.
382 if (navigation_id
.main_frame_url
.is_empty())
383 RecordNavigationEvent(NAVIGATION_EVENT_ONLOAD_EMPTY_URL
);
385 OnNavigationComplete(navigation_id
);
389 NOTREACHED() << "Unexpected initialization_state_: "
390 << initialization_state_
;
395 case content::NOTIFICATION_LOAD_FROM_MEMORY_CACHE
: {
396 const content::LoadFromMemoryCacheDetails
* load_details
=
397 content::Details
<content::LoadFromMemoryCacheDetails
>(details
).ptr();
398 const content::WebContents
* web_contents
=
399 content::Source
<content::NavigationController
>(
400 source
).ptr()->GetWebContents();
402 NavigationID
navigation_id(*web_contents
);
403 OnSubresourceLoadedFromMemory(navigation_id
,
405 load_details
->mime_type
,
406 load_details
->resource_type
);
410 case chrome::NOTIFICATION_HISTORY_LOADED
: {
411 DCHECK_EQ(initialization_state_
, INITIALIZING
);
412 notification_registrar_
.Remove(this,
413 chrome::NOTIFICATION_HISTORY_LOADED
,
414 content::Source
<Profile
>(profile_
));
415 OnHistoryAndCacheLoaded();
419 case chrome::NOTIFICATION_HISTORY_URLS_DELETED
: {
420 DCHECK_EQ(initialization_state_
, INITIALIZED
);
421 const content::Details
<const history::URLsDeletedDetails
>
422 urls_deleted_details
=
423 content::Details
<const history::URLsDeletedDetails
>(details
);
424 if (urls_deleted_details
->all_history
) {
426 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ReportingEvent",
427 REPORTING_EVENT_ALL_HISTORY_CLEARED
,
428 REPORTING_EVENT_COUNT
);
430 DeleteUrls(urls_deleted_details
->rows
);
431 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ReportingEvent",
432 REPORTING_EVENT_PARTIAL_HISTORY_CLEARED
,
433 REPORTING_EVENT_COUNT
);
439 NOTREACHED() << "Unexpected notification observed.";
444 void ResourcePrefetchPredictor::Shutdown() {
445 if (prefetch_manager_
) {
446 prefetch_manager_
->ShutdownOnUIThread();
447 prefetch_manager_
= NULL
;
451 void ResourcePrefetchPredictor::OnMainFrameRequest(
452 const URLRequestSummary
& request
) {
453 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
454 DCHECK_EQ(INITIALIZED
, initialization_state_
);
456 RecordNavigationEvent(NAVIGATION_EVENT_REQUEST_STARTED
);
458 StartPrefetching(request
.navigation_id
);
460 // Cleanup older navigations.
461 CleanupAbandonedNavigations(request
.navigation_id
);
463 // New empty navigation entry.
464 inflight_navigations_
.insert(std::make_pair(
465 request
.navigation_id
,
466 make_linked_ptr(new std::vector
<URLRequestSummary
>())));
469 void ResourcePrefetchPredictor::OnMainFrameResponse(
470 const URLRequestSummary
& response
) {
471 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
472 if (initialization_state_
!= INITIALIZED
)
475 RecordNavigationEvent(NAVIGATION_EVENT_RESPONSE_STARTED
);
477 StopPrefetching(response
.navigation_id
);
480 void ResourcePrefetchPredictor::OnMainFrameRedirect(
481 const URLRequestSummary
& response
) {
482 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
484 RecordNavigationEvent(NAVIGATION_EVENT_REQUEST_REDIRECTED
);
486 // TODO(shishir): There are significant gains to be had here if we can use the
487 // start URL in a redirect chain as the key to start prefetching. We can save
488 // of redirect times considerably assuming that the redirect chains do not
491 // Stop any inflight prefetching. Remove the older navigation.
492 StopPrefetching(response
.navigation_id
);
493 inflight_navigations_
.erase(response
.navigation_id
);
495 // A redirect will not lead to another OnMainFrameRequest call, so record the
496 // redirect url as a new navigation.
498 // The redirect url may be empty if the url was invalid.
499 if (response
.redirect_url
.is_empty()) {
500 RecordNavigationEvent(NAVIGATION_EVENT_REQUEST_REDIRECTED_EMPTY_URL
);
504 NavigationID
navigation_id(response
.navigation_id
);
505 navigation_id
.main_frame_url
= response
.redirect_url
;
506 inflight_navigations_
.insert(std::make_pair(
508 make_linked_ptr(new std::vector
<URLRequestSummary
>())));
511 void ResourcePrefetchPredictor::OnSubresourceResponse(
512 const URLRequestSummary
& response
) {
513 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
515 if (inflight_navigations_
.find(response
.navigation_id
) ==
516 inflight_navigations_
.end()) {
520 inflight_navigations_
[response
.navigation_id
]->push_back(response
);
523 void ResourcePrefetchPredictor::OnSubresourceLoadedFromMemory(
524 const NavigationID
& navigation_id
,
525 const GURL
& resource_url
,
526 const std::string
& mime_type
,
527 ResourceType::Type resource_type
) {
528 if (inflight_navigations_
.find(navigation_id
) == inflight_navigations_
.end())
531 URLRequestSummary summary
;
532 summary
.navigation_id
= navigation_id
;
533 summary
.resource_url
= resource_url
;
534 summary
.mime_type
= mime_type
;
535 summary
.resource_type
= GetResourceTypeFromMimeType(mime_type
, resource_type
);
536 summary
.was_cached
= true;
537 inflight_navigations_
[navigation_id
]->push_back(summary
);
540 void ResourcePrefetchPredictor::OnNavigationComplete(
541 const NavigationID
& navigation_id
) {
542 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
544 NavigationMap::const_iterator nav_it
=
545 inflight_navigations_
.find(navigation_id
);
546 if (nav_it
== inflight_navigations_
.end()) {
547 RecordNavigationEvent(NAVIGATION_EVENT_ONLOAD_UNTRACKED_URL
);
550 RecordNavigationEvent(NAVIGATION_EVENT_ONLOAD_TRACKED_URL
);
553 if (prefetch_manager_
.get()) {
554 ResultsMap::iterator results_it
= results_map_
.find(navigation_id
);
555 bool have_prefetch_results
= results_it
!= results_map_
.end();
556 UMA_HISTOGRAM_BOOLEAN("ResourcePrefetchPredictor.HavePrefetchResults",
557 have_prefetch_results
);
558 if (have_prefetch_results
) {
559 ReportAccuracyStats(results_it
->second
->key_type
,
561 results_it
->second
->requests
.get());
564 scoped_ptr
<ResourcePrefetcher::RequestVector
> requests(
565 new ResourcePrefetcher::RequestVector
);
566 PrefetchKeyType key_type
;
567 if (GetPrefetchData(navigation_id
, requests
.get(), &key_type
)){
568 RecordNavigationEvent(NAVIGATION_EVENT_HAVE_PREDICTIONS_FOR_URL
);
569 ReportPredictedAccuracyStats(key_type
,
573 RecordNavigationEvent(NAVIGATION_EVENT_NO_PREDICTIONS_FOR_URL
);
577 // Remove the navigation from the inflight navigations.
578 std::vector
<URLRequestSummary
>* requests
=
579 inflight_navigations_
[navigation_id
].release();
580 inflight_navigations_
.erase(navigation_id
);
582 // Kick off history lookup to determine if we should record the URL.
583 HistoryService
* history_service
= HistoryServiceFactory::GetForProfile(
584 profile_
, Profile::EXPLICIT_ACCESS
);
585 DCHECK(history_service
);
586 history_service
->ScheduleDBTask(
587 new GetUrlVisitCountTask(
590 base::Bind(&ResourcePrefetchPredictor::OnVisitCountLookup
,
592 &history_lookup_consumer_
);
595 bool ResourcePrefetchPredictor::GetPrefetchData(
596 const NavigationID
& navigation_id
,
597 ResourcePrefetcher::RequestVector
* prefetch_requests
,
598 PrefetchKeyType
* key_type
) {
599 DCHECK(prefetch_requests
);
602 *key_type
= PREFETCH_KEY_TYPE_URL
;
603 const GURL
& main_frame_url
= navigation_id
.main_frame_url
;
605 bool use_url_data
= config_
.IsPrefetchingEnabled() ?
606 config_
.IsURLPrefetchingEnabled() : config_
.IsURLLearningEnabled();
608 PrefetchDataMap::const_iterator iterator
=
609 url_table_cache_
->find(main_frame_url
.spec());
610 if (iterator
!= url_table_cache_
->end())
611 PopulatePrefetcherRequest(iterator
->second
, prefetch_requests
);
613 if (!prefetch_requests
->empty())
616 bool use_host_data
= config_
.IsPrefetchingEnabled() ?
617 config_
.IsHostPrefetchingEnabled() : config_
.IsHostLearningEnabled();
619 PrefetchDataMap::const_iterator iterator
=
620 host_table_cache_
->find(main_frame_url
.host());
621 if (iterator
!= host_table_cache_
->end()) {
622 *key_type
= PREFETCH_KEY_TYPE_HOST
;
623 PopulatePrefetcherRequest(iterator
->second
, prefetch_requests
);
627 return !prefetch_requests
->empty();
630 void ResourcePrefetchPredictor::PopulatePrefetcherRequest(
631 const PrefetchData
& data
,
632 ResourcePrefetcher::RequestVector
* requests
) {
633 for (ResourceRows::const_iterator it
= data
.resources
.begin();
634 it
!= data
.resources
.end(); ++it
) {
635 float confidence
= static_cast<float>(it
->number_of_hits
) /
636 (it
->number_of_hits
+ it
->number_of_misses
);
637 if (confidence
< config_
.min_resource_confidence_to_trigger_prefetch
||
638 it
->number_of_hits
< config_
.min_resource_hits_to_trigger_prefetch
) {
642 ResourcePrefetcher::Request
* req
= new ResourcePrefetcher::Request(
644 requests
->push_back(req
);
648 void ResourcePrefetchPredictor::StartPrefetching(
649 const NavigationID
& navigation_id
) {
650 if (!prefetch_manager_
.get()) // Prefetching not enabled.
653 // Prefer URL based data first.
654 scoped_ptr
<ResourcePrefetcher::RequestVector
> requests(
655 new ResourcePrefetcher::RequestVector
);
656 PrefetchKeyType key_type
;
657 if (!GetPrefetchData(navigation_id
, requests
.get(), &key_type
)) {
658 // No prefetching data at host or URL level.
662 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
663 base::Bind(&ResourcePrefetcherManager::MaybeAddPrefetch
,
667 base::Passed(&requests
)));
670 void ResourcePrefetchPredictor::StopPrefetching(
671 const NavigationID
& navigation_id
) {
672 if (!prefetch_manager_
.get()) // Not enabled.
675 BrowserThread::PostTask(
676 BrowserThread::IO
, FROM_HERE
,
677 base::Bind(&ResourcePrefetcherManager::MaybeRemovePrefetch
,
682 void ResourcePrefetchPredictor::StartInitialization() {
683 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
685 DCHECK_EQ(initialization_state_
, NOT_INITIALIZED
);
686 initialization_state_
= INITIALIZING
;
688 // Create local caches using the database as loaded.
689 scoped_ptr
<PrefetchDataMap
> url_data_map(new PrefetchDataMap());
690 scoped_ptr
<PrefetchDataMap
> host_data_map(new PrefetchDataMap());
691 PrefetchDataMap
* url_data_ptr
= url_data_map
.get();
692 PrefetchDataMap
* host_data_ptr
= host_data_map
.get();
694 BrowserThread::PostTaskAndReply(
695 BrowserThread::DB
, FROM_HERE
,
696 base::Bind(&ResourcePrefetchPredictorTables::GetAllData
,
697 tables_
, url_data_ptr
, host_data_ptr
),
698 base::Bind(&ResourcePrefetchPredictor::CreateCaches
, AsWeakPtr(),
699 base::Passed(&url_data_map
), base::Passed(&host_data_map
)));
702 void ResourcePrefetchPredictor::CreateCaches(
703 scoped_ptr
<PrefetchDataMap
> url_data_map
,
704 scoped_ptr
<PrefetchDataMap
> host_data_map
) {
705 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
707 DCHECK_EQ(initialization_state_
, INITIALIZING
);
708 DCHECK(!url_table_cache_
);
709 DCHECK(!host_table_cache_
);
710 DCHECK(inflight_navigations_
.empty());
712 url_table_cache_
.reset(url_data_map
.release());
713 host_table_cache_
.reset(host_data_map
.release());
715 UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.UrlTableMainFrameUrlCount",
716 url_table_cache_
->size());
717 UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HostTableHostCount",
718 host_table_cache_
->size());
720 // Add notifications for history loading if it is not ready.
721 HistoryService
* history_service
= HistoryServiceFactory::GetForProfile(
722 profile_
, Profile::EXPLICIT_ACCESS
);
723 if (!history_service
) {
724 notification_registrar_
.Add(this, chrome::NOTIFICATION_HISTORY_LOADED
,
725 content::Source
<Profile
>(profile_
));
727 OnHistoryAndCacheLoaded();
731 void ResourcePrefetchPredictor::OnHistoryAndCacheLoaded() {
732 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
733 DCHECK_EQ(initialization_state_
, INITIALIZING
);
735 notification_registrar_
.Add(this,
736 content::NOTIFICATION_LOAD_FROM_MEMORY_CACHE
,
737 content::NotificationService::AllSources());
738 notification_registrar_
.Add(this,
739 chrome::NOTIFICATION_HISTORY_URLS_DELETED
,
740 content::Source
<Profile
>(profile_
));
742 // Initialize the prefetch manager only if prefetching is enabled.
743 if (config_
.IsPrefetchingEnabled()) {
744 prefetch_manager_
= new ResourcePrefetcherManager(
745 this, config_
, profile_
->GetRequestContext());
748 initialization_state_
= INITIALIZED
;
751 void ResourcePrefetchPredictor::CleanupAbandonedNavigations(
752 const NavigationID
& navigation_id
) {
753 static const base::TimeDelta max_navigation_age
=
754 base::TimeDelta::FromSeconds(config_
.max_navigation_lifetime_seconds
);
756 base::TimeTicks time_now
= base::TimeTicks::Now();
757 for (NavigationMap::iterator it
= inflight_navigations_
.begin();
758 it
!= inflight_navigations_
.end();) {
759 if (it
->first
.IsSameRenderer(navigation_id
) ||
760 (time_now
- it
->first
.creation_time
> max_navigation_age
)) {
761 inflight_navigations_
.erase(it
++);
762 RecordNavigationEvent(NAVIGATION_EVENT_REQUEST_EXPIRED
);
767 for (ResultsMap::iterator it
= results_map_
.begin();
768 it
!= results_map_
.end();) {
769 if (it
->first
.IsSameRenderer(navigation_id
) ||
770 (time_now
- it
->first
.creation_time
> max_navigation_age
)) {
772 results_map_
.erase(it
++);
779 void ResourcePrefetchPredictor::DeleteAllUrls() {
780 inflight_navigations_
.clear();
781 url_table_cache_
->clear();
782 host_table_cache_
->clear();
784 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
785 base::Bind(&ResourcePrefetchPredictorTables::DeleteAllData
, tables_
));
788 void ResourcePrefetchPredictor::DeleteUrls(const history::URLRows
& urls
) {
789 // Check all the urls in the database and pick out the ones that are present
791 std::vector
<std::string
> urls_to_delete
, hosts_to_delete
;
793 for (history::URLRows::const_iterator it
= urls
.begin(); it
!= urls
.end();
795 const std::string url_spec
= it
->url().spec();
796 if (url_table_cache_
->find(url_spec
) != url_table_cache_
->end()) {
797 urls_to_delete
.push_back(url_spec
);
798 url_table_cache_
->erase(url_spec
);
801 const std::string host
= it
->url().host();
802 if (host_table_cache_
->find(host
) != host_table_cache_
->end()) {
803 hosts_to_delete
.push_back(host
);
804 host_table_cache_
->erase(host
);
808 if (!urls_to_delete
.empty() || !hosts_to_delete
.empty()) {
809 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
810 base::Bind(&ResourcePrefetchPredictorTables::DeleteData
,
817 void ResourcePrefetchPredictor::RemoveOldestEntryInPrefetchDataMap(
818 PrefetchKeyType key_type
,
819 PrefetchDataMap
* data_map
) {
820 if (data_map
->empty())
823 base::Time oldest_time
;
824 std::string key_to_delete
;
825 for (PrefetchDataMap::iterator it
= data_map
->begin();
826 it
!= data_map
->end(); ++it
) {
827 if (key_to_delete
.empty() || it
->second
.last_visit
< oldest_time
) {
828 key_to_delete
= it
->first
;
829 oldest_time
= it
->second
.last_visit
;
833 data_map
->erase(key_to_delete
);
834 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
835 base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint
,
841 void ResourcePrefetchPredictor::OnVisitCountLookup(
843 const NavigationID
& navigation_id
,
844 const std::vector
<URLRequestSummary
>& requests
) {
845 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
847 UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HistoryVisitCountForUrl",
850 // URL level data - merge only if we are already saving the data, or we it
851 // meets the cutoff requirement.
852 const std::string url_spec
= navigation_id
.main_frame_url
.spec();
853 bool already_tracking
= url_table_cache_
->find(url_spec
) !=
854 url_table_cache_
->end();
855 bool should_track_url
= already_tracking
||
856 (visit_count
>= config_
.min_url_visit_count
);
858 if (should_track_url
) {
859 RecordNavigationEvent(NAVIGATION_EVENT_SHOULD_TRACK_URL
);
861 if (config_
.IsURLLearningEnabled()) {
862 LearnNavigation(url_spec
, PREFETCH_KEY_TYPE_URL
, requests
,
863 config_
.max_urls_to_track
, url_table_cache_
.get());
866 RecordNavigationEvent(NAVIGATION_EVENT_SHOULD_NOT_TRACK_URL
);
869 // Host level data - no cutoff, always learn the navigation if enabled.
870 if (config_
.IsHostLearningEnabled()) {
871 LearnNavigation(navigation_id
.main_frame_url
.host(),
872 PREFETCH_KEY_TYPE_HOST
,
874 config_
.max_hosts_to_track
,
875 host_table_cache_
.get());
878 // Remove the navigation from the results map.
879 delete results_map_
[navigation_id
];
880 results_map_
.erase(navigation_id
);
883 void ResourcePrefetchPredictor::LearnNavigation(
884 const std::string
& key
,
885 PrefetchKeyType key_type
,
886 const std::vector
<URLRequestSummary
>& new_resources
,
887 int max_data_map_size
,
888 PrefetchDataMap
* data_map
) {
889 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
891 // If the primary key is too long reject it.
892 if (key
.length() > ResourcePrefetchPredictorTables::kMaxStringLength
) {
893 if (key_type
== PREFETCH_KEY_TYPE_HOST
)
894 RecordNavigationEvent(NAVIGATION_EVENT_HOST_TOO_LONG
);
896 RecordNavigationEvent(NAVIGATION_EVENT_MAIN_FRAME_URL_TOO_LONG
);
900 PrefetchDataMap::iterator cache_entry
= data_map
->find(key
);
901 if (cache_entry
== data_map
->end()) {
902 if (static_cast<int>(data_map
->size()) >= max_data_map_size
) {
903 // The table is full, delete an entry.
904 RemoveOldestEntryInPrefetchDataMap(key_type
, data_map
);
907 cache_entry
= data_map
->insert(std::make_pair(
908 key
, PrefetchData(key_type
, key
))).first
;
909 cache_entry
->second
.last_visit
= base::Time::Now();
910 int new_resources_size
= static_cast<int>(new_resources
.size());
911 std::set
<GURL
> resources_seen
;
912 for (int i
= 0; i
< new_resources_size
; ++i
) {
913 if (resources_seen
.find(new_resources
[i
].resource_url
) !=
914 resources_seen
.end()) {
917 ResourceRow row_to_add
;
918 row_to_add
.resource_url
= new_resources
[i
].resource_url
;
919 row_to_add
.resource_type
= new_resources
[i
].resource_type
;
920 row_to_add
.number_of_hits
= 1;
921 row_to_add
.average_position
= i
+ 1;
922 cache_entry
->second
.resources
.push_back(row_to_add
);
923 resources_seen
.insert(new_resources
[i
].resource_url
);
926 ResourceRows
& old_resources
= cache_entry
->second
.resources
;
927 cache_entry
->second
.last_visit
= base::Time::Now();
929 // Build indices over the data.
930 std::map
<GURL
, int> new_index
, old_index
;
931 int new_resources_size
= static_cast<int>(new_resources
.size());
932 for (int i
= 0; i
< new_resources_size
; ++i
) {
933 const URLRequestSummary
& summary
= new_resources
[i
];
934 // Take the first occurence of every url.
935 if (new_index
.find(summary
.resource_url
) == new_index
.end())
936 new_index
[summary
.resource_url
] = i
;
938 int old_resources_size
= static_cast<int>(old_resources
.size());
939 for (int i
= 0; i
< old_resources_size
; ++i
) {
940 const ResourceRow
& row
= old_resources
[i
];
941 DCHECK(old_index
.find(row
.resource_url
) == old_index
.end());
942 old_index
[row
.resource_url
] = i
;
945 // Go through the old urls and update their hit/miss counts.
946 for (int i
= 0; i
< old_resources_size
; ++i
) {
947 ResourceRow
& old_row
= old_resources
[i
];
948 if (new_index
.find(old_row
.resource_url
) == new_index
.end()) {
949 ++old_row
.number_of_misses
;
950 ++old_row
.consecutive_misses
;
952 const URLRequestSummary
& new_row
=
953 new_resources
[new_index
[old_row
.resource_url
]];
955 // Update the resource type since it could have changed.
956 if (new_row
.resource_type
!= ResourceType::LAST_TYPE
)
957 old_row
.resource_type
= new_row
.resource_type
;
959 int position
= new_index
[old_row
.resource_url
] + 1;
960 int total
= old_row
.number_of_hits
+ old_row
.number_of_misses
;
961 old_row
.average_position
=
962 ((old_row
.average_position
* total
) + position
) / (total
+ 1);
963 ++old_row
.number_of_hits
;
964 old_row
.consecutive_misses
= 0;
968 // Add the new ones that we have not seen before.
969 for (int i
= 0; i
< new_resources_size
; ++i
) {
970 const URLRequestSummary
& summary
= new_resources
[i
];
971 if (old_index
.find(summary
.resource_url
) != old_index
.end())
974 // Only need to add new stuff.
975 ResourceRow row_to_add
;
976 row_to_add
.resource_url
= summary
.resource_url
;
977 row_to_add
.resource_type
= summary
.resource_type
;
978 row_to_add
.number_of_hits
= 1;
979 row_to_add
.average_position
= i
+ 1;
980 old_resources
.push_back(row_to_add
);
982 // To ensure we dont add the same url twice.
983 old_index
[summary
.resource_url
] = 0;
987 // Trim and sort the resources after the update.
988 ResourceRows
& resources
= cache_entry
->second
.resources
;
989 for (ResourceRows::iterator it
= resources
.begin();
990 it
!= resources
.end();) {
992 if (it
->consecutive_misses
>= config_
.max_consecutive_misses
)
993 it
= resources
.erase(it
);
997 std::sort(resources
.begin(), resources
.end(),
998 ResourcePrefetchPredictorTables::ResourceRowSorter());
999 if (static_cast<int>(resources
.size()) > config_
.max_resources_per_entry
)
1000 resources
.resize(config_
.max_resources_per_entry
);
1002 // If the row has no resources, remove it from the cache and delete the
1003 // entry in the database. Else update the database.
1004 if (resources
.size() == 0) {
1005 data_map
->erase(key
);
1006 BrowserThread::PostTask(
1007 BrowserThread::DB
, FROM_HERE
,
1008 base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint
,
1013 bool is_host
= key_type
== PREFETCH_KEY_TYPE_HOST
;
1014 PrefetchData
empty_data(
1015 !is_host
? PREFETCH_KEY_TYPE_HOST
: PREFETCH_KEY_TYPE_URL
,
1017 const PrefetchData
& host_data
= is_host
? cache_entry
->second
: empty_data
;
1018 const PrefetchData
& url_data
= is_host
? empty_data
: cache_entry
->second
;
1019 BrowserThread::PostTask(
1020 BrowserThread::DB
, FROM_HERE
,
1021 base::Bind(&ResourcePrefetchPredictorTables::UpdateData
,
1028 ////////////////////////////////////////////////////////////////////////////////
1029 // Accuracy measurement.
1031 void ResourcePrefetchPredictor::ReportAccuracyStats(
1032 PrefetchKeyType key_type
,
1033 const std::vector
<URLRequestSummary
>& actual
,
1034 ResourcePrefetcher::RequestVector
* prefetched
) const {
1035 // Annotate the results.
1036 std::map
<GURL
, bool> actual_resources
;
1037 for (std::vector
<URLRequestSummary
>::const_iterator it
= actual
.begin();
1038 it
!= actual
.end(); ++it
) {
1039 actual_resources
[it
->resource_url
] = it
->was_cached
;
1042 int prefetch_cancelled
= 0, prefetch_failed
= 0, prefetch_not_started
= 0;
1043 // 'a_' -> actual, 'p_' -> predicted.
1044 int p_cache_a_cache
= 0, p_cache_a_network
= 0, p_cache_a_notused
= 0,
1045 p_network_a_cache
= 0, p_network_a_network
= 0, p_network_a_notused
= 0;
1047 for (ResourcePrefetcher::RequestVector::iterator it
= prefetched
->begin();
1048 it
!= prefetched
->end(); ++it
) {
1049 ResourcePrefetcher::Request
* req
= *it
;
1051 // Set the usage states if the resource was actually used.
1052 std::map
<GURL
, bool>::iterator actual_it
= actual_resources
.find(
1054 if (actual_it
!= actual_resources
.end()) {
1055 if (actual_it
->second
) {
1057 ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE
;
1060 ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK
;
1064 switch (req
->prefetch_status
) {
1066 // TODO(shishir): Add histogram for each cancellation reason.
1067 case ResourcePrefetcher::Request::PREFETCH_STATUS_REDIRECTED
:
1068 case ResourcePrefetcher::Request::PREFETCH_STATUS_AUTH_REQUIRED
:
1069 case ResourcePrefetcher::Request::PREFETCH_STATUS_CERT_REQUIRED
:
1070 case ResourcePrefetcher::Request::PREFETCH_STATUS_CERT_ERROR
:
1071 case ResourcePrefetcher::Request::PREFETCH_STATUS_CANCELLED
:
1072 ++prefetch_cancelled
;
1075 case ResourcePrefetcher::Request::PREFETCH_STATUS_FAILED
:
1079 case ResourcePrefetcher::Request::PREFETCH_STATUS_FROM_CACHE
:
1080 if (req
->usage_status
==
1081 ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE
)
1083 else if (req
->usage_status
==
1084 ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK
)
1085 ++p_cache_a_network
;
1087 ++p_cache_a_notused
;
1090 case ResourcePrefetcher::Request::PREFETCH_STATUS_FROM_NETWORK
:
1091 if (req
->usage_status
==
1092 ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE
)
1093 ++p_network_a_cache
;
1094 else if (req
->usage_status
==
1095 ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK
)
1096 ++p_network_a_network
;
1098 ++p_network_a_notused
;
1101 case ResourcePrefetcher::Request::PREFETCH_STATUS_NOT_STARTED
:
1102 ++prefetch_not_started
;
1105 case ResourcePrefetcher::Request::PREFETCH_STATUS_STARTED
:
1106 DLOG(FATAL
) << "Invalid prefetch status";
1111 int total_prefetched
= p_cache_a_cache
+ p_cache_a_network
+ p_cache_a_notused
1112 + p_network_a_cache
+ p_network_a_network
+ p_network_a_notused
;
1114 std::string histogram_type
= key_type
== PREFETCH_KEY_TYPE_HOST
? "Host." :
1117 // Macros to avoid using the STATIC_HISTOGRAM_POINTER_BLOCK in UMA_HISTOGRAM
1119 #define RPP_HISTOGRAM_PERCENTAGE(suffix, value) \
1121 std::string name = "ResourcePrefetchPredictor." + histogram_type + suffix; \
1122 std::string g_name = "ResourcePrefetchPredictor." + std::string(suffix); \
1123 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet( \
1124 name, 1, 101, 102, base::Histogram::kUmaTargetedHistogramFlag); \
1125 histogram->Add(value); \
1126 UMA_HISTOGRAM_PERCENTAGE(g_name, value); \
1129 RPP_HISTOGRAM_PERCENTAGE("PrefetchCancelled",
1130 prefetch_cancelled
* 100.0 / total_prefetched
);
1131 RPP_HISTOGRAM_PERCENTAGE("PrefetchFailed",
1132 prefetch_failed
* 100.0 / total_prefetched
);
1133 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromCacheUsedFromCache",
1134 p_cache_a_cache
* 100.0 / total_prefetched
);
1135 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromCacheUsedFromNetwork",
1136 p_cache_a_network
* 100.0 / total_prefetched
);
1137 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromCacheNotUsed",
1138 p_cache_a_notused
* 100.0 / total_prefetched
);
1139 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromNetworkUsedFromCache",
1140 p_network_a_cache
* 100.0 / total_prefetched
);
1141 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromNetworkUsedFromNetwork",
1142 p_network_a_network
* 100.0 / total_prefetched
);
1143 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromNetworkNotUsed",
1144 p_network_a_notused
* 100.0 / total_prefetched
);
1146 RPP_HISTOGRAM_PERCENTAGE(
1147 "PrefetchNotStarted",
1148 prefetch_not_started
* 100.0 / (prefetch_not_started
+ total_prefetched
));
1150 #undef RPP_HISTOGRAM_PERCENTAGE
1153 void ResourcePrefetchPredictor::ReportPredictedAccuracyStats(
1154 PrefetchKeyType key_type
,
1155 const std::vector
<URLRequestSummary
>& actual
,
1156 const ResourcePrefetcher::RequestVector
& predicted
) const {
1157 std::map
<GURL
, bool> actual_resources
;
1158 int from_network
= 0;
1159 for (std::vector
<URLRequestSummary
>::const_iterator it
= actual
.begin();
1160 it
!= actual
.end(); ++it
) {
1161 actual_resources
[it
->resource_url
] = it
->was_cached
;
1162 if (!it
->was_cached
)
1166 // Measure the accuracy at 25, 50 predicted resources.
1167 ReportPredictedAccuracyStatsHelper(key_type
, predicted
, actual_resources
,
1169 ReportPredictedAccuracyStatsHelper(key_type
, predicted
, actual_resources
,
1173 void ResourcePrefetchPredictor::ReportPredictedAccuracyStatsHelper(
1174 PrefetchKeyType key_type
,
1175 const ResourcePrefetcher::RequestVector
& predicted
,
1176 const std::map
<GURL
, bool>& actual
,
1177 int total_resources_fetched_from_network
,
1178 int max_assumed_prefetched
) const {
1179 int prefetch_cached
= 0, prefetch_network
= 0, prefetch_missed
= 0;
1180 int num_assumed_prefetched
= std::min(static_cast<int>(predicted
.size()),
1181 max_assumed_prefetched
);
1182 if (num_assumed_prefetched
== 0)
1185 for (int i
= 0; i
< num_assumed_prefetched
; ++i
) {
1186 const ResourcePrefetcher::Request
& row
= *(predicted
[i
]);
1187 std::map
<GURL
, bool>::const_iterator it
= actual
.find(row
.resource_url
);
1188 if (it
== actual
.end()) {
1190 } else if (it
->second
) {
1197 std::string prefix
= key_type
== PREFETCH_KEY_TYPE_HOST
?
1198 "ResourcePrefetchPredictor.Host.Predicted" :
1199 "ResourcePrefetchPredictor.Url.Predicted";
1200 std::string suffix
= "_" + base::IntToString(max_assumed_prefetched
);
1202 // Macros to avoid using the STATIC_HISTOGRAM_POINTER_BLOCK in UMA_HISTOGRAM
1204 #define RPP_PREDICTED_HISTOGRAM_COUNTS(name, value) \
1206 std::string full_name = prefix + name + suffix; \
1207 base::HistogramBase* histogram = base::Histogram::FactoryGet( \
1208 full_name, 1, 1000000, 50, \
1209 base::Histogram::kUmaTargetedHistogramFlag); \
1210 histogram->Add(value); \
1213 #define RPP_PREDICTED_HISTOGRAM_PERCENTAGE(name, value) \
1215 std::string full_name = prefix + name + suffix; \
1216 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet( \
1217 full_name, 1, 101, 102, base::Histogram::kUmaTargetedHistogramFlag); \
1218 histogram->Add(value); \
1221 RPP_PREDICTED_HISTOGRAM_COUNTS("PrefetchCount", num_assumed_prefetched
);
1222 RPP_PREDICTED_HISTOGRAM_COUNTS("PrefetchMisses_Count", prefetch_missed
);
1223 RPP_PREDICTED_HISTOGRAM_COUNTS("PrefetchFromCache_Count", prefetch_cached
);
1224 RPP_PREDICTED_HISTOGRAM_COUNTS("PrefetchFromNetwork_Count", prefetch_network
);
1226 RPP_PREDICTED_HISTOGRAM_PERCENTAGE(
1227 "PrefetchMisses_PercentOfTotalPrefetched",
1228 prefetch_missed
* 100.0 / num_assumed_prefetched
);
1229 RPP_PREDICTED_HISTOGRAM_PERCENTAGE(
1230 "PrefetchFromCache_PercentOfTotalPrefetched",
1231 prefetch_cached
* 100.0 / num_assumed_prefetched
);
1232 RPP_PREDICTED_HISTOGRAM_PERCENTAGE(
1233 "PrefetchFromNetwork_PercentOfTotalPrefetched",
1234 prefetch_network
* 100.0 / num_assumed_prefetched
);
1236 // Measure the ratio of total number of resources prefetched from network vs
1237 // the total number of resources fetched by the page from the network.
1238 if (total_resources_fetched_from_network
> 0) {
1239 RPP_PREDICTED_HISTOGRAM_PERCENTAGE(
1240 "PrefetchFromNetworkPercentOfTotalFromNetwork",
1241 prefetch_network
* 100.0 / total_resources_fetched_from_network
);
1244 #undef RPP_PREDICTED_HISTOGRAM_PERCENTAGE
1245 #undef RPP_PREDICTED_HISTOGRAM_COUNTS
1248 } // namespace predictors