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

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

Powered by Google App Engine
This is Rietveld 408576698