Override server-side simple-cache trial with commandline switches.
[chromium-blink-merge.git] / chrome / browser / predictors / resource_prefetch_predictor.cc
blobb37f3a1496b3bec7c33b72a6002be65bc40fdc18
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"
7 #include <map>
8 #include <set>
9 #include <utility>
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;
44 namespace {
46 // For reporting whether a subresource is handled or not, and for what reasons.
47 enum ResourceStatus {
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
60 // single main frame.
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.
82 enum ReportingEvent {
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",
90 event,
91 NAVIGATION_EVENT_COUNT);
94 } // namespace
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 {
103 public:
104 typedef ResourcePrefetchPredictor::URLRequestSummary URLRequestSummary;
105 typedef base::Callback<void(
106 int, // Visit count.
107 const NavigationID&,
108 const std::vector<URLRequestSummary>&)> VisitInfoCallback;
110 GetUrlVisitCountTask(
111 const NavigationID& navigation_id,
112 std::vector<URLRequestSummary>* requests,
113 VisitInfoCallback callback)
114 : visit_count_(0),
115 navigation_id_(navigation_id),
116 requests_(requests),
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();
126 return true;
129 virtual void DoneRunOnMainThread() OVERRIDE {
130 callback_.Run(visit_count_, navigation_id_, *requests_);
133 private:
134 virtual ~GetUrlVisitCountTask() { }
136 int visit_count_;
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.
147 // static
148 bool ResourcePrefetchPredictor::ShouldRecordRequest(
149 net::URLRequest* request,
150 ResourceType::Type resource_type) {
151 return resource_type == ResourceType::MAIN_FRAME &&
152 IsHandledMainPage(request);
155 // static
156 bool ResourcePrefetchPredictor::ShouldRecordResponse(
157 net::URLRequest* response) {
158 const content::ResourceRequestInfo* request_info =
159 content::ResourceRequestInfo::ForRequest(response);
160 if (!request_info)
161 return false;
163 return request_info->GetResourceType() == ResourceType::MAIN_FRAME ?
164 IsHandledMainPage(response) : IsHandledSubresource(response);
167 // static
168 bool ResourcePrefetchPredictor::ShouldRecordRedirect(
169 net::URLRequest* response) {
170 const content::ResourceRequestInfo* request_info =
171 content::ResourceRequestInfo::ForRequest(response);
172 if (!request_info)
173 return false;
175 return request_info->GetResourceType() == ResourceType::MAIN_FRAME &&
176 IsHandledMainPage(response);
179 // static
180 bool ResourcePrefetchPredictor::IsHandledMainPage(net::URLRequest* request) {
181 return request->original_url().scheme() == chrome::kHttpScheme;
184 // static
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",
218 resource_status,
219 RESOURCE_STATUS_MAX);
221 return resource_status == 0;
224 // static
225 bool ResourcePrefetchPredictor::IsCacheable(const net::URLRequest* response) {
226 if (response->was_cached())
227 return true;
229 // For non cached responses, we will ensure that the freshness lifetime is
230 // some sane value.
231 const net::HttpResponseInfo& response_info = response->response_info();
232 if (!response_info.headers)
233 return false;
234 base::Time response_time(response_info.response_time);
235 response_time += base::TimeDelta::FromSeconds(1);
236 base::TimeDelta freshness = response_info.headers->GetFreshnessLifetime(
237 response_time);
238 return freshness > base::TimeDelta();
241 // static
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;
251 else
252 return fallback;
255 ////////////////////////////////////////////////////////////////////////////////
256 // ResourcePrefetchPredictor structs.
258 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary()
259 : resource_type(ResourceType::LAST_TYPE),
260 was_cached(false) {
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,
291 Profile* profile)
292 : profile_(profile),
293 config_(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)
319 return;
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)
329 return;
331 if (response.resource_type == ResourceType::MAIN_FRAME)
332 OnMainFrameResponse(response);
333 else
334 OnSubresourceResponse(response);
337 void ResourcePrefetchPredictor::RecordUrlRedirect(
338 const URLRequestSummary& response) {
339 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
340 if (initialization_state_ != INITIALIZED)
341 return;
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.";
357 delete result;
361 void ResourcePrefetchPredictor::Observe(
362 int type,
363 const content::NotificationSource& source,
364 const content::NotificationDetails& details) {
365 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
367 switch (type) {
368 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
369 switch (initialization_state_) {
370 case NOT_INITIALIZED:
371 StartInitialization();
372 break;
373 case INITIALIZING:
374 break;
375 case INITIALIZED: {
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);
384 else
385 OnNavigationComplete(navigation_id);
386 break;
388 default:
389 NOTREACHED() << "Unexpected initialization_state_: "
390 << initialization_state_;
392 break;
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,
404 load_details->url,
405 load_details->mime_type,
406 load_details->resource_type);
407 break;
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();
416 break;
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) {
425 DeleteAllUrls();
426 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ReportingEvent",
427 REPORTING_EVENT_ALL_HISTORY_CLEARED,
428 REPORTING_EVENT_COUNT);
429 } else {
430 DeleteUrls(urls_deleted_details->rows);
431 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ReportingEvent",
432 REPORTING_EVENT_PARTIAL_HISTORY_CLEARED,
433 REPORTING_EVENT_COUNT);
435 break;
438 default:
439 NOTREACHED() << "Unexpected notification observed.";
440 break;
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)
473 return;
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
489 // change.
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);
501 return;
504 NavigationID navigation_id(response.navigation_id);
505 navigation_id.main_frame_url = response.redirect_url;
506 inflight_navigations_.insert(std::make_pair(
507 navigation_id,
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()) {
517 return;
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())
529 return;
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);
548 return;
550 RecordNavigationEvent(NAVIGATION_EVENT_ONLOAD_TRACKED_URL);
552 // Report any stats.
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,
560 *(nav_it->second),
561 results_it->second->requests.get());
563 } else {
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,
570 *(nav_it->second),
571 *requests);
572 } else {
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(
588 navigation_id,
589 requests,
590 base::Bind(&ResourcePrefetchPredictor::OnVisitCountLookup,
591 AsWeakPtr())),
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);
600 DCHECK(key_type);
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();
607 if (use_url_data) {
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())
614 return true;
616 bool use_host_data = config_.IsPrefetchingEnabled() ?
617 config_.IsHostPrefetchingEnabled() : config_.IsHostLearningEnabled();
618 if (use_host_data) {
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) {
639 continue;
642 ResourcePrefetcher::Request* req = new ResourcePrefetcher::Request(
643 it->resource_url);
644 requests->push_back(req);
648 void ResourcePrefetchPredictor::StartPrefetching(
649 const NavigationID& navigation_id) {
650 if (!prefetch_manager_.get()) // Prefetching not enabled.
651 return;
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.
659 return;
662 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
663 base::Bind(&ResourcePrefetcherManager::MaybeAddPrefetch,
664 prefetch_manager_,
665 navigation_id,
666 key_type,
667 base::Passed(&requests)));
670 void ResourcePrefetchPredictor::StopPrefetching(
671 const NavigationID& navigation_id) {
672 if (!prefetch_manager_.get()) // Not enabled.
673 return;
675 BrowserThread::PostTask(
676 BrowserThread::IO, FROM_HERE,
677 base::Bind(&ResourcePrefetcherManager::MaybeRemovePrefetch,
678 prefetch_manager_,
679 navigation_id));
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_));
726 } else {
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);
763 } else {
764 ++it;
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)) {
771 delete it->second;
772 results_map_.erase(it++);
773 } else {
774 ++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
790 // in the cache.
791 std::vector<std::string> urls_to_delete, hosts_to_delete;
793 for (history::URLRows::const_iterator it = urls.begin(); it != urls.end();
794 ++it) {
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,
811 tables_,
812 urls_to_delete,
813 hosts_to_delete));
817 void ResourcePrefetchPredictor::RemoveOldestEntryInPrefetchDataMap(
818 PrefetchKeyType key_type,
819 PrefetchDataMap* data_map) {
820 if (data_map->empty())
821 return;
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,
836 tables_,
837 key_to_delete,
838 key_type));
841 void ResourcePrefetchPredictor::OnVisitCountLookup(
842 int visit_count,
843 const NavigationID& navigation_id,
844 const std::vector<URLRequestSummary>& requests) {
845 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
847 UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HistoryVisitCountForUrl",
848 visit_count);
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());
865 } else {
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,
873 requests,
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);
895 else
896 RecordNavigationEvent(NAVIGATION_EVENT_MAIN_FRAME_URL_TOO_LONG);
897 return;
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()) {
915 continue;
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);
925 } else {
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;
951 } else {
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())
972 continue;
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();) {
991 it->UpdateScore();
992 if (it->consecutive_misses >= config_.max_consecutive_misses)
993 it = resources.erase(it);
994 else
995 ++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,
1009 tables_,
1010 key,
1011 key_type));
1012 } else {
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,
1016 std::string());
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,
1022 tables_,
1023 url_data,
1024 host_data));
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(
1053 req->resource_url);
1054 if (actual_it != actual_resources.end()) {
1055 if (actual_it->second) {
1056 req->usage_status =
1057 ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE;
1058 } else {
1059 req->usage_status =
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;
1073 break;
1075 case ResourcePrefetcher::Request::PREFETCH_STATUS_FAILED:
1076 ++prefetch_failed;
1077 break;
1079 case ResourcePrefetcher::Request::PREFETCH_STATUS_FROM_CACHE:
1080 if (req->usage_status ==
1081 ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE)
1082 ++p_cache_a_cache;
1083 else if (req->usage_status ==
1084 ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK)
1085 ++p_cache_a_network;
1086 else
1087 ++p_cache_a_notused;
1088 break;
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;
1097 else
1098 ++p_network_a_notused;
1099 break;
1101 case ResourcePrefetcher::Request::PREFETCH_STATUS_NOT_STARTED:
1102 ++prefetch_not_started;
1103 break;
1105 case ResourcePrefetcher::Request::PREFETCH_STATUS_STARTED:
1106 DLOG(FATAL) << "Invalid prefetch status";
1107 break;
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." :
1115 "Url.";
1117 // Macros to avoid using the STATIC_HISTOGRAM_POINTER_BLOCK in UMA_HISTOGRAM
1118 // definitions.
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)
1163 ++from_network;
1166 // Measure the accuracy at 25, 50 predicted resources.
1167 ReportPredictedAccuracyStatsHelper(key_type, predicted, actual_resources,
1168 from_network, 25);
1169 ReportPredictedAccuracyStatsHelper(key_type, predicted, actual_resources,
1170 from_network, 50);
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)
1183 return;
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()) {
1189 ++prefetch_missed;
1190 } else if (it->second) {
1191 ++prefetch_cached;
1192 } else {
1193 ++prefetch_network;
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
1203 // definitions.
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