| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/predictors/resource_prefetch_predictor.h" | 5 #include "chrome/browser/predictors/resource_prefetch_predictor.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/macros.h" | 12 #include "base/macros.h" |
| 13 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
| 14 #include "base/metrics/sparse_histogram.h" | 14 #include "base/metrics/sparse_histogram.h" |
| 15 #include "base/strings/string_number_conversions.h" | 15 #include "base/strings/string_number_conversions.h" |
| 16 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
| 17 #include "base/time/time.h" | 17 #include "base/time/time.h" |
| 18 #include "chrome/browser/history/history_service_factory.h" | 18 #include "chrome/browser/history/history_service_factory.h" |
| 19 #include "chrome/browser/predictors/predictor_database.h" | 19 #include "chrome/browser/predictors/predictor_database.h" |
| 20 #include "chrome/browser/predictors/predictor_database_factory.h" | 20 #include "chrome/browser/predictors/predictor_database_factory.h" |
| 21 #include "chrome/browser/predictors/resource_prefetch_predictor.pb.h" |
| 21 #include "chrome/browser/predictors/resource_prefetcher_manager.h" | 22 #include "chrome/browser/predictors/resource_prefetcher_manager.h" |
| 22 #include "chrome/browser/profiles/profile.h" | 23 #include "chrome/browser/profiles/profile.h" |
| 23 #include "chrome/common/chrome_switches.h" | 24 #include "chrome/common/chrome_switches.h" |
| 24 #include "chrome/common/url_constants.h" | 25 #include "chrome/common/url_constants.h" |
| 25 #include "components/history/core/browser/history_database.h" | 26 #include "components/history/core/browser/history_database.h" |
| 26 #include "components/history/core/browser/history_db_task.h" | 27 #include "components/history/core/browser/history_db_task.h" |
| 27 #include "components/history/core/browser/history_service.h" | 28 #include "components/history/core/browser/history_service.h" |
| 28 #include "components/mime_util/mime_util.h" | 29 #include "components/mime_util/mime_util.h" |
| 29 #include "content/public/browser/browser_thread.h" | 30 #include "content/public/browser/browser_thread.h" |
| 30 #include "content/public/browser/navigation_controller.h" | 31 #include "content/public/browser/navigation_controller.h" |
| 31 #include "content/public/browser/resource_request_info.h" | 32 #include "content/public/browser/resource_request_info.h" |
| 32 #include "content/public/browser/web_contents.h" | 33 #include "content/public/browser/web_contents.h" |
| 33 #include "net/base/mime_util.h" | 34 #include "net/base/mime_util.h" |
| 34 #include "net/base/network_change_notifier.h" | 35 #include "net/base/network_change_notifier.h" |
| 35 #include "net/http/http_response_headers.h" | 36 #include "net/http/http_response_headers.h" |
| 36 #include "net/url_request/url_request.h" | 37 #include "net/url_request/url_request.h" |
| 37 #include "net/url_request/url_request_context_getter.h" | 38 #include "net/url_request/url_request_context_getter.h" |
| 38 | 39 |
| 39 using content::BrowserThread; | 40 using content::BrowserThread; |
| 41 using chrome_browser_predictors::ResourceData; |
| 40 | 42 |
| 41 namespace { | 43 namespace { |
| 42 | 44 |
| 43 // Sorted by decreasing likelihood according to HTTP archive. | 45 // Sorted by decreasing likelihood according to HTTP archive. |
| 44 const char* kFontMimeTypes[] = {"font/woff2", | 46 const char* kFontMimeTypes[] = {"font/woff2", |
| 45 "application/x-font-woff", | 47 "application/x-font-woff", |
| 46 "application/font-woff", | 48 "application/font-woff", |
| 47 "application/font-woff2", | 49 "application/font-woff2", |
| 48 "font/x-woff", | 50 "font/x-woff", |
| 49 "application/x-font-ttf", | 51 "application/x-font-ttf", |
| (...skipping 650 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 700 PopulatePrefetcherRequest(iterator->second, prefetch_requests); | 702 PopulatePrefetcherRequest(iterator->second, prefetch_requests); |
| 701 } | 703 } |
| 702 } | 704 } |
| 703 | 705 |
| 704 return !prefetch_requests->empty(); | 706 return !prefetch_requests->empty(); |
| 705 } | 707 } |
| 706 | 708 |
| 707 void ResourcePrefetchPredictor::PopulatePrefetcherRequest( | 709 void ResourcePrefetchPredictor::PopulatePrefetcherRequest( |
| 708 const PrefetchData& data, | 710 const PrefetchData& data, |
| 709 ResourcePrefetcher::RequestVector* requests) { | 711 ResourcePrefetcher::RequestVector* requests) { |
| 710 for (ResourceRows::const_iterator it = data.resources.begin(); | 712 for (const ResourceData& resource : data.resources) { |
| 711 it != data.resources.end(); ++it) { | 713 float confidence = |
| 712 float confidence = static_cast<float>(it->number_of_hits) / | 714 static_cast<float>(resource.number_of_hits()) / |
| 713 (it->number_of_hits + it->number_of_misses); | 715 (resource.number_of_hits() + resource.number_of_misses()); |
| 714 if (confidence < config_.min_resource_confidence_to_trigger_prefetch || | 716 if (confidence < config_.min_resource_confidence_to_trigger_prefetch || |
| 715 it->number_of_hits < config_.min_resource_hits_to_trigger_prefetch) { | 717 resource.number_of_hits() < |
| 718 config_.min_resource_hits_to_trigger_prefetch) { |
| 716 continue; | 719 continue; |
| 717 } | 720 } |
| 718 | 721 |
| 719 ResourcePrefetcher::Request* req = new ResourcePrefetcher::Request( | 722 ResourcePrefetcher::Request* req = |
| 720 it->resource_url); | 723 new ResourcePrefetcher::Request(GURL(resource.resource_url())); |
| 721 requests->push_back(req); | 724 requests->push_back(req); |
| 722 } | 725 } |
| 723 } | 726 } |
| 724 | 727 |
| 725 void ResourcePrefetchPredictor::StartPrefetching( | 728 void ResourcePrefetchPredictor::StartPrefetching( |
| 726 const NavigationID& navigation_id) { | 729 const NavigationID& navigation_id) { |
| 727 if (!prefetch_manager_.get()) // Prefetching not enabled. | 730 if (!prefetch_manager_.get()) // Prefetching not enabled. |
| 728 return; | 731 return; |
| 729 | 732 |
| 730 // Prefer URL based data first. | 733 // Prefer URL based data first. |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 939 } | 942 } |
| 940 | 943 |
| 941 void ResourcePrefetchPredictor::LearnNavigation( | 944 void ResourcePrefetchPredictor::LearnNavigation( |
| 942 const std::string& key, | 945 const std::string& key, |
| 943 PrefetchKeyType key_type, | 946 PrefetchKeyType key_type, |
| 944 const std::vector<URLRequestSummary>& new_resources, | 947 const std::vector<URLRequestSummary>& new_resources, |
| 945 size_t max_data_map_size, | 948 size_t max_data_map_size, |
| 946 PrefetchDataMap* data_map) { | 949 PrefetchDataMap* data_map) { |
| 947 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 950 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 948 | 951 |
| 952 using ResourceType = ::chrome_browser_predictors::ResourceData_ResourceType; |
| 953 using Priority = ::chrome_browser_predictors::ResourceData_Priority; |
| 954 |
| 949 // If the primary key is too long reject it. | 955 // If the primary key is too long reject it. |
| 950 if (key.length() > ResourcePrefetchPredictorTables::kMaxStringLength) { | 956 if (key.length() > ResourcePrefetchPredictorTables::kMaxStringLength) { |
| 951 if (key_type == PREFETCH_KEY_TYPE_HOST) | 957 if (key_type == PREFETCH_KEY_TYPE_HOST) |
| 952 RecordNavigationEvent(NAVIGATION_EVENT_HOST_TOO_LONG); | 958 RecordNavigationEvent(NAVIGATION_EVENT_HOST_TOO_LONG); |
| 953 else | 959 else |
| 954 RecordNavigationEvent(NAVIGATION_EVENT_MAIN_FRAME_URL_TOO_LONG); | 960 RecordNavigationEvent(NAVIGATION_EVENT_MAIN_FRAME_URL_TOO_LONG); |
| 955 return; | 961 return; |
| 956 } | 962 } |
| 957 | 963 |
| 958 PrefetchDataMap::iterator cache_entry = data_map->find(key); | 964 PrefetchDataMap::iterator cache_entry = data_map->find(key); |
| 959 if (cache_entry == data_map->end()) { | 965 if (cache_entry == data_map->end()) { |
| 960 if (data_map->size() >= max_data_map_size) { | 966 if (data_map->size() >= max_data_map_size) { |
| 961 // The table is full, delete an entry. | 967 // The table is full, delete an entry. |
| 962 RemoveOldestEntryInPrefetchDataMap(key_type, data_map); | 968 RemoveOldestEntryInPrefetchDataMap(key_type, data_map); |
| 963 } | 969 } |
| 964 | 970 |
| 965 cache_entry = data_map->insert(std::make_pair( | 971 cache_entry = data_map->insert(std::make_pair( |
| 966 key, PrefetchData(key_type, key))).first; | 972 key, PrefetchData(key_type, key))).first; |
| 967 cache_entry->second.last_visit = base::Time::Now(); | 973 cache_entry->second.last_visit = base::Time::Now(); |
| 968 size_t new_resources_size = new_resources.size(); | 974 size_t new_resources_size = new_resources.size(); |
| 969 std::set<GURL> resources_seen; | 975 std::set<GURL> resources_seen; |
| 970 for (size_t i = 0; i < new_resources_size; ++i) { | 976 for (size_t i = 0; i < new_resources_size; ++i) { |
| 971 if (resources_seen.find(new_resources[i].resource_url) != | 977 if (resources_seen.find(new_resources[i].resource_url) != |
| 972 resources_seen.end()) { | 978 resources_seen.end()) { |
| 973 continue; | 979 continue; |
| 974 } | 980 } |
| 975 ResourceRow row_to_add; | 981 ResourceData resource_to_add; |
| 976 row_to_add.resource_url = new_resources[i].resource_url; | 982 resource_to_add.set_resource_url(new_resources[i].resource_url.spec()); |
| 977 row_to_add.resource_type = new_resources[i].resource_type; | 983 resource_to_add.set_resource_type( |
| 978 row_to_add.number_of_hits = 1; | 984 static_cast<ResourceType>(new_resources[i].resource_type)); |
| 979 row_to_add.average_position = i + 1; | 985 resource_to_add.set_number_of_hits(1); |
| 980 row_to_add.priority = new_resources[i].priority; | 986 resource_to_add.set_average_position(i + 1); |
| 981 row_to_add.has_validators = new_resources[i].has_validators; | 987 resource_to_add.set_priority( |
| 982 row_to_add.always_revalidate = new_resources[i].always_revalidate; | 988 static_cast<Priority>(new_resources[i].priority)); |
| 983 cache_entry->second.resources.push_back(row_to_add); | 989 resource_to_add.set_has_validators(new_resources[i].has_validators); |
| 990 resource_to_add.set_always_revalidate(new_resources[i].always_revalidate); |
| 991 cache_entry->second.resources.push_back(resource_to_add); |
| 984 resources_seen.insert(new_resources[i].resource_url); | 992 resources_seen.insert(new_resources[i].resource_url); |
| 985 } | 993 } |
| 986 } else { | 994 } else { |
| 987 ResourceRows& old_resources = cache_entry->second.resources; | 995 std::vector<ResourceData>& old_resources = cache_entry->second.resources; |
| 988 cache_entry->second.last_visit = base::Time::Now(); | 996 cache_entry->second.last_visit = base::Time::Now(); |
| 989 | 997 |
| 990 // Build indices over the data. | 998 // Build indices over the data. |
| 991 std::map<GURL, int> new_index, old_index; | 999 std::map<GURL, int> new_index, old_index; |
| 992 int new_resources_size = static_cast<int>(new_resources.size()); | 1000 int new_resources_size = static_cast<int>(new_resources.size()); |
| 993 for (int i = 0; i < new_resources_size; ++i) { | 1001 for (int i = 0; i < new_resources_size; ++i) { |
| 994 const URLRequestSummary& summary = new_resources[i]; | 1002 const URLRequestSummary& summary = new_resources[i]; |
| 995 // Take the first occurence of every url. | 1003 // Take the first occurence of every url. |
| 996 if (new_index.find(summary.resource_url) == new_index.end()) | 1004 if (new_index.find(summary.resource_url) == new_index.end()) |
| 997 new_index[summary.resource_url] = i; | 1005 new_index[summary.resource_url] = i; |
| 998 } | 1006 } |
| 999 int old_resources_size = static_cast<int>(old_resources.size()); | 1007 int old_resources_size = static_cast<int>(old_resources.size()); |
| 1000 for (int i = 0; i < old_resources_size; ++i) { | 1008 for (int i = 0; i < old_resources_size; ++i) { |
| 1001 const ResourceRow& row = old_resources[i]; | 1009 const ResourceData& resource = old_resources[i]; |
| 1002 DCHECK(old_index.find(row.resource_url) == old_index.end()); | 1010 GURL resource_url(resource.resource_url()); |
| 1003 old_index[row.resource_url] = i; | 1011 DCHECK(old_index.find(resource_url) == old_index.end()); |
| 1012 old_index[resource_url] = i; |
| 1004 } | 1013 } |
| 1005 | 1014 |
| 1006 // Go through the old urls and update their hit/miss counts. | 1015 // Go through the old urls and update their hit/miss counts. |
| 1007 for (int i = 0; i < old_resources_size; ++i) { | 1016 for (int i = 0; i < old_resources_size; ++i) { |
| 1008 ResourceRow& old_row = old_resources[i]; | 1017 ResourceData& old_resource = old_resources[i]; |
| 1009 if (new_index.find(old_row.resource_url) == new_index.end()) { | 1018 GURL resource_url(old_resource.resource_url()); |
| 1010 ++old_row.number_of_misses; | 1019 if (new_index.find(resource_url) == new_index.end()) { |
| 1011 ++old_row.consecutive_misses; | 1020 old_resource.set_number_of_misses(old_resource.number_of_misses() + 1); |
| 1021 old_resource.set_consecutive_misses(old_resource.consecutive_misses() + |
| 1022 1); |
| 1012 } else { | 1023 } else { |
| 1013 const URLRequestSummary& new_row = | 1024 const URLRequestSummary& new_summary = |
| 1014 new_resources[new_index[old_row.resource_url]]; | 1025 new_resources[new_index[GURL(old_resource.resource_url())]]; |
| 1015 | 1026 |
| 1016 // Update the resource type since it could have changed. | 1027 // Update the resource type and priority. |
| 1017 if (new_row.resource_type != content::RESOURCE_TYPE_LAST_TYPE) | 1028 if (new_summary.resource_type != content::RESOURCE_TYPE_LAST_TYPE) |
| 1018 old_row.resource_type = new_row.resource_type; | 1029 old_resource.set_resource_type( |
| 1030 static_cast<ResourceType>(new_summary.resource_type)); |
| 1031 old_resource.set_priority(static_cast<Priority>(new_summary.priority)); |
| 1019 | 1032 |
| 1020 old_row.priority = new_row.priority; | 1033 int position = new_index[resource_url] + 1; |
| 1021 | 1034 int total = |
| 1022 int position = new_index[old_row.resource_url] + 1; | 1035 old_resource.number_of_hits() + old_resource.number_of_misses(); |
| 1023 int total = old_row.number_of_hits + old_row.number_of_misses; | 1036 old_resource.set_average_position( |
| 1024 old_row.average_position = | 1037 ((old_resource.average_position() * total) + position) / |
| 1025 ((old_row.average_position * total) + position) / (total + 1); | 1038 (total + 1)); |
| 1026 ++old_row.number_of_hits; | 1039 old_resource.set_number_of_hits(old_resource.number_of_hits() + 1); |
| 1027 old_row.consecutive_misses = 0; | 1040 old_resource.set_consecutive_misses(0); |
| 1028 } | 1041 } |
| 1029 } | 1042 } |
| 1030 | 1043 |
| 1031 // Add the new ones that we have not seen before. | 1044 // Add the new ones that we have not seen before. |
| 1032 for (int i = 0; i < new_resources_size; ++i) { | 1045 for (int i = 0; i < new_resources_size; ++i) { |
| 1033 const URLRequestSummary& summary = new_resources[i]; | 1046 const URLRequestSummary& summary = new_resources[i]; |
| 1034 if (old_index.find(summary.resource_url) != old_index.end()) | 1047 if (old_index.find(summary.resource_url) != old_index.end()) |
| 1035 continue; | 1048 continue; |
| 1036 | 1049 |
| 1037 // Only need to add new stuff. | 1050 // Only need to add new stuff. |
| 1038 ResourceRow row_to_add; | 1051 ResourceData resource_to_add; |
| 1039 row_to_add.resource_url = summary.resource_url; | 1052 resource_to_add.set_resource_url(summary.resource_url.spec()); |
| 1040 row_to_add.resource_type = summary.resource_type; | 1053 resource_to_add.set_resource_type( |
| 1041 row_to_add.number_of_hits = 1; | 1054 static_cast<ResourceType>(summary.resource_type)); |
| 1042 row_to_add.average_position = i + 1; | 1055 resource_to_add.set_number_of_hits(1); |
| 1043 row_to_add.priority = summary.priority; | 1056 resource_to_add.set_average_position(i + 1); |
| 1044 row_to_add.has_validators = new_resources[i].has_validators; | 1057 resource_to_add.set_priority(static_cast<Priority>(summary.priority)); |
| 1045 row_to_add.always_revalidate = new_resources[i].always_revalidate; | 1058 resource_to_add.set_has_validators(new_resources[i].has_validators); |
| 1046 old_resources.push_back(row_to_add); | 1059 resource_to_add.set_always_revalidate(new_resources[i].always_revalidate); |
| 1060 old_resources.push_back(resource_to_add); |
| 1047 | 1061 |
| 1048 // To ensure we dont add the same url twice. | 1062 // To ensure we dont add the same url twice. |
| 1049 old_index[summary.resource_url] = 0; | 1063 old_index[summary.resource_url] = 0; |
| 1050 } | 1064 } |
| 1051 } | 1065 } |
| 1052 | 1066 |
| 1053 // Trim and sort the resources after the update. | 1067 // Trim and sort the resources after the update. |
| 1054 ResourceRows& resources = cache_entry->second.resources; | 1068 std::vector<ResourceData>& resources = cache_entry->second.resources; |
| 1055 for (ResourceRows::iterator it = resources.begin(); | 1069 for (std::vector<ResourceData>::iterator it = resources.begin(); |
| 1056 it != resources.end();) { | 1070 it != resources.end();) { |
| 1057 it->UpdateScore(); | 1071 ResourcePrefetchPredictorTables::UpdateResourceScore(&(*it)); |
| 1058 if (it->consecutive_misses >= config_.max_consecutive_misses) | 1072 if (it->consecutive_misses() >= config_.max_consecutive_misses) |
| 1059 it = resources.erase(it); | 1073 it = resources.erase(it); |
| 1060 else | 1074 else |
| 1061 ++it; | 1075 ++it; |
| 1062 } | 1076 } |
| 1063 ResourcePrefetchPredictorTables::SortResourceRows(&resources); | 1077 ResourcePrefetchPredictorTables::SortResources(&resources); |
| 1064 if (resources.size() > config_.max_resources_per_entry) | 1078 if (resources.size() > config_.max_resources_per_entry) |
| 1065 resources.resize(config_.max_resources_per_entry); | 1079 resources.resize(config_.max_resources_per_entry); |
| 1066 | 1080 |
| 1067 // If the row has no resources, remove it from the cache and delete the | 1081 // If the row has no resources, remove it from the cache and delete the |
| 1068 // entry in the database. Else update the database. | 1082 // entry in the database. Else update the database. |
| 1069 if (resources.empty()) { | 1083 if (resources.empty()) { |
| 1070 data_map->erase(key); | 1084 data_map->erase(key); |
| 1071 BrowserThread::PostTask( | 1085 BrowserThread::PostTask( |
| 1072 BrowserThread::DB, FROM_HERE, | 1086 BrowserThread::DB, FROM_HERE, |
| 1073 base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint, | 1087 base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint, |
| (...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1424 // HistoryService is already loaded. Continue with Initialization. | 1438 // HistoryService is already loaded. Continue with Initialization. |
| 1425 OnHistoryAndCacheLoaded(); | 1439 OnHistoryAndCacheLoaded(); |
| 1426 return; | 1440 return; |
| 1427 } | 1441 } |
| 1428 DCHECK(!history_service_observer_.IsObserving(history_service)); | 1442 DCHECK(!history_service_observer_.IsObserving(history_service)); |
| 1429 history_service_observer_.Add(history_service); | 1443 history_service_observer_.Add(history_service); |
| 1430 return; | 1444 return; |
| 1431 } | 1445 } |
| 1432 | 1446 |
| 1433 } // namespace predictors | 1447 } // namespace predictors |
| OLD | NEW |