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

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

Issue 10416002: Seculative resource prefetching for URLs CL. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Addressing Domnich's comments. Created 8 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
(Empty)
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.
4
5 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
6
7 #include <map>
8 #include <set>
9 #include <utility>
10
11 #include "base/command_line.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/time.h"
15 #include "chrome/browser/history/history.h"
16 #include "chrome/browser/history/history_notifications.h"
17 #include "chrome/browser/history/in_memory_database.h"
18 #include "chrome/browser/history/url_database.h"
19 #include "chrome/browser/predictors/predictor_database.h"
20 #include "chrome/browser/predictors/predictor_database_factory.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/chrome_notification_types.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/url_constants.h"
25 #include "content/browser/load_from_memory_cache_details.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/navigation_controller.h"
28 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/notification_source.h"
30 #include "content/public/browser/notification_types.h"
31 #include "content/public/browser/resource_request_info.h"
32 #include "content/public/browser/web_contents.h"
33 #include "net/base/mime_util.h"
34 #include "net/http/http_response_headers.h"
35 #include "net/url_request/url_request.h"
36 #include "net/url_request/url_request_context_getter.h"
37
38 using content::BrowserThread;
39
40 namespace {
41
42 // Don't store subresources whose Urls are longer than this.
43 size_t kMaxSubresourceUrlLengthBytes = 1000;
44
45 // For reporting histograms about navigation status.
46 enum NavigationStatus {
47 NAVIGATION_STATUS_COMPLETE = 0,
48 NAVIGATION_STATUS_COMPLETE_ABANDONED = 1,
49 NAVIGATION_STATUS_ABANDONED = 2,
50 NAVIGATION_STATUS_COUNT = 3
51 };
52
53 // For reporting whether a subresource is handled or not, and for what reasons.
54 enum ResourceStatus {
55 RESOURCE_STATUS_HANDLED = 0,
56 RESOURCE_STATUS_NOT_HTTP_PAGE = 1,
57 RESOURCE_STATUS_NOT_HTTP_RESOURCE = 2,
58 RESOURCE_STATUS_UNSUPPORTED_MIME_TYPE = 4,
59 RESOURCE_STATUS_NOT_GET = 8,
60 RESOURCE_STATUS_URL_TOO_LONG = 16,
61 RESOURCE_STATUS_NOT_CACHEABLE = 32,
62 RESOURCE_STATUS_COUNT = 64,
dominich 2012/06/13 21:48:04 COUNT should be MAX
Shishir 2012/06/13 21:56:15 Done.
63 };
64
65 } // namespace
66
67 namespace predictors {
68
69 ResourcePrefetchPredictor::Config::Config()
70 : max_navigation_lifetime_seconds(60),
71 max_urls_to_track(500),
72 min_url_visit_count(3),
73 max_resources_per_entry(50),
74 max_consecutive_misses(3),
75 num_resources_assumed_prefetched(25) {
76 }
77
78 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary()
79 : resource_type(ResourceType::LAST_TYPE),
80 was_cached(false) {
81 }
82
83 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary(
84 const URLRequestSummary& other)
85 : navigation_id(other.navigation_id),
86 resource_url(other.resource_url),
87 resource_type(other.resource_type),
88 mime_type(other.mime_type),
89 was_cached(other.was_cached) {
90 }
91
92 ResourcePrefetchPredictor::URLRequestSummary::~URLRequestSummary() {
93 }
94
95 ResourcePrefetchPredictor::ResourcePrefetchPredictor(
96 const Config& config,
97 Profile* profile)
98 : profile_(profile),
99 config_(config),
100 initialization_state_(NOT_INITIALIZED),
101 tables_(PredictorDatabaseFactory::GetForProfile(
102 profile)->resource_prefetch_tables()) {
103 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104 }
105
106 ResourcePrefetchPredictor::~ResourcePrefetchPredictor() {
107 }
108
109 // static
110 bool ResourcePrefetchPredictor::IsEnabled() {
111 CommandLine* command_line = CommandLine::ForCurrentProcess();
112 return command_line->HasSwitch(
113 switches::kEnableSpeculativeResourcePrefetching);
114 }
115
116 void ResourcePrefetchPredictor::LazilyInitialize() {
117 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
118
119 DCHECK_EQ(initialization_state_, NOT_INITIALIZED);
120 initialization_state_ = INITIALIZING;
121
122 // Request the in-memory database from the history to force it to load so it's
123 // available as soon as possible.
124 HistoryService* history_service =
125 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
126 if (history_service)
127 history_service->InMemoryDatabase();
128
129 // Create local caches using the database as loaded.
130 std::vector<UrlTableRow>* url_rows = new std::vector<UrlTableRow>();
131 BrowserThread::PostTaskAndReply(
132 BrowserThread::DB, FROM_HERE,
133 base::Bind(&ResourcePrefetchPredictorTables::GetAllRows,
134 tables_, url_rows),
135 base::Bind(&ResourcePrefetchPredictor::CreateCaches, AsWeakPtr(),
136 base::Owned(url_rows)));
137 }
138
139 void ResourcePrefetchPredictor::CreateCaches(
140 std::vector<UrlTableRow>* url_rows) {
141 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142
143 DCHECK_EQ(initialization_state_, INITIALIZING);
144 DCHECK(url_table_cache_.empty());
145 DCHECK(inflight_navigations_.empty());
146
147 // Copy the data to local caches.
148 for (UrlTableRowVector::iterator it = url_rows->begin();
149 it != url_rows->end(); ++it) {
150 url_table_cache_[it->main_frame_url].rows.push_back(*it);
151 }
152
153 // Score and sort the database caches.
154 // TODO(shishir): The following would be much more efficient if we used
155 // pointers.
156 for (UrlTableCacheMap::iterator it = url_table_cache_.begin();
157 it != url_table_cache_.end(); ++it) {
158 std::sort(it->second.rows.begin(),
159 it->second.rows.end(),
160 ResourcePrefetchPredictorTables::UrlTableRowSorter());
161 }
162
163 // Add notifications for history loading if it is not ready.
164 if (!profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)) {
165 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_LOADED,
166 content::Source<Profile>(profile_));
167 } else {
168 OnHistoryAndCacheLoaded();
169 }
170 }
171
172 // static
173 bool ResourcePrefetchPredictor::ShouldRecordRequest(
174 net::URLRequest* request,
175 ResourceType::Type resource_type) {
176 return resource_type == ResourceType::MAIN_FRAME &&
177 IsHandledMainPage(request);
178 }
179
180 // static
181 bool ResourcePrefetchPredictor::ShouldRecordResponse(
182 net::URLRequest* response) {
183 const content::ResourceRequestInfo* request_info =
184 content::ResourceRequestInfo::ForRequest(response);
185 if (!request_info)
186 return false;
187
188 return request_info->GetResourceType() == ResourceType::MAIN_FRAME ?
189 IsHandledMainPage(response) : IsHandledSubresource(response);
190 }
191
192 // static
193 bool ResourcePrefetchPredictor::ShouldRecordRedirect(
194 net::URLRequest* response) {
195 const content::ResourceRequestInfo* request_info =
196 content::ResourceRequestInfo::ForRequest(response);
197 if (!request_info)
198 return false;
199
200 return request_info->GetResourceType() == ResourceType::MAIN_FRAME &&
201 IsHandledMainPage(response);
202 }
203
204 // static
205 bool ResourcePrefetchPredictor::IsHandledMainPage(net::URLRequest* request) {
206 return request->original_url().scheme() == chrome::kHttpScheme;
207 }
208
209 // static
210 bool ResourcePrefetchPredictor::IsHandledSubresource(
211 net::URLRequest* response) {
212 int resource_status = 0;
213 if (response->first_party_for_cookies().scheme() != chrome::kHttpScheme)
214 resource_status |= RESOURCE_STATUS_NOT_HTTP_PAGE;
215
216 if (response->original_url().scheme() != chrome::kHttpScheme)
217 resource_status |= RESOURCE_STATUS_NOT_HTTP_RESOURCE;
218
219 std::string mime_type;
220 response->GetMimeType(&mime_type);
221 if (!mime_type.empty() &&
222 !net::IsSupportedImageMimeType(mime_type.c_str()) &&
223 !net::IsSupportedJavascriptMimeType(mime_type.c_str()) &&
224 !net::MatchesMimeType("text/css", mime_type)) {
225 resource_status |= RESOURCE_STATUS_UNSUPPORTED_MIME_TYPE;
226 }
227
228 if (response->method() != "GET")
229 resource_status |= RESOURCE_STATUS_NOT_GET;
230
231 if (response->original_url().spec().length() >
232 kMaxSubresourceUrlLengthBytes) {
233 resource_status |= RESOURCE_STATUS_URL_TOO_LONG;
234 }
235
236 if (!IsCacheable(response))
237 resource_status |= RESOURCE_STATUS_NOT_CACHEABLE;
238
239 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ResourceStatus",
240 resource_status,
241 NAVIGATION_STATUS_COUNT);
dominich 2012/06/13 21:48:04 RESOURCE_STATUS_MAX
Shishir 2012/06/13 21:56:15 Done.
242
243 return resource_status == 0;
244 }
245
246 // static
247 bool ResourcePrefetchPredictor::IsCacheable(const net::URLRequest* response) {
248 if (response->was_cached())
249 return true;
250
251 // For non cached responses, we will ensure that the freshness lifetime is
252 // some sane value.
253 const net::HttpResponseInfo& response_info = response->response_info();
254 base::Time response_time(response_info.response_time);
255 response_time += base::TimeDelta::FromSeconds(1);
256 base::TimeDelta freshness = response_info.headers->GetFreshnessLifetime(
257 response_time);
258 return freshness > base::TimeDelta();
259 }
260
261 // static
262 ResourceType::Type ResourcePrefetchPredictor::GetResourceTypeFromMimeType(
263 const std::string& mime_type,
264 ResourceType::Type fallback) {
265 if (net::IsSupportedImageMimeType(mime_type.c_str()))
266 return ResourceType::IMAGE;
267 else if (net::IsSupportedJavascriptMimeType(mime_type.c_str()))
268 return ResourceType::SCRIPT;
269 else if (net::MatchesMimeType("text/css", mime_type))
270 return ResourceType::STYLESHEET;
271 else
272 return fallback;
273 }
274
275 void ResourcePrefetchPredictor::RecordURLRequest(
276 const URLRequestSummary& request) {
277 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
278
279 if (initialization_state_ == NOT_INITIALIZED) {
280 LazilyInitialize();
281 return;
282 } else if (initialization_state_ != INITIALIZED) {
283 return;
284 }
285 DCHECK_EQ(INITIALIZED, initialization_state_);
286
287 CHECK_EQ(request.resource_type, ResourceType::MAIN_FRAME);
288 OnMainFrameRequest(request);
289 }
290
291 void ResourcePrefetchPredictor::RecordUrlResponse(
292 const URLRequestSummary& response) {
293 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
294 if (initialization_state_ != INITIALIZED)
295 return;
296
297 if (response.resource_type == ResourceType::MAIN_FRAME)
298 OnMainFrameResponse(response);
299 else
300 OnSubresourceResponse(response);
301 }
302
303 void ResourcePrefetchPredictor::RecordUrlRedirect(
304 const URLRequestSummary& response) {
305 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
306 if (initialization_state_ != INITIALIZED)
307 return;
308
309 CHECK_EQ(response.resource_type, ResourceType::MAIN_FRAME);
310 OnMainFrameRedirect(response);
311 }
312
313 void ResourcePrefetchPredictor::OnMainFrameRequest(
314 const URLRequestSummary& request) {
315 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
316 DCHECK_EQ(INITIALIZED, initialization_state_);
317
318 // TODO(shishir): Remove this code after verifying that the same navigation is
319 // not seen multiple times.
320 NavigationMap::const_iterator it =
321 inflight_navigations_.find(request.navigation_id);
322 if (it != inflight_navigations_.end()) {
323 DCHECK(it->first.creation_time != request.navigation_id.creation_time);
324 }
325
326 // Cleanup older navigations.
327 CleanupAbandonedNavigations(request.navigation_id);
328
329 // New empty navigation entry.
330 inflight_navigations_.insert(std::make_pair(
331 request.navigation_id, std::vector<URLRequestSummary>()));
332 }
333
334 void ResourcePrefetchPredictor::OnMainFrameResponse(
335 const URLRequestSummary& response) {
336 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337
338 // TODO(shishir): The prefreshing will be stopped here.
339 }
340
341 void ResourcePrefetchPredictor::OnMainFrameRedirect(
342 const URLRequestSummary& response) {
343 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
344
345 inflight_navigations_.erase(response.navigation_id);
346 }
347
348 void ResourcePrefetchPredictor::OnSubresourceResponse(
349 const URLRequestSummary& response) {
350 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
351
352 if (inflight_navigations_.find(response.navigation_id) ==
353 inflight_navigations_.end()) {
354 return;
355 }
356
357 inflight_navigations_[response.navigation_id].push_back(response);
358 }
359
360 void ResourcePrefetchPredictor::OnSubresourceLoadedFromMemory(
361 const NavigationID& navigation_id,
362 const GURL& resource_url,
363 const std::string& mime_type,
364 ResourceType::Type resource_type) {
365 if (inflight_navigations_.find(navigation_id) == inflight_navigations_.end())
366 return;
367
368 URLRequestSummary summary;
369 summary.navigation_id = navigation_id;
370 summary.resource_url = resource_url;
371 summary.mime_type = mime_type;
372 summary.resource_type = GetResourceTypeFromMimeType(mime_type, resource_type);
373 summary.was_cached = true;
374 inflight_navigations_[navigation_id].push_back(summary);
375 }
376
377 void ResourcePrefetchPredictor::CleanupAbandonedNavigations(
378 const NavigationID& navigation_id) {
379 static const base::TimeDelta max_navigation_age =
380 base::TimeDelta::FromSeconds(config_.max_navigation_lifetime_seconds);
381
382 base::TimeTicks time_now = base::TimeTicks::Now();
383 for (NavigationMap::iterator it = inflight_navigations_.begin();
384 it != inflight_navigations_.end();) {
385 if (it->first.IsSameRenderer(navigation_id) ||
386 (time_now - it->first.creation_time > max_navigation_age)) {
387 inflight_navigations_.erase(it++);
388 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.NavigationStatus",
389 NAVIGATION_STATUS_ABANDONED,
390 NAVIGATION_STATUS_COUNT);
391 } else {
392 ++it;
393 }
394 }
395 }
396
397 void ResourcePrefetchPredictor::Observe(
398 int type,
399 const content::NotificationSource& source,
400 const content::NotificationDetails& details) {
401 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
402
403 switch (type) {
404 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
405 const content::WebContents* web_contents =
406 content::Source<content::WebContents>(source).ptr();
407 NavigationID navigation_id(*web_contents);
408 OnNavigationComplete(navigation_id);
409 break;
410 }
411
412 case content::NOTIFICATION_LOAD_FROM_MEMORY_CACHE: {
413 const LoadFromMemoryCacheDetails* load_details =
414 content::Details<LoadFromMemoryCacheDetails>(details).ptr();
415 const content::WebContents* web_contents =
416 content::Source<content::NavigationController>(
417 source).ptr()->GetWebContents();
418
419 NavigationID navigation_id(*web_contents);
420 OnSubresourceLoadedFromMemory(navigation_id,
421 load_details->url(),
422 load_details->mime_type(),
423 load_details->resource_type());
424 break;
425 }
426
427 case chrome::NOTIFICATION_HISTORY_LOADED: {
428 DCHECK_EQ(initialization_state_, INITIALIZING);
429 notification_registrar_.Remove(this,
430 chrome::NOTIFICATION_HISTORY_LOADED,
431 content::Source<Profile>(profile_));
432 OnHistoryAndCacheLoaded();
433 break;
434 }
435
436 case chrome::NOTIFICATION_HISTORY_URLS_DELETED: {
437 DCHECK_EQ(initialization_state_, INITIALIZED);
438 const content::Details<const history::URLsDeletedDetails>
439 urls_deleted_details =
440 content::Details<const history::URLsDeletedDetails>(details);
441 if (urls_deleted_details->all_history)
442 DeleteAllUrls();
443 else
444 DeleteUrls(urls_deleted_details->rows);
445 break;
446 }
447
448 default:
449 NOTREACHED() << "Unexpected notification observed.";
450 break;
451 }
452 }
453
454 void ResourcePrefetchPredictor::OnHistoryAndCacheLoaded() {
455 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
456 DCHECK_EQ(initialization_state_, INITIALIZING);
457
458 // Update the data with last visit info from in memory history db.
459 HistoryService* history_service =
460 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
461 DCHECK(history_service);
462 history::URLDatabase* url_db = history_service->InMemoryDatabase();
463 if (url_db) {
464 std::vector<GURL> urls_to_delete;
465 for (UrlTableCacheMap::iterator it = url_table_cache_.begin();
466 it != url_table_cache_.end();) {
467 history::URLRow url_row;
468 if (url_db->GetRowForURL(it->first, &url_row) == 0) {
469 urls_to_delete.push_back(it->first);
470 url_table_cache_.erase(it++);
471 } else {
472 it->second.last_visit = url_row.last_visit();
473 ++it;
474 }
475 }
476 if (!urls_to_delete.empty())
477 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
478 base::Bind(&ResourcePrefetchPredictorTables::DeleteRowsForUrls,
479 tables_,
480 urls_to_delete));
481 }
482
483 notification_registrar_.Add(this,
484 content::NOTIFICATION_LOAD_FROM_MEMORY_CACHE,
485 content::NotificationService::AllSources());
486 notification_registrar_.Add(this,
487 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
488 content::NotificationService::AllSources());
489 notification_registrar_.Add(this,
490 chrome::NOTIFICATION_HISTORY_URLS_DELETED,
491 content::Source<Profile>(profile_));
492
493 // TODO(shishir): Maybe listen for notifications for navigation being
494 // abandoned and cleanup the inflight_navigations_.
495
496 initialization_state_ = INITIALIZED;
497 }
498
499 bool ResourcePrefetchPredictor::ShouldTrackUrl(const GURL& url) {
500 if (url_table_cache_.find(url) != url_table_cache_.end())
501 return true;
502
503 HistoryService* history_service =
504 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
505 DCHECK(history_service);
506 history::URLDatabase* url_db = history_service->InMemoryDatabase();
507 if (!url_db)
508 return false;
509
510 history::URLRow url_row;
511 return url_db->GetRowForURL(url, &url_row) != 0 &&
512 url_row.visit_count() >= config_.min_url_visit_count;
513 }
514
515 void ResourcePrefetchPredictor::OnNavigationComplete(
516 const NavigationID& navigation_id) {
517 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
518
519 if (inflight_navigations_.find(navigation_id) ==
520 inflight_navigations_.end()) {
521 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.NavigationStatus",
522 NAVIGATION_STATUS_COMPLETE_ABANDONED,
523 NAVIGATION_STATUS_COUNT);
524 return;
525 }
526
527 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.NavigationStatus",
528 NAVIGATION_STATUS_COMPLETE,
529 NAVIGATION_STATUS_COUNT);
530
531 // Report any stats.
532 MaybeReportAccuracyStats(navigation_id);
533
534 // Update the URL table.
535 const GURL& main_frame_url = navigation_id.main_frame_url;
536 if (ShouldTrackUrl(main_frame_url))
537 LearnUrlNavigation(main_frame_url, inflight_navigations_[navigation_id]);
538
539 // Remove the navigation.
540 inflight_navigations_.erase(navigation_id);
541 }
542
543 void ResourcePrefetchPredictor::LearnUrlNavigation(
544 const GURL& main_frame_url,
545 const std::vector<URLRequestSummary>& new_resources) {
546 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
547
548 if (url_table_cache_.find(main_frame_url) == url_table_cache_.end()) {
549 if (url_table_cache_.size() >= config_.max_urls_to_track)
550 RemoveAnEntryFromUrlDB();
551
552 url_table_cache_[main_frame_url].last_visit = base::Time::Now();
553 int new_resources_size = static_cast<int>(new_resources.size());
554 std::set<GURL> resources_seen;
555 for (int i = 0; i < new_resources_size; ++i) {
556 if (resources_seen.find(new_resources[i].resource_url) !=
557 resources_seen.end()) {
558 continue;
559 }
560 UrlTableRow row_to_add;
561 row_to_add.main_frame_url = main_frame_url;
562 row_to_add.resource_url = new_resources[i].resource_url;
563 row_to_add.resource_type = new_resources[i].resource_type;
564 row_to_add.number_of_hits = 1;
565 row_to_add.average_position = i + 1;
566 url_table_cache_[main_frame_url].rows.push_back(row_to_add);
567 resources_seen.insert(new_resources[i].resource_url);
568 }
569 } else {
570 UrlTableRowVector& old_resources = url_table_cache_[main_frame_url].rows;
571 url_table_cache_[main_frame_url].last_visit = base::Time::Now();
572
573 // Build indices over the data.
574 std::map<GURL, int> new_index, old_index;
575 int new_resources_size = static_cast<int>(new_resources.size());
576 for (int i = 0; i < new_resources_size; ++i) {
577 const URLRequestSummary& summary = new_resources[i];
578 // Take the first occurence of every url.
579 if (new_index.find(summary.resource_url) == new_index.end())
580 new_index[summary.resource_url] = i;
581 }
582 int old_resources_size = static_cast<int>(old_resources.size());
583 for (int i = 0; i < old_resources_size; ++i) {
584 const UrlTableRow& row = old_resources[i];
585 DCHECK(old_index.find(row.resource_url) == old_index.end());
586 old_index[row.resource_url] = i;
587 }
588
589 // Go through the old urls and update their hit/miss counts.
590 for (int i = 0; i < old_resources_size; ++i) {
591 UrlTableRow& old_row = old_resources[i];
592 if (new_index.find(old_row.resource_url) == new_index.end()) {
593 ++old_row.number_of_misses;
594 ++old_row.consecutive_misses;
595 } else {
596 const URLRequestSummary& new_row =
597 new_resources[new_index[old_row.resource_url]];
598
599 // Update the resource type since it could have changed.
600 if (new_row.resource_type != ResourceType::LAST_TYPE)
601 old_row.resource_type = new_row.resource_type;
602
603 int position = new_index[old_row.resource_url] + 1;
604 int total = old_row.number_of_hits + old_row.number_of_misses;
605 old_row.average_position =
606 ((old_row.average_position * total) + position) / (total + 1);
607 ++old_row.number_of_hits;
608 old_row.consecutive_misses = 0;
609 }
610 }
611
612 // Add the new ones that we have not seen before.
613 for (int i = 0; i < new_resources_size; ++i) {
614 const URLRequestSummary& summary = new_resources[i];
615 if (old_index.find(summary.resource_url) != old_index.end())
616 continue;
617
618 // Only need to add new stuff.
619 UrlTableRow row_to_add;
620 row_to_add.main_frame_url = main_frame_url;
621 row_to_add.resource_url = summary.resource_url;
622 row_to_add.resource_type = summary.resource_type;
623 row_to_add.number_of_hits = 1;
624 row_to_add.average_position = i + 1;
625 old_resources.push_back(row_to_add);
626
627 // To ensure we dont add the same url twice.
628 old_index[summary.resource_url] = 0;
629 }
630 }
631
632 // Trim and sort the rows after the update.
633 UrlTableRowVector& rows = url_table_cache_[main_frame_url].rows;
634 for (UrlTableRowVector::iterator it = rows.begin(); it != rows.end();) {
635 it->UpdateScore();
636 if (it->consecutive_misses >= config_.max_consecutive_misses)
637 it = rows.erase(it);
638 else
639 ++it;
640 }
641 std::sort(rows.begin(), rows.end(),
642 ResourcePrefetchPredictorTables::UrlTableRowSorter());
643 if (static_cast<int>(rows.size()) > config_.max_resources_per_entry)
644 rows.resize(config_.max_resources_per_entry);
645
646 BrowserThread::PostTask(
647 BrowserThread::DB, FROM_HERE,
648 base::Bind(&ResourcePrefetchPredictorTables::UpdateRowsForUrl,
649 tables_,
650 main_frame_url,
651 rows));
652 }
653
654 void ResourcePrefetchPredictor::RemoveAnEntryFromUrlDB() {
655 if (url_table_cache_.empty())
656 return;
657
658 // TODO(shishir): Maybe use a heap to do this more efficiently.
659 base::Time oldest_time;
660 GURL url_to_erase;
661 for (UrlTableCacheMap::iterator it = url_table_cache_.begin();
662 it != url_table_cache_.end(); ++it) {
663 if (url_to_erase.is_empty() || it->second.last_visit < oldest_time) {
664 url_to_erase = it->first;
665 oldest_time = it->second.last_visit;
666 }
667 }
668 url_table_cache_.erase(url_to_erase);
669
670 std::vector<GURL> urls_to_delete(1, url_to_erase);
671 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
672 base::Bind(&ResourcePrefetchPredictorTables::DeleteRowsForUrls,
673 tables_,
674 urls_to_delete));
675 }
676
677 void ResourcePrefetchPredictor::MaybeReportAccuracyStats(
678 const NavigationID& navigation_id) const {
679 const GURL& main_frame_url = navigation_id.main_frame_url;
680 DCHECK(inflight_navigations_.find(navigation_id) !=
681 inflight_navigations_.end());
682
683 bool have_predictions_for_url =
684 url_table_cache_.find(main_frame_url) != url_table_cache_.end();
685 UMA_HISTOGRAM_BOOLEAN("ResourcePrefetchPredictor.HavePredictionsForUrl",
686 have_predictions_for_url);
687 if (!have_predictions_for_url)
688 return;
689
690 const std::vector<URLRequestSummary>& actual =
691 inflight_navigations_.find(navigation_id)->second;
692 const UrlTableRowVector& predicted =
693 url_table_cache_.find(main_frame_url)->second.rows;
694
695 std::map<GURL, bool> actual_resources;
696 for (std::vector<URLRequestSummary>::const_iterator it = actual.begin();
697 it != actual.end(); ++it) {
698 actual_resources[it->resource_url] = it->was_cached;
699 }
700
701 int prefetch_cached = 0, prefetch_network = 0, prefetch_missed = 0;
702 int num_assumed_prefetched = std::min(
703 static_cast<int>(predicted.size()),
704 config_.num_resources_assumed_prefetched);
705 for (int i = 0; i < num_assumed_prefetched; ++i) {
706 const UrlTableRow& row = predicted[i];
707 if (actual_resources.find(row.resource_url) == actual_resources.end()) {
708 ++prefetch_missed;
709 } else if (actual_resources[row.resource_url]) {
710 ++prefetch_cached;
711 } else {
712 ++prefetch_network;
713 }
714 }
715
716 UMA_HISTOGRAM_PERCENTAGE(
717 "ResourcePrefetchPredictor.PredictedPrefetchMisses",
718 prefetch_missed * 100.0 / num_assumed_prefetched);
719 UMA_HISTOGRAM_PERCENTAGE(
720 "ResourcePrefetchPredictor.PredictedPrefetchFromCache",
721 prefetch_cached * 100.0 / num_assumed_prefetched);
722 UMA_HISTOGRAM_PERCENTAGE(
723 "ResourcePrefetchPredictor.PredictedPrefetchFromNetwork",
724 prefetch_network * 100.0 / num_assumed_prefetched);
725 }
726
727 void ResourcePrefetchPredictor::DeleteAllUrls() {
728 inflight_navigations_.clear();
729 url_table_cache_.clear();
730
731 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
732 base::Bind(&ResourcePrefetchPredictorTables::DeleteAllRows, tables_));
733 }
734
735 void ResourcePrefetchPredictor::DeleteUrls(const history::URLRows& urls) {
736 std::vector<GURL> urls_to_delete;
737 for (UrlTableCacheMap::iterator it = url_table_cache_.begin();
738 it != url_table_cache_.end();) {
739 if (std::find_if(urls.begin(), urls.end(),
740 history::URLRow::URLRowHasURL(it->first)) != urls.end()) {
741 urls_to_delete.push_back(it->first);
742 url_table_cache_.erase(it++);
743 } else {
744 ++it;
745 }
746 }
747
748 if (!urls_to_delete.empty())
749 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
750 base::Bind(&ResourcePrefetchPredictorTables::DeleteRowsForUrls,
751 tables_,
752 urls_to_delete));
753 }
754
755 void ResourcePrefetchPredictor::SetTablesForTesting(
756 scoped_refptr<ResourcePrefetchPredictorTables> tables) {
757 tables_ = tables;
758 }
759
760 } // namespace predictors
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698