| Index: chrome/browser/prerender/prerender_local_predictor.cc
|
| diff --git a/chrome/browser/prerender/prerender_local_predictor.cc b/chrome/browser/prerender/prerender_local_predictor.cc
|
| deleted file mode 100644
|
| index 671ba32072627c3f6af9bc8c61a18bb1fc29b7e7..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/prerender/prerender_local_predictor.cc
|
| +++ /dev/null
|
| @@ -1,1531 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome/browser/prerender/prerender_local_predictor.h"
|
| -
|
| -#include <ctype.h>
|
| -
|
| -#include <algorithm>
|
| -#include <map>
|
| -#include <set>
|
| -#include <string>
|
| -#include <utility>
|
| -
|
| -#include "base/json/json_reader.h"
|
| -#include "base/json/json_writer.h"
|
| -#include "base/metrics/field_trial.h"
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/stl_util.h"
|
| -#include "base/timer/timer.h"
|
| -#include "chrome/browser/browser_process.h"
|
| -#include "chrome/browser/history/history_service_factory.h"
|
| -#include "chrome/browser/prerender/prerender_field_trial.h"
|
| -#include "chrome/browser/prerender/prerender_handle.h"
|
| -#include "chrome/browser/prerender/prerender_histograms.h"
|
| -#include "chrome/browser/prerender/prerender_manager.h"
|
| -#include "chrome/browser/prerender/prerender_util.h"
|
| -#include "chrome/browser/profiles/profile.h"
|
| -#include "chrome/browser/safe_browsing/database_manager.h"
|
| -#include "chrome/browser/safe_browsing/safe_browsing_service.h"
|
| -#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
|
| -#include "chrome/common/prefetch_messages.h"
|
| -#include "components/history/core/browser/history_database.h"
|
| -#include "components/history/core/browser/history_db_task.h"
|
| -#include "components/history/core/browser/history_service.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| -#include "content/public/browser/navigation_controller.h"
|
| -#include "content/public/browser/navigation_entry.h"
|
| -#include "content/public/browser/render_frame_host.h"
|
| -#include "content/public/browser/render_process_host.h"
|
| -#include "content/public/browser/web_contents.h"
|
| -#include "crypto/secure_hash.h"
|
| -#include "grit/browser_resources.h"
|
| -#include "net/base/escape.h"
|
| -#include "net/base/load_flags.h"
|
| -#include "net/url_request/url_fetcher.h"
|
| -#include "ui/base/page_transition_types.h"
|
| -#include "ui/base/resource/resource_bundle.h"
|
| -#include "url/url_canon.h"
|
| -
|
| -using base::DictionaryValue;
|
| -using base::ListValue;
|
| -using base::Value;
|
| -using content::BrowserThread;
|
| -using ui::PageTransition;
|
| -using content::RenderFrameHost;
|
| -using content::SessionStorageNamespace;
|
| -using content::WebContents;
|
| -using history::URLID;
|
| -using net::URLFetcher;
|
| -using predictors::LoggedInPredictorTable;
|
| -using std::string;
|
| -using std::vector;
|
| -
|
| -namespace prerender {
|
| -
|
| -namespace {
|
| -
|
| -static const size_t kURLHashSize = 5;
|
| -static const int kNumPrerenderCandidates = 5;
|
| -static const int kInvalidProcessId = -1;
|
| -static const int kInvalidFrameId = -1;
|
| -static const int kMaxPrefetchItems = 100;
|
| -
|
| -} // namespace
|
| -
|
| -// When considering a candidate URL to be prerendered, we need to collect the
|
| -// data in this struct to make the determination whether we should issue the
|
| -// prerender or not.
|
| -struct PrerenderLocalPredictor::LocalPredictorURLInfo {
|
| - URLID id;
|
| - GURL url;
|
| - bool url_lookup_success;
|
| - bool logged_in;
|
| - bool logged_in_lookup_ok;
|
| - bool local_history_based;
|
| - bool service_whitelist;
|
| - bool service_whitelist_lookup_ok;
|
| - bool service_whitelist_reported;
|
| - double priority;
|
| -};
|
| -
|
| -// A struct consisting of everything needed for launching a potential prerender
|
| -// on a navigation: The navigation URL (source) triggering potential prerenders,
|
| -// and a set of candidate URLs.
|
| -struct PrerenderLocalPredictor::CandidatePrerenderInfo {
|
| - LocalPredictorURLInfo source_url_;
|
| - vector<LocalPredictorURLInfo> candidate_urls_;
|
| - scoped_refptr<SessionStorageNamespace> session_storage_namespace_;
|
| - // Render Process ID and Route ID of the page causing the prerender to be
|
| - // issued. Needed so that we can cause its renderer to issue prefetches within
|
| - // its context.
|
| - int render_process_id_;
|
| - int render_frame_id_;
|
| - scoped_ptr<gfx::Size> size_;
|
| - base::Time start_time_; // used for various time measurements
|
| - explicit CandidatePrerenderInfo(URLID source_id)
|
| - : render_process_id_(kInvalidProcessId),
|
| - render_frame_id_(kInvalidFrameId) {
|
| - source_url_.id = source_id;
|
| - }
|
| - void MaybeAddCandidateURLFromLocalData(URLID id, double priority) {
|
| - LocalPredictorURLInfo info;
|
| - info.id = id;
|
| - info.local_history_based = true;
|
| - info.service_whitelist = false;
|
| - info.service_whitelist_lookup_ok = false;
|
| - info.service_whitelist_reported = false;
|
| - info.priority = priority;
|
| - MaybeAddCandidateURLInternal(info);
|
| - }
|
| - void MaybeAddCandidateURLFromService(GURL url, double priority,
|
| - bool whitelist,
|
| - bool whitelist_lookup_ok) {
|
| - LocalPredictorURLInfo info;
|
| - info.id = kint64max;
|
| - info.url = url;
|
| - info.url_lookup_success = true;
|
| - info.local_history_based = false;
|
| - info.service_whitelist = whitelist;
|
| - info.service_whitelist_lookup_ok = whitelist_lookup_ok;
|
| - info.service_whitelist_reported = true;
|
| - info.priority = priority;
|
| - MaybeAddCandidateURLInternal(info);
|
| - }
|
| - void MaybeAddCandidateURLInternal(const LocalPredictorURLInfo& info) {
|
| - // TODO(tburkard): clean up this code, potentially using a list or a heap
|
| - int max_candidates = kNumPrerenderCandidates;
|
| - // We first insert local candidates, then service candidates.
|
| - // Since we want to keep kNumPrerenderCandidates for both local & service
|
| - // candidates, we need to double the maximum number of candidates once
|
| - // we start seeing service candidates.
|
| - if (!info.local_history_based)
|
| - max_candidates *= 2;
|
| - int insert_pos = candidate_urls_.size();
|
| - if (insert_pos < max_candidates)
|
| - candidate_urls_.push_back(info);
|
| - while (insert_pos > 0 &&
|
| - candidate_urls_[insert_pos - 1].priority < info.priority) {
|
| - if (insert_pos < max_candidates)
|
| - candidate_urls_[insert_pos] = candidate_urls_[insert_pos - 1];
|
| - insert_pos--;
|
| - }
|
| - if (insert_pos < max_candidates)
|
| - candidate_urls_[insert_pos] = info;
|
| - }
|
| -};
|
| -
|
| -namespace {
|
| -
|
| -#define TIMING_HISTOGRAM(name, value) \
|
| - UMA_HISTOGRAM_CUSTOM_TIMES(name, value, \
|
| - base::TimeDelta::FromMilliseconds(10), \
|
| - base::TimeDelta::FromSeconds(10), \
|
| - 50);
|
| -
|
| -// Task to lookup the URL for a given URLID.
|
| -class GetURLForURLIDTask : public history::HistoryDBTask {
|
| - public:
|
| - GetURLForURLIDTask(
|
| - PrerenderLocalPredictor::CandidatePrerenderInfo* request,
|
| - const base::Closure& callback)
|
| - : request_(request),
|
| - callback_(callback),
|
| - start_time_(base::Time::Now()) {
|
| - }
|
| -
|
| - bool RunOnDBThread(history::HistoryBackend* backend,
|
| - history::HistoryDatabase* db) override {
|
| - DoURLLookup(db, &request_->source_url_);
|
| - for (int i = 0; i < static_cast<int>(request_->candidate_urls_.size()); i++)
|
| - DoURLLookup(db, &request_->candidate_urls_[i]);
|
| - return true;
|
| - }
|
| -
|
| - void DoneRunOnMainThread() override {
|
| - callback_.Run();
|
| - TIMING_HISTOGRAM("Prerender.LocalPredictorURLLookupTime",
|
| - base::Time::Now() - start_time_);
|
| - }
|
| -
|
| - private:
|
| - ~GetURLForURLIDTask() override {}
|
| -
|
| - void DoURLLookup(history::HistoryDatabase* db,
|
| - PrerenderLocalPredictor::LocalPredictorURLInfo* request) {
|
| - history::URLRow url_row;
|
| - request->url_lookup_success = db->GetURLRow(request->id, &url_row);
|
| - if (request->url_lookup_success)
|
| - request->url = url_row.url();
|
| - }
|
| -
|
| - PrerenderLocalPredictor::CandidatePrerenderInfo* request_;
|
| - base::Closure callback_;
|
| - base::Time start_time_;
|
| - DISALLOW_COPY_AND_ASSIGN(GetURLForURLIDTask);
|
| -};
|
| -
|
| -// Task to load history from the visit database on startup.
|
| -class GetVisitHistoryTask : public history::HistoryDBTask {
|
| - public:
|
| - GetVisitHistoryTask(PrerenderLocalPredictor* local_predictor,
|
| - int max_visits)
|
| - : local_predictor_(local_predictor),
|
| - max_visits_(max_visits),
|
| - visit_history_(new vector<history::BriefVisitInfo>) {
|
| - }
|
| -
|
| - bool RunOnDBThread(history::HistoryBackend* backend,
|
| - history::HistoryDatabase* db) override {
|
| - db->GetBriefVisitInfoOfMostRecentVisits(max_visits_, visit_history_.get());
|
| - return true;
|
| - }
|
| -
|
| - void DoneRunOnMainThread() override {
|
| - local_predictor_->OnGetInitialVisitHistory(visit_history_.Pass());
|
| - }
|
| -
|
| - private:
|
| - ~GetVisitHistoryTask() override {}
|
| -
|
| - PrerenderLocalPredictor* local_predictor_;
|
| - int max_visits_;
|
| - scoped_ptr<vector<history::BriefVisitInfo> > visit_history_;
|
| - DISALLOW_COPY_AND_ASSIGN(GetVisitHistoryTask);
|
| -};
|
| -
|
| -// Maximum visit history to retrieve from the visit database.
|
| -const int kMaxVisitHistory = 100 * 1000;
|
| -
|
| -// Visit history size at which to trigger pruning, and number of items to prune.
|
| -const int kVisitHistoryPruneThreshold = 120 * 1000;
|
| -const int kVisitHistoryPruneAmount = 20 * 1000;
|
| -
|
| -const int kMinLocalPredictionTimeMs = 500;
|
| -
|
| -int GetMaxLocalPredictionTimeMs() {
|
| - return GetLocalPredictorTTLSeconds() * 1000;
|
| -}
|
| -
|
| -bool IsBackForward(PageTransition transition) {
|
| - return (transition & ui::PAGE_TRANSITION_FORWARD_BACK) != 0;
|
| -}
|
| -
|
| -bool IsHomePage(PageTransition transition) {
|
| - return (transition & ui::PAGE_TRANSITION_HOME_PAGE) != 0;
|
| -}
|
| -
|
| -bool IsIntermediateRedirect(PageTransition transition) {
|
| - return (transition & ui::PAGE_TRANSITION_CHAIN_END) == 0;
|
| -}
|
| -
|
| -bool IsFormSubmit(PageTransition transition) {
|
| - return ui::PageTransitionCoreTypeIs(transition,
|
| - ui::PAGE_TRANSITION_FORM_SUBMIT);
|
| -}
|
| -
|
| -bool ShouldExcludeTransitionForPrediction(PageTransition transition) {
|
| - return IsBackForward(transition) || IsHomePage(transition) ||
|
| - IsIntermediateRedirect(transition);
|
| -}
|
| -
|
| -base::Time GetCurrentTime() {
|
| - return base::Time::Now();
|
| -}
|
| -
|
| -bool StringContainsIgnoringCase(string haystack, string needle) {
|
| - std::transform(haystack.begin(), haystack.end(), haystack.begin(), ::tolower);
|
| - std::transform(needle.begin(), needle.end(), needle.begin(), ::tolower);
|
| - return haystack.find(needle) != string::npos;
|
| -}
|
| -
|
| -bool IsExtendedRootURL(const GURL& url) {
|
| - const string& path = url.path();
|
| - return path == "/index.html" || path == "/home.html" ||
|
| - path == "/main.html" ||
|
| - path == "/index.htm" || path == "/home.htm" || path == "/main.htm" ||
|
| - path == "/index.php" || path == "/home.php" || path == "/main.php" ||
|
| - path == "/index.asp" || path == "/home.asp" || path == "/main.asp" ||
|
| - path == "/index.py" || path == "/home.py" || path == "/main.py" ||
|
| - path == "/index.pl" || path == "/home.pl" || path == "/main.pl";
|
| -}
|
| -
|
| -bool IsRootPageURL(const GURL& url) {
|
| - return (url.path() == "/" || url.path().empty() || IsExtendedRootURL(url)) &&
|
| - (!url.has_query()) && (!url.has_ref());
|
| -}
|
| -
|
| -bool IsLogInURL(const GURL& url) {
|
| - return StringContainsIgnoringCase(url.spec().c_str(), "login") ||
|
| - StringContainsIgnoringCase(url.spec().c_str(), "signin");
|
| -}
|
| -
|
| -bool IsLogOutURL(const GURL& url) {
|
| - return StringContainsIgnoringCase(url.spec().c_str(), "logout") ||
|
| - StringContainsIgnoringCase(url.spec().c_str(), "signout");
|
| -}
|
| -
|
| -int64 URLHashToInt64(const unsigned char* data) {
|
| - static_assert(kURLHashSize < sizeof(int64), "url hash must fit in int64");
|
| - int64 value = 0;
|
| - memcpy(&value, data, kURLHashSize);
|
| - return value;
|
| -}
|
| -
|
| -int64 GetInt64URLHashForURL(const GURL& url) {
|
| - static_assert(kURLHashSize < sizeof(int64), "url hash must fit in int64");
|
| - scoped_ptr<crypto::SecureHash> hash(
|
| - crypto::SecureHash::Create(crypto::SecureHash::SHA256));
|
| - int64 hash_value = 0;
|
| - const char* url_string = url.spec().c_str();
|
| - hash->Update(url_string, strlen(url_string));
|
| - hash->Finish(&hash_value, kURLHashSize);
|
| - return hash_value;
|
| -}
|
| -
|
| -bool URLsIdenticalIgnoringFragments(const GURL& url1, const GURL& url2) {
|
| - url::Replacements<char> replacement;
|
| - replacement.ClearRef();
|
| - GURL u1 = url1.ReplaceComponents(replacement);
|
| - GURL u2 = url2.ReplaceComponents(replacement);
|
| - return (u1 == u2);
|
| -}
|
| -
|
| -void LookupLoggedInStatesOnDBThread(
|
| - scoped_refptr<LoggedInPredictorTable> logged_in_predictor_table,
|
| - PrerenderLocalPredictor::CandidatePrerenderInfo* request) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
|
| - for (int i = 0; i < static_cast<int>(request->candidate_urls_.size()); i++) {
|
| - PrerenderLocalPredictor::LocalPredictorURLInfo* info =
|
| - &request->candidate_urls_[i];
|
| - if (info->url_lookup_success) {
|
| - logged_in_predictor_table->HasUserLoggedIn(
|
| - info->url, &info->logged_in, &info->logged_in_lookup_ok);
|
| - } else {
|
| - info->logged_in_lookup_ok = false;
|
| - }
|
| - }
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -struct PrerenderLocalPredictor::PrerenderProperties {
|
| - PrerenderProperties(URLID url_id, const GURL& url, double priority,
|
| - base::Time start_time)
|
| - : url_id(url_id),
|
| - url(url),
|
| - priority(priority),
|
| - start_time(start_time),
|
| - would_have_matched(false) {
|
| - }
|
| -
|
| - // Default constructor for dummy element
|
| - PrerenderProperties()
|
| - : priority(0.0), would_have_matched(false) {
|
| - }
|
| -
|
| - double GetCurrentDecayedPriority() {
|
| - // If we are no longer prerendering, the priority is 0.
|
| - if (!prerender_handle || !prerender_handle->IsPrerendering())
|
| - return 0.0;
|
| - int half_life_time_seconds =
|
| - GetLocalPredictorPrerenderPriorityHalfLifeTimeSeconds();
|
| - if (half_life_time_seconds < 1)
|
| - return priority;
|
| - double multiple_elapsed =
|
| - (GetCurrentTime() - actual_start_time).InMillisecondsF() /
|
| - base::TimeDelta::FromSeconds(half_life_time_seconds).InMillisecondsF();
|
| - // Decay factor: 2 ^ (-multiple_elapsed)
|
| - double decay_factor = exp(- multiple_elapsed * log(2.0));
|
| - return priority * decay_factor;
|
| - }
|
| -
|
| - URLID url_id;
|
| - GURL url;
|
| - double priority;
|
| - // For expiration purposes, this is a synthetic start time consisting either
|
| - // of the actual start time, or of the last time the page was re-requested
|
| - // for prerendering - 10 seconds (unless the original request came after
|
| - // that). This is to emulate the effect of re-prerendering a page that is
|
| - // about to expire, because it was re-requested for prerendering a second
|
| - // time after the actual prerender being kept around.
|
| - base::Time start_time;
|
| - // The actual time this page was last requested for prerendering.
|
| - base::Time actual_start_time;
|
| - scoped_ptr<PrerenderHandle> prerender_handle;
|
| - // Indicates whether this prerender would have matched a URL navigated to,
|
| - // but was not swapped in for some reason.
|
| - bool would_have_matched;
|
| -};
|
| -
|
| -// A class simulating a set of URLs prefetched, for statistical purposes.
|
| -class PrerenderLocalPredictor::PrefetchList {
|
| - public:
|
| - enum SeenType {
|
| - SEEN_TABCONTENTS_OBSERVER,
|
| - SEEN_HISTORY,
|
| - SEEN_MAX_VALUE
|
| - };
|
| -
|
| - PrefetchList() {}
|
| - ~PrefetchList() {
|
| - STLDeleteValues(&entries_);
|
| - }
|
| -
|
| - // Adds a new URL being prefetched. If the URL is already in the list,
|
| - // nothing will happen. Returns whether a new prefetch was added.
|
| - bool AddURL(const GURL& url) {
|
| - ExpireOldItems();
|
| - string url_string = url.spec().c_str();
|
| - base::hash_map<string, ListEntry*>::iterator it = entries_.find(url_string);
|
| - if (it != entries_.end()) {
|
| - // If a prefetch previously existed, and has not been seen yet in either
|
| - // a tab contents or a history, we will not re-issue it. Otherwise, if it
|
| - // may have been consumed by either tab contents or history, we will
|
| - // permit re-issuing another one.
|
| - if (!it->second->seen_history_ &&
|
| - !it->second->seen_tabcontents_) {
|
| - return false;
|
| - }
|
| - }
|
| - ListEntry* entry = new ListEntry(url_string);
|
| - entries_[entry->url_] = entry;
|
| - entry_list_.push_back(entry);
|
| - ExpireOldItems();
|
| - return true;
|
| - }
|
| -
|
| - // Marks the URL provided as seen in the context specified. Returns true
|
| - // iff the item is currently in the list and had not been seen before in
|
| - // the context specified, i.e. the marking was successful.
|
| - bool MarkURLSeen(const GURL& url, SeenType type) {
|
| - ExpireOldItems();
|
| - bool return_value = false;
|
| - base::hash_map<string, ListEntry*>::iterator it =
|
| - entries_.find(url.spec().c_str());
|
| - if (it == entries_.end())
|
| - return return_value;
|
| - if (type == SEEN_TABCONTENTS_OBSERVER && !it->second->seen_tabcontents_) {
|
| - it->second->seen_tabcontents_ = true;
|
| - return_value = true;
|
| - }
|
| - if (type == SEEN_HISTORY && !it->second->seen_history_) {
|
| - it->second->seen_history_ = true;
|
| - return_value = true;
|
| - }
|
| - // If the item has been seen in both the history and in tab contents,
|
| - // and the page load time has been recorded, erase it from the map to
|
| - // make room for new prefetches.
|
| - if (it->second->seen_tabcontents_ && it->second->seen_history_ &&
|
| - it->second->seen_plt_) {
|
| - entries_.erase(url.spec().c_str());
|
| - }
|
| - return return_value;
|
| - }
|
| -
|
| - // Marks the PLT for the provided UR as seen. Returns true
|
| - // iff the item is currently in the list and the PLT had not been seen
|
| - // before, i.e. the sighting was successful.
|
| - bool MarkPLTSeen(const GURL& url, base::TimeDelta plt) {
|
| - ExpireOldItems();
|
| - base::hash_map<string, ListEntry*>::iterator it =
|
| - entries_.find(url.spec().c_str());
|
| - if (it == entries_.end() || it->second->seen_plt_ ||
|
| - it->second->add_time_ > GetCurrentTime() - plt) {
|
| - return false;
|
| - }
|
| - it->second->seen_plt_ = true;
|
| - // If the item has been seen in both the history and in tab contents,
|
| - // and the page load time has been recorded, erase it from the map to
|
| - // make room for new prefetches.
|
| - if (it->second->seen_tabcontents_ && it->second->seen_history_ &&
|
| - it->second->seen_plt_) {
|
| - entries_.erase(url.spec().c_str());
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - private:
|
| - struct ListEntry {
|
| - explicit ListEntry(const string& url)
|
| - : url_(url),
|
| - add_time_(GetCurrentTime()),
|
| - seen_tabcontents_(false),
|
| - seen_history_(false),
|
| - seen_plt_(false) {
|
| - }
|
| - std::string url_;
|
| - base::Time add_time_;
|
| - bool seen_tabcontents_;
|
| - bool seen_history_;
|
| - bool seen_plt_;
|
| - };
|
| -
|
| - void ExpireOldItems() {
|
| - base::Time expiry_cutoff = GetCurrentTime() -
|
| - base::TimeDelta::FromSeconds(GetPrerenderPrefetchListTimeoutSeconds());
|
| - while (!entry_list_.empty() &&
|
| - (entry_list_.front()->add_time_ < expiry_cutoff ||
|
| - entries_.size() > kMaxPrefetchItems)) {
|
| - ListEntry* entry = entry_list_.front();
|
| - entry_list_.pop_front();
|
| - // If the entry to be deleted is still the one active in entries_,
|
| - // we must erase it from entries_.
|
| - base::hash_map<string, ListEntry*>::iterator it =
|
| - entries_.find(entry->url_);
|
| - if (it != entries_.end() && it->second == entry)
|
| - entries_.erase(entry->url_);
|
| - delete entry;
|
| - }
|
| - }
|
| -
|
| - base::hash_map<string, ListEntry*> entries_;
|
| - std::list<ListEntry*> entry_list_;
|
| - DISALLOW_COPY_AND_ASSIGN(PrefetchList);
|
| -};
|
| -
|
| -PrerenderLocalPredictor::PrerenderLocalPredictor(
|
| - PrerenderManager* prerender_manager)
|
| - : prerender_manager_(prerender_manager),
|
| - prefetch_list_(new PrefetchList()),
|
| - history_service_observer_(this),
|
| - weak_factory_(this) {
|
| - RecordEvent(EVENT_CONSTRUCTED);
|
| - if (base::MessageLoop::current()) {
|
| - timer_.Start(FROM_HERE,
|
| - base::TimeDelta::FromMilliseconds(kInitDelayMs),
|
| - this,
|
| - &PrerenderLocalPredictor::Init);
|
| - RecordEvent(EVENT_INIT_SCHEDULED);
|
| - }
|
| -
|
| - static const size_t kChecksumHashSize = 32;
|
| - base::RefCountedStaticMemory* url_whitelist_data =
|
| - ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
|
| - IDR_PRERENDER_URL_WHITELIST);
|
| - size_t size = url_whitelist_data->size();
|
| - const unsigned char* front = url_whitelist_data->front();
|
| - if (size < kChecksumHashSize ||
|
| - (size - kChecksumHashSize) % kURLHashSize != 0) {
|
| - RecordEvent(EVENT_URL_WHITELIST_ERROR);
|
| - return;
|
| - }
|
| - scoped_ptr<crypto::SecureHash> hash(
|
| - crypto::SecureHash::Create(crypto::SecureHash::SHA256));
|
| - hash->Update(front + kChecksumHashSize, size - kChecksumHashSize);
|
| - char hash_value[kChecksumHashSize];
|
| - hash->Finish(hash_value, kChecksumHashSize);
|
| - if (memcmp(hash_value, front, kChecksumHashSize)) {
|
| - RecordEvent(EVENT_URL_WHITELIST_ERROR);
|
| - return;
|
| - }
|
| - for (const unsigned char* p = front + kChecksumHashSize;
|
| - p < front + size;
|
| - p += kURLHashSize) {
|
| - url_whitelist_.insert(URLHashToInt64(p));
|
| - }
|
| - RecordEvent(EVENT_URL_WHITELIST_OK);
|
| -}
|
| -
|
| -PrerenderLocalPredictor::~PrerenderLocalPredictor() {
|
| - Shutdown();
|
| - for (PrerenderProperties* p : issued_prerenders_) {
|
| - DCHECK(p != NULL);
|
| - if (p->prerender_handle)
|
| - p->prerender_handle->OnCancel();
|
| - }
|
| - STLDeleteContainerPairPointers(
|
| - outstanding_prerender_service_requests_.begin(),
|
| - outstanding_prerender_service_requests_.end());
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::Shutdown() {
|
| - timer_.Stop();
|
| - history_service_observer_.RemoveAll();
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::OnAddVisit(
|
| - history::HistoryService* history_service,
|
| - const history::BriefVisitInfo& info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - RecordEvent(EVENT_ADD_VISIT);
|
| - if (!visit_history_.get())
|
| - return;
|
| - visit_history_->push_back(info);
|
| - if (static_cast<int>(visit_history_->size()) > kVisitHistoryPruneThreshold) {
|
| - visit_history_->erase(visit_history_->begin(),
|
| - visit_history_->begin() + kVisitHistoryPruneAmount);
|
| - }
|
| - RecordEvent(EVENT_ADD_VISIT_INITIALIZED);
|
| - if (current_prerender_.get() &&
|
| - current_prerender_->url_id == info.url_id &&
|
| - IsPrerenderStillValid(current_prerender_.get())) {
|
| - UMA_HISTOGRAM_CUSTOM_TIMES(
|
| - "Prerender.LocalPredictorTimeUntilUsed",
|
| - GetCurrentTime() - current_prerender_->actual_start_time,
|
| - base::TimeDelta::FromMilliseconds(10),
|
| - base::TimeDelta::FromMilliseconds(GetMaxLocalPredictionTimeMs()),
|
| - 50);
|
| - last_swapped_in_prerender_.reset(current_prerender_.release());
|
| - RecordEvent(EVENT_ADD_VISIT_PRERENDER_IDENTIFIED);
|
| - }
|
| - if (ShouldExcludeTransitionForPrediction(info.transition))
|
| - return;
|
| - Profile* profile = prerender_manager_->profile();
|
| - if (!profile ||
|
| - ShouldDisableLocalPredictorDueToPreferencesAndNetwork(profile)) {
|
| - return;
|
| - }
|
| - RecordEvent(EVENT_ADD_VISIT_RELEVANT_TRANSITION);
|
| - base::TimeDelta max_age =
|
| - base::TimeDelta::FromMilliseconds(GetMaxLocalPredictionTimeMs());
|
| - base::TimeDelta min_age =
|
| - base::TimeDelta::FromMilliseconds(kMinLocalPredictionTimeMs);
|
| - std::set<URLID> next_urls_currently_found;
|
| - std::map<URLID, int> next_urls_num_found;
|
| - int num_occurrences_of_current_visit = 0;
|
| - base::Time last_visited;
|
| - scoped_ptr<CandidatePrerenderInfo> lookup_info(
|
| - new CandidatePrerenderInfo(info.url_id));
|
| - const vector<history::BriefVisitInfo>& visits = *(visit_history_.get());
|
| - for (int i = 0; i < static_cast<int>(visits.size()); i++) {
|
| - if (!ShouldExcludeTransitionForPrediction(visits[i].transition)) {
|
| - if (visits[i].url_id == info.url_id) {
|
| - last_visited = visits[i].time;
|
| - num_occurrences_of_current_visit++;
|
| - next_urls_currently_found.clear();
|
| - continue;
|
| - }
|
| - if (!last_visited.is_null() &&
|
| - last_visited > visits[i].time - max_age &&
|
| - last_visited < visits[i].time - min_age) {
|
| - if (!IsFormSubmit(visits[i].transition))
|
| - next_urls_currently_found.insert(visits[i].url_id);
|
| - }
|
| - }
|
| - if (i == static_cast<int>(visits.size()) - 1 ||
|
| - visits[i+1].url_id == info.url_id) {
|
| - for (std::set<URLID>::iterator it = next_urls_currently_found.begin();
|
| - it != next_urls_currently_found.end();
|
| - ++it) {
|
| - std::pair<std::map<URLID, int>::iterator, bool> insert_ret =
|
| - next_urls_num_found.insert(std::pair<URLID, int>(*it, 0));
|
| - std::map<URLID, int>::iterator num_found_it = insert_ret.first;
|
| - num_found_it->second++;
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (num_occurrences_of_current_visit > 1) {
|
| - RecordEvent(EVENT_ADD_VISIT_RELEVANT_TRANSITION_REPEAT_URL);
|
| - } else {
|
| - RecordEvent(EVENT_ADD_VISIT_RELEVANT_TRANSITION_NEW_URL);
|
| - }
|
| -
|
| - for (std::map<URLID, int>::const_iterator it = next_urls_num_found.begin();
|
| - it != next_urls_num_found.end();
|
| - ++it) {
|
| - // Only consider a candidate next page for prerendering if it was viewed
|
| - // at least twice, and at least 10% of the time.
|
| - if (num_occurrences_of_current_visit > 0 &&
|
| - it->second > 1 &&
|
| - it->second * 10 >= num_occurrences_of_current_visit) {
|
| - RecordEvent(EVENT_ADD_VISIT_IDENTIFIED_PRERENDER_CANDIDATE);
|
| - double priority = static_cast<double>(it->second) /
|
| - static_cast<double>(num_occurrences_of_current_visit);
|
| - lookup_info->MaybeAddCandidateURLFromLocalData(it->first, priority);
|
| - }
|
| - }
|
| -
|
| - RecordEvent(EVENT_START_URL_LOOKUP);
|
| - history::HistoryService* history = GetHistoryIfExists();
|
| - if (history) {
|
| - RecordEvent(EVENT_GOT_HISTORY_ISSUING_LOOKUP);
|
| - CandidatePrerenderInfo* lookup_info_ptr = lookup_info.get();
|
| - history->ScheduleDBTask(
|
| - scoped_ptr<history::HistoryDBTask>(
|
| - new GetURLForURLIDTask(
|
| - lookup_info_ptr,
|
| - base::Bind(&PrerenderLocalPredictor::OnLookupURL,
|
| - base::Unretained(this),
|
| - base::Passed(&lookup_info)))),
|
| - &history_db_tracker_);
|
| - }
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::OnLookupURL(
|
| - scoped_ptr<CandidatePrerenderInfo> info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT);
|
| -
|
| - if (!info->source_url_.url_lookup_success) {
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_FAILED);
|
| - return;
|
| - }
|
| -
|
| - if (prefetch_list_->MarkURLSeen(info->source_url_.url,
|
| - PrefetchList::SEEN_HISTORY)) {
|
| - RecordEvent(EVENT_PREFETCH_LIST_SEEN_HISTORY);
|
| - }
|
| -
|
| - if (info->candidate_urls_.size() > 0 &&
|
| - info->candidate_urls_[0].url_lookup_success) {
|
| - LogCandidateURLStats(info->candidate_urls_[0].url);
|
| - }
|
| -
|
| - WebContents* source_web_contents = NULL;
|
| - bool multiple_source_web_contents_candidates = false;
|
| -
|
| -#if !defined(OS_ANDROID)
|
| - // We need to figure out what tab launched the prerender. We do this by
|
| - // comparing URLs. This may not always work: the URL may occur in two
|
| - // tabs, and we pick the wrong one, or the tab we should have picked
|
| - // may have navigated elsewhere. Hopefully, this doesn't happen too often,
|
| - // so we ignore these cases for now.
|
| - // TODO(tburkard): Reconsider this, potentially measure it, and fix this
|
| - // in the future.
|
| - for (TabContentsIterator it; !it.done(); it.Next()) {
|
| - if (it->GetURL() == info->source_url_.url) {
|
| - if (!source_web_contents)
|
| - source_web_contents = *it;
|
| - else
|
| - multiple_source_web_contents_candidates = true;
|
| - }
|
| - }
|
| -#endif
|
| -
|
| - if (!source_web_contents) {
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_NO_SOURCE_WEBCONTENTS_FOUND);
|
| - return;
|
| - }
|
| -
|
| - if (multiple_source_web_contents_candidates)
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_MULTIPLE_SOURCE_WEBCONTENTS_FOUND);
|
| -
|
| - info->session_storage_namespace_ =
|
| - source_web_contents->GetController().GetDefaultSessionStorageNamespace();
|
| - RenderFrameHost* rfh = source_web_contents->GetMainFrame();
|
| - info->render_process_id_ = rfh->GetProcess()->GetID();
|
| - info->render_frame_id_ = rfh->GetRoutingID();
|
| -
|
| - gfx::Rect container_bounds = source_web_contents->GetContainerBounds();
|
| - info->size_.reset(new gfx::Size(container_bounds.size()));
|
| -
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_SUCCESS);
|
| -
|
| - DoPrerenderServiceCheck(info.Pass());
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::DoPrerenderServiceCheck(
|
| - scoped_ptr<CandidatePrerenderInfo> info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - if (!ShouldQueryPrerenderService(prerender_manager_->profile())) {
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_DISABLED);
|
| - DoLoggedInLookup(info.Pass());
|
| - return;
|
| - }
|
| - /*
|
| - Create a JSON request.
|
| - Here is a sample request:
|
| - { "prerender_request": {
|
| - "version": 1,
|
| - "behavior_id": 6,
|
| - "hint_request": {
|
| - "browse_history": [
|
| - { "url": "http://www.cnn.com/"
|
| - }
|
| - ]
|
| - },
|
| - "candidate_check_request": {
|
| - "candidates": [
|
| - { "url": "http://www.cnn.com/sports/"
|
| - },
|
| - { "url": "http://www.cnn.com/politics/"
|
| - }
|
| - ]
|
| - }
|
| - }
|
| - }
|
| - */
|
| - base::DictionaryValue json_data;
|
| - base::DictionaryValue* req = new base::DictionaryValue();
|
| - req->SetInteger("version", 1);
|
| - req->SetInteger("behavior_id", GetPrerenderServiceBehaviorID());
|
| - if (ShouldQueryPrerenderServiceForCurrentURL() &&
|
| - info->source_url_.url_lookup_success) {
|
| - base::ListValue* browse_history = new base::ListValue();
|
| - base::DictionaryValue* browse_item = new base::DictionaryValue();
|
| - browse_item->SetString("url", info->source_url_.url.spec());
|
| - browse_history->Append(browse_item);
|
| - base::DictionaryValue* hint_request = new base::DictionaryValue();
|
| - hint_request->Set("browse_history", browse_history);
|
| - req->Set("hint_request", hint_request);
|
| - }
|
| - int num_candidate_urls = 0;
|
| - for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) {
|
| - if (info->candidate_urls_[i].url_lookup_success)
|
| - num_candidate_urls++;
|
| - }
|
| - if (ShouldQueryPrerenderServiceForCandidateURLs() &&
|
| - num_candidate_urls > 0) {
|
| - base::ListValue* candidates = new base::ListValue();
|
| - base::DictionaryValue* candidate;
|
| - for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) {
|
| - if (info->candidate_urls_[i].url_lookup_success) {
|
| - candidate = new base::DictionaryValue();
|
| - candidate->SetString("url", info->candidate_urls_[i].url.spec());
|
| - candidates->Append(candidate);
|
| - }
|
| - }
|
| - base::DictionaryValue* candidate_check_request =
|
| - new base::DictionaryValue();
|
| - candidate_check_request->Set("candidates", candidates);
|
| - req->Set("candidate_check_request", candidate_check_request);
|
| - }
|
| - json_data.Set("prerender_request", req);
|
| - string request_string;
|
| - base::JSONWriter::Write(&json_data, &request_string);
|
| - GURL fetch_url(GetPrerenderServiceURLPrefix() +
|
| - net::EscapeQueryParamValue(request_string, false));
|
| - net::URLFetcher* fetcher = net::URLFetcher::Create(
|
| - 0,
|
| - fetch_url,
|
| - URLFetcher::GET, this);
|
| - fetcher->SetRequestContext(
|
| - prerender_manager_->profile()->GetRequestContext());
|
| - fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE |
|
| - net::LOAD_DO_NOT_SAVE_COOKIES |
|
| - net::LOAD_DO_NOT_SEND_COOKIES);
|
| - fetcher->AddExtraRequestHeader("Pragma: no-cache");
|
| - info->start_time_ = base::Time::Now();
|
| - outstanding_prerender_service_requests_.insert(
|
| - std::make_pair(fetcher, info.release()));
|
| - base::MessageLoop::current()->PostDelayedTask(
|
| - FROM_HERE,
|
| - base::Bind(&PrerenderLocalPredictor::MaybeCancelURLFetcher,
|
| - weak_factory_.GetWeakPtr(), fetcher),
|
| - base::TimeDelta::FromMilliseconds(GetPrerenderServiceFetchTimeoutMs()));
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_ISSUED_LOOKUP);
|
| - fetcher->Start();
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::MaybeCancelURLFetcher(net::URLFetcher* fetcher) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - OutstandingFetchers::iterator it =
|
| - outstanding_prerender_service_requests_.find(fetcher);
|
| - if (it == outstanding_prerender_service_requests_.end())
|
| - return;
|
| - delete it->first;
|
| - scoped_ptr<CandidatePrerenderInfo> info(it->second);
|
| - outstanding_prerender_service_requests_.erase(it);
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_LOOKUP_TIMED_OUT);
|
| - DoLoggedInLookup(info.Pass());
|
| -}
|
| -
|
| -bool PrerenderLocalPredictor::ApplyParsedPrerenderServiceResponse(
|
| - base::DictionaryValue* dict,
|
| - CandidatePrerenderInfo* info,
|
| - bool* hinting_timed_out,
|
| - bool* hinting_url_lookup_timed_out,
|
| - bool* candidate_url_lookup_timed_out) {
|
| - /*
|
| - Process the response to the request.
|
| - Here is a sample response to illustrate the format.
|
| - {
|
| - "prerender_response": {
|
| - "behavior_id": 6,
|
| - "hint_response": {
|
| - "hinting_timed_out": 0,
|
| - "candidates": [
|
| - { "url": "http://www.cnn.com/story-1",
|
| - "in_index": 1,
|
| - "likelihood": 0.60,
|
| - "in_index_timed_out": 0
|
| - },
|
| - { "url": "http://www.cnn.com/story-2",
|
| - "in_index": 1,
|
| - "likelihood": 0.30,
|
| - "in_index_timed_out": 0
|
| - }
|
| - ]
|
| - },
|
| - "candidate_check_response": {
|
| - "candidates": [
|
| - { "url": "http://www.cnn.com/sports/",
|
| - "in_index": 1,
|
| - "in_index_timed_out": 0
|
| - },
|
| - { "url": "http://www.cnn.com/politics/",
|
| - "in_index": 0,
|
| - "in_index_timed_out": "1"
|
| - }
|
| - ]
|
| - }
|
| - }
|
| - }
|
| - */
|
| - base::ListValue* list = NULL;
|
| - int int_value;
|
| - if (!dict->GetInteger("prerender_response.behavior_id", &int_value) ||
|
| - int_value != GetPrerenderServiceBehaviorID()) {
|
| - return false;
|
| - }
|
| - if (!dict->GetList("prerender_response.candidate_check_response.candidates",
|
| - &list)) {
|
| - if (ShouldQueryPrerenderServiceForCandidateURLs()) {
|
| - for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) {
|
| - if (info->candidate_urls_[i].url_lookup_success)
|
| - return false;
|
| - }
|
| - }
|
| - } else {
|
| - for (size_t i = 0; i < list->GetSize(); i++) {
|
| - base::DictionaryValue* d;
|
| - if (!list->GetDictionary(i, &d))
|
| - return false;
|
| - string url_string;
|
| - if (!d->GetString("url", &url_string) || !GURL(url_string).is_valid())
|
| - return false;
|
| - GURL url(url_string);
|
| - int in_index_timed_out = 0;
|
| - int in_index = 0;
|
| - if ((!d->GetInteger("in_index_timed_out", &in_index_timed_out) ||
|
| - in_index_timed_out != 1) &&
|
| - !d->GetInteger("in_index", &in_index)) {
|
| - return false;
|
| - }
|
| - if (in_index < 0 || in_index > 1 ||
|
| - in_index_timed_out < 0 || in_index_timed_out > 1) {
|
| - return false;
|
| - }
|
| - if (in_index_timed_out == 1)
|
| - *candidate_url_lookup_timed_out = true;
|
| - for (size_t j = 0; j < info->candidate_urls_.size(); j++) {
|
| - if (info->candidate_urls_[j].url == url) {
|
| - info->candidate_urls_[j].service_whitelist_reported = true;
|
| - info->candidate_urls_[j].service_whitelist = (in_index == 1);
|
| - info->candidate_urls_[j].service_whitelist_lookup_ok =
|
| - ((1 - in_index_timed_out) == 1);
|
| - }
|
| - }
|
| - }
|
| - for (size_t i = 0; i < info->candidate_urls_.size(); i++) {
|
| - if (info->candidate_urls_[i].url_lookup_success &&
|
| - !info->candidate_urls_[i].service_whitelist_reported) {
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (ShouldQueryPrerenderServiceForCurrentURL() &&
|
| - info->source_url_.url_lookup_success) {
|
| - list = NULL;
|
| - if (dict->GetInteger("prerender_response.hint_response.hinting_timed_out",
|
| - &int_value) &&
|
| - int_value == 1) {
|
| - *hinting_timed_out = true;
|
| - } else if (!dict->GetList("prerender_response.hint_response.candidates",
|
| - &list)) {
|
| - return false;
|
| - } else {
|
| - for (int i = 0; i < static_cast<int>(list->GetSize()); i++) {
|
| - base::DictionaryValue* d;
|
| - if (!list->GetDictionary(i, &d))
|
| - return false;
|
| - string url;
|
| - if (!d->GetString("url", &url) || !GURL(url).is_valid())
|
| - return false;
|
| - double priority;
|
| - if (!d->GetDouble("likelihood", &priority) || priority < 0.0 ||
|
| - priority > 1.0) {
|
| - return false;
|
| - }
|
| - int in_index_timed_out = 0;
|
| - int in_index = 0;
|
| - if ((!d->GetInteger("in_index_timed_out", &in_index_timed_out) ||
|
| - in_index_timed_out != 1) &&
|
| - !d->GetInteger("in_index", &in_index)) {
|
| - return false;
|
| - }
|
| - if (in_index < 0 || in_index > 1 || in_index_timed_out < 0 ||
|
| - in_index_timed_out > 1) {
|
| - return false;
|
| - }
|
| - if (in_index_timed_out == 1)
|
| - *hinting_url_lookup_timed_out = true;
|
| - info->MaybeAddCandidateURLFromService(GURL(url),
|
| - priority,
|
| - in_index == 1,
|
| - !in_index_timed_out);
|
| - }
|
| - if (list->GetSize() > 0)
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_RETURNED_HINTING_CANDIDATES);
|
| - }
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::OnURLFetchComplete(
|
| - const net::URLFetcher* source) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_RECEIVED_RESULT);
|
| - net::URLFetcher* fetcher = const_cast<net::URLFetcher*>(source);
|
| - OutstandingFetchers::iterator it =
|
| - outstanding_prerender_service_requests_.find(fetcher);
|
| - if (it == outstanding_prerender_service_requests_.end()) {
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_NO_RECORD_FOR_RESULT);
|
| - return;
|
| - }
|
| - scoped_ptr<CandidatePrerenderInfo> info(it->second);
|
| - outstanding_prerender_service_requests_.erase(it);
|
| - TIMING_HISTOGRAM("Prerender.LocalPredictorServiceLookupTime",
|
| - base::Time::Now() - info->start_time_);
|
| - string result;
|
| - fetcher->GetResponseAsString(&result);
|
| - scoped_ptr<base::Value> root;
|
| - root.reset(base::JSONReader::Read(result));
|
| - bool hinting_timed_out = false;
|
| - bool hinting_url_lookup_timed_out = false;
|
| - bool candidate_url_lookup_timed_out = false;
|
| - if (!root.get() || !root->IsType(base::Value::TYPE_DICTIONARY)) {
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_PARSE_ERROR_INCORRECT_JSON);
|
| - } else {
|
| - if (ApplyParsedPrerenderServiceResponse(
|
| - static_cast<base::DictionaryValue*>(root.get()),
|
| - info.get(),
|
| - &hinting_timed_out,
|
| - &hinting_url_lookup_timed_out,
|
| - &candidate_url_lookup_timed_out)) {
|
| - // We finished parsing the result, and found no errors.
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_PARSED_CORRECTLY);
|
| - if (hinting_timed_out)
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_HINTING_TIMED_OUT);
|
| - if (hinting_url_lookup_timed_out)
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_HINTING_URL_LOOKUP_TIMED_OUT);
|
| - if (candidate_url_lookup_timed_out)
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_CANDIDATE_URL_LOOKUP_TIMED_OUT);
|
| - DoLoggedInLookup(info.Pass());
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // If we did not return earlier, an error happened during parsing.
|
| - // Record this, and proceed.
|
| - RecordEvent(EVENT_PRERENDER_SERVICE_PARSE_ERROR);
|
| - DoLoggedInLookup(info.Pass());
|
| -}
|
| -
|
| -void PrerenderLocalPredictor:: DoLoggedInLookup(
|
| - scoped_ptr<CandidatePrerenderInfo> info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - scoped_refptr<LoggedInPredictorTable> logged_in_table =
|
| - prerender_manager_->logged_in_predictor_table();
|
| -
|
| - if (!logged_in_table.get()) {
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_NO_LOGGED_IN_TABLE_FOUND);
|
| - return;
|
| - }
|
| -
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_ISSUING_LOGGED_IN_LOOKUP);
|
| -
|
| - info->start_time_ = base::Time::Now();
|
| -
|
| - CandidatePrerenderInfo* info_ptr = info.get();
|
| - BrowserThread::PostTaskAndReply(
|
| - BrowserThread::DB, FROM_HERE,
|
| - base::Bind(&LookupLoggedInStatesOnDBThread,
|
| - logged_in_table,
|
| - info_ptr),
|
| - base::Bind(&PrerenderLocalPredictor::ContinuePrerenderCheck,
|
| - weak_factory_.GetWeakPtr(),
|
| - base::Passed(&info)));
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::LogCandidateURLStats(const GURL& url) const {
|
| - if (url_whitelist_.count(GetInt64URLHashForURL(url)) > 0) {
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_ON_WHITELIST);
|
| - if (IsRootPageURL(url))
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_ON_WHITELIST_ROOT_PAGE);
|
| - }
|
| - if (IsRootPageURL(url))
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_ROOT_PAGE);
|
| - if (IsExtendedRootURL(url))
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_EXTENDED_ROOT_PAGE);
|
| - if (IsRootPageURL(url) && url.SchemeIs("http"))
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_ROOT_PAGE_HTTP);
|
| - if (url.SchemeIs("http"))
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_IS_HTTP);
|
| - if (url.has_query())
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_HAS_QUERY_STRING);
|
| - if (IsLogOutURL(url))
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_CONTAINS_LOGOUT);
|
| - if (IsLogInURL(url))
|
| - RecordEvent(EVENT_PRERENDER_URL_LOOKUP_RESULT_CONTAINS_LOGIN);
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::OnGetInitialVisitHistory(
|
| - scoped_ptr<vector<history::BriefVisitInfo> > visit_history) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK(!visit_history_.get());
|
| - RecordEvent(EVENT_INIT_SUCCEEDED);
|
| - // Since the visit history has descending timestamps, we must reverse it.
|
| - visit_history_.reset(new vector<history::BriefVisitInfo>(
|
| - visit_history->rbegin(), visit_history->rend()));
|
| -}
|
| -
|
| -history::HistoryService* PrerenderLocalPredictor::GetHistoryIfExists() const {
|
| - Profile* profile = prerender_manager_->profile();
|
| - if (!profile)
|
| - return NULL;
|
| - return HistoryServiceFactory::GetForProfileWithoutCreating(profile);
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::Init() {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - RecordEvent(EVENT_INIT_STARTED);
|
| - Profile* profile = prerender_manager_->profile();
|
| - if (!profile ||
|
| - ShouldDisableLocalPredictorBasedOnSyncAndConfiguration(profile)) {
|
| - RecordEvent(EVENT_INIT_FAILED_UNENCRYPTED_SYNC_NOT_ENABLED);
|
| - return;
|
| - }
|
| - history::HistoryService* history = GetHistoryIfExists();
|
| - if (history) {
|
| - CHECK(!history_service_observer_.IsObserving(history));
|
| - history->ScheduleDBTask(
|
| - scoped_ptr<history::HistoryDBTask>(
|
| - new GetVisitHistoryTask(this, kMaxVisitHistory)),
|
| - &history_db_tracker_);
|
| - history_service_observer_.Add(history);
|
| - } else {
|
| - RecordEvent(EVENT_INIT_FAILED_NO_HISTORY);
|
| - }
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::OnPLTEventForURL(const GURL& url,
|
| - base::TimeDelta page_load_time) {
|
| - if (prefetch_list_->MarkPLTSeen(url, page_load_time)) {
|
| - UMA_HISTOGRAM_CUSTOM_TIMES("Prerender.LocalPredictorPrefetchMatchPLT",
|
| - page_load_time,
|
| - base::TimeDelta::FromMilliseconds(10),
|
| - base::TimeDelta::FromSeconds(60),
|
| - 100);
|
| - }
|
| -
|
| - scoped_ptr<PrerenderProperties> prerender;
|
| - if (DoesPrerenderMatchPLTRecord(last_swapped_in_prerender_.get(),
|
| - url, page_load_time)) {
|
| - prerender.reset(last_swapped_in_prerender_.release());
|
| - }
|
| - if (DoesPrerenderMatchPLTRecord(current_prerender_.get(),
|
| - url, page_load_time)) {
|
| - prerender.reset(current_prerender_.release());
|
| - }
|
| - if (!prerender.get())
|
| - return;
|
| - if (IsPrerenderStillValid(prerender.get())) {
|
| - UMA_HISTOGRAM_CUSTOM_TIMES("Prerender.SimulatedLocalBrowsingBaselinePLT",
|
| - page_load_time,
|
| - base::TimeDelta::FromMilliseconds(10),
|
| - base::TimeDelta::FromSeconds(60),
|
| - 100);
|
| -
|
| - base::TimeDelta prerender_age = GetCurrentTime() - prerender->start_time;
|
| - if (prerender_age > page_load_time) {
|
| - base::TimeDelta new_plt;
|
| - if (prerender_age < 2 * page_load_time)
|
| - new_plt = 2 * page_load_time - prerender_age;
|
| - UMA_HISTOGRAM_CUSTOM_TIMES("Prerender.SimulatedLocalBrowsingPLT",
|
| - new_plt,
|
| - base::TimeDelta::FromMilliseconds(10),
|
| - base::TimeDelta::FromSeconds(60),
|
| - 100);
|
| - }
|
| - }
|
| -}
|
| -
|
| -bool PrerenderLocalPredictor::IsPrerenderStillValid(
|
| - PrerenderLocalPredictor::PrerenderProperties* prerender) const {
|
| - return (prerender &&
|
| - (prerender->start_time +
|
| - base::TimeDelta::FromMilliseconds(GetMaxLocalPredictionTimeMs()))
|
| - > GetCurrentTime());
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::RecordEvent(
|
| - PrerenderLocalPredictor::Event event) const {
|
| - UMA_HISTOGRAM_ENUMERATION("Prerender.LocalPredictorEvent",
|
| - event, PrerenderLocalPredictor::EVENT_MAX_VALUE);
|
| -}
|
| -
|
| -bool PrerenderLocalPredictor::DoesPrerenderMatchPLTRecord(
|
| - PrerenderProperties* prerender,
|
| - const GURL& url,
|
| - base::TimeDelta plt) const {
|
| - if (prerender && prerender->start_time < GetCurrentTime() - plt) {
|
| - if (prerender->url.is_empty())
|
| - RecordEvent(EVENT_ERROR_NO_PRERENDER_URL_FOR_PLT);
|
| - return (prerender->url == url);
|
| - } else {
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -PrerenderLocalPredictor::PrerenderProperties*
|
| -PrerenderLocalPredictor::GetIssuedPrerenderSlotForPriority(const GURL& url,
|
| - double priority) {
|
| - int num_prerenders = GetLocalPredictorMaxConcurrentPrerenders();
|
| - while (static_cast<int>(issued_prerenders_.size()) < num_prerenders)
|
| - issued_prerenders_.push_back(new PrerenderProperties());
|
| - // First, check if we already have a prerender for the same URL issued.
|
| - // If yes, we don't want to prerender this URL again, so we return NULL
|
| - // (on matching slot found).
|
| - for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) {
|
| - PrerenderProperties* p = issued_prerenders_[i];
|
| - DCHECK(p != NULL);
|
| - if (p->prerender_handle && p->prerender_handle->IsPrerendering() &&
|
| - p->prerender_handle->Matches(url, NULL)) {
|
| - return NULL;
|
| - }
|
| - }
|
| - // Otherwise, let's see if there are any empty slots. If yes, return the first
|
| - // one we find. Otherwise, if the lowest priority prerender has a lower
|
| - // priority than the page we want to prerender, use its slot.
|
| - PrerenderProperties* lowest_priority_prerender = NULL;
|
| - for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) {
|
| - PrerenderProperties* p = issued_prerenders_[i];
|
| - DCHECK(p != NULL);
|
| - if (!p->prerender_handle || !p->prerender_handle->IsPrerendering())
|
| - return p;
|
| - double decayed_priority = p->GetCurrentDecayedPriority();
|
| - if (decayed_priority > priority)
|
| - continue;
|
| - if (lowest_priority_prerender == NULL ||
|
| - lowest_priority_prerender->GetCurrentDecayedPriority() >
|
| - decayed_priority) {
|
| - lowest_priority_prerender = p;
|
| - }
|
| - }
|
| - return lowest_priority_prerender;
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::ContinuePrerenderCheck(
|
| - scoped_ptr<CandidatePrerenderInfo> info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - TIMING_HISTOGRAM("Prerender.LocalPredictorLoggedInLookupTime",
|
| - base::Time::Now() - info->start_time_);
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_STARTED);
|
| - if (info->candidate_urls_.size() == 0) {
|
| - RecordEvent(EVENT_NO_PRERENDER_CANDIDATES);
|
| - return;
|
| - }
|
| - scoped_ptr<LocalPredictorURLInfo> url_info;
|
| -#if defined(FULL_SAFE_BROWSING)
|
| - scoped_refptr<SafeBrowsingDatabaseManager> sb_db_manager =
|
| - g_browser_process->safe_browsing_service()->database_manager();
|
| -#endif
|
| - int num_issued = 0;
|
| - for (int i = 0; i < static_cast<int>(info->candidate_urls_.size()); i++) {
|
| - if (num_issued >= GetLocalPredictorMaxLaunchPrerenders())
|
| - return;
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL);
|
| - url_info.reset(new LocalPredictorURLInfo(info->candidate_urls_[i]));
|
| - if (url_info->local_history_based) {
|
| - if (SkipLocalPredictorLocalCandidates()) {
|
| - url_info.reset(NULL);
|
| - continue;
|
| - }
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_LOCAL);
|
| - }
|
| - if (!url_info->local_history_based) {
|
| - if (SkipLocalPredictorServiceCandidates()) {
|
| - url_info.reset(NULL);
|
| - continue;
|
| - }
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_SERVICE);
|
| - }
|
| -
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_EXAMINE_NEXT_URL_NOT_SKIPPED);
|
| -
|
| - // We need to check whether we can issue a prerender for this URL.
|
| - // We test a set of conditions. Each condition can either rule out
|
| - // a prerender (in which case we reset url_info, so that it will not
|
| - // be prerendered, and we continue, which means try the next candidate
|
| - // URL), or it can be sufficient to issue the prerender without any
|
| - // further checks (in which case we just break).
|
| - // The order of the checks is critical, because it prescribes the logic
|
| - // we use here to decide what to prerender.
|
| - if (!url_info->url_lookup_success) {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_NO_URL);
|
| - url_info.reset(NULL);
|
| - continue;
|
| - }
|
| - if (!SkipLocalPredictorFragment() &&
|
| - URLsIdenticalIgnoringFragments(info->source_url_.url,
|
| - url_info->url)) {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_URLS_IDENTICAL_BUT_FRAGMENT);
|
| - url_info.reset(NULL);
|
| - continue;
|
| - }
|
| - if (!SkipLocalPredictorHTTPS() && url_info->url.SchemeIs("https")) {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_HTTPS);
|
| - url_info.reset(NULL);
|
| - continue;
|
| - }
|
| - if (IsRootPageURL(url_info->url)) {
|
| - // For root pages, we assume that they are reasonably safe, and we
|
| - // will just prerender them without any additional checks.
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ROOT_PAGE);
|
| - IssuePrerender(info.get(), url_info.get());
|
| - num_issued++;
|
| - continue;
|
| - }
|
| - if (IsLogOutURL(url_info->url)) {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_LOGOUT_URL);
|
| - url_info.reset(NULL);
|
| - continue;
|
| - }
|
| - if (IsLogInURL(url_info->url)) {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_LOGIN_URL);
|
| - url_info.reset(NULL);
|
| - continue;
|
| - }
|
| -#if defined(FULL_SAFE_BROWSING)
|
| - if (!SkipLocalPredictorWhitelist() && sb_db_manager.get() &&
|
| - sb_db_manager->CheckSideEffectFreeWhitelistUrl(url_info->url)) {
|
| - // If a page is on the side-effect free whitelist, we will just prerender
|
| - // it without any additional checks.
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SIDE_EFFECT_FREE_WHITELIST);
|
| - IssuePrerender(info.get(), url_info.get());
|
| - num_issued++;
|
| - continue;
|
| - }
|
| -#endif
|
| - if (!SkipLocalPredictorServiceWhitelist() &&
|
| - url_info->service_whitelist && url_info->service_whitelist_lookup_ok) {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ON_SERVICE_WHITELIST);
|
| - IssuePrerender(info.get(), url_info.get());
|
| - num_issued++;
|
| - continue;
|
| - }
|
| - if (!SkipLocalPredictorLoggedIn() &&
|
| - !url_info->logged_in && url_info->logged_in_lookup_ok) {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_NOT_LOGGED_IN);
|
| - IssuePrerender(info.get(), url_info.get());
|
| - num_issued++;
|
| - continue;
|
| - }
|
| - if (!SkipLocalPredictorDefaultNoPrerender()) {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_NOT_PRERENDERING);
|
| - url_info.reset(NULL);
|
| - } else {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_FALLTHROUGH_PRERENDERING);
|
| - IssuePrerender(info.get(), url_info.get());
|
| - num_issued++;
|
| - continue;
|
| - }
|
| - }
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::IssuePrerender(
|
| - CandidatePrerenderInfo* info,
|
| - LocalPredictorURLInfo* url_info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - RecordEvent(EVENT_ISSUE_PRERENDER_CALLED);
|
| - if (prefetch_list_->AddURL(url_info->url)) {
|
| - RecordEvent(EVENT_PREFETCH_LIST_ADDED);
|
| - // If we are prefetching rather than prerendering, now is the time to launch
|
| - // the prefetch.
|
| - if (IsLocalPredictorPrerenderPrefetchEnabled()) {
|
| - RecordEvent(EVENT_ISSUE_PRERENDER_PREFETCH_ENABLED);
|
| - // Obtain the render frame host that caused this prefetch.
|
| - RenderFrameHost* rfh = RenderFrameHost::FromID(info->render_process_id_,
|
| - info->render_frame_id_);
|
| - // If it is still alive, launch the prefresh.
|
| - if (rfh) {
|
| - rfh->Send(new PrefetchMsg_Prefetch(rfh->GetRoutingID(), url_info->url));
|
| - RecordEvent(EVENT_ISSUE_PRERENDER_PREFETCH_ISSUED);
|
| - }
|
| - }
|
| - }
|
| - PrerenderProperties* prerender_properties =
|
| - GetIssuedPrerenderSlotForPriority(url_info->url, url_info->priority);
|
| - if (!prerender_properties) {
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_PRIORITY_TOO_LOW);
|
| - return;
|
| - }
|
| - RecordEvent(EVENT_CONTINUE_PRERENDER_CHECK_ISSUING_PRERENDER);
|
| - DCHECK(prerender_properties != NULL);
|
| - DCHECK(info != NULL);
|
| - DCHECK(url_info != NULL);
|
| - if (!IsLocalPredictorPrerenderLaunchEnabled())
|
| - return;
|
| - URLID url_id = url_info->id;
|
| - const GURL& url = url_info->url;
|
| - double priority = url_info->priority;
|
| - base::Time current_time = GetCurrentTime();
|
| - RecordEvent(EVENT_ISSUING_PRERENDER);
|
| -
|
| - // Issue the prerender and obtain a new handle.
|
| - scoped_ptr<prerender::PrerenderHandle> new_prerender_handle(
|
| - prerender_manager_->AddPrerenderFromLocalPredictor(
|
| - url, info->session_storage_namespace_.get(), *(info->size_)));
|
| -
|
| - // Check if this is a duplicate of an existing prerender. If yes, clean up
|
| - // the new handle.
|
| - for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) {
|
| - PrerenderProperties* p = issued_prerenders_[i];
|
| - DCHECK(p != NULL);
|
| - if (new_prerender_handle &&
|
| - new_prerender_handle->RepresentingSamePrerenderAs(
|
| - p->prerender_handle.get())) {
|
| - new_prerender_handle->OnCancel();
|
| - new_prerender_handle.reset(NULL);
|
| - RecordEvent(EVENT_ISSUE_PRERENDER_ALREADY_PRERENDERING);
|
| - break;
|
| - }
|
| - }
|
| -
|
| - if (new_prerender_handle.get()) {
|
| - RecordEvent(EVENT_ISSUE_PRERENDER_NEW_PRERENDER);
|
| - // The new prerender does not match any existing prerenders. Update
|
| - // prerender_properties so that it reflects the new entry.
|
| - prerender_properties->url_id = url_id;
|
| - prerender_properties->url = url;
|
| - prerender_properties->priority = priority;
|
| - prerender_properties->start_time = current_time;
|
| - prerender_properties->actual_start_time = current_time;
|
| - prerender_properties->would_have_matched = false;
|
| - prerender_properties->prerender_handle.swap(new_prerender_handle);
|
| - // new_prerender_handle now represents the old previou prerender that we
|
| - // are replacing. So we need to cancel it.
|
| - if (new_prerender_handle) {
|
| - new_prerender_handle->OnCancel();
|
| - RecordEvent(EVENT_ISSUE_PRERENDER_CANCELLED_OLD_PRERENDER);
|
| - }
|
| - }
|
| -
|
| - RecordEvent(EVENT_ADD_VISIT_PRERENDERING);
|
| - if (current_prerender_.get() && current_prerender_->url_id == url_id) {
|
| - RecordEvent(EVENT_ADD_VISIT_PRERENDERING_EXTENDED);
|
| - if (priority > current_prerender_->priority)
|
| - current_prerender_->priority = priority;
|
| - // If the prerender already existed, we want to extend it. However,
|
| - // we do not want to set its start_time to the current time to
|
| - // disadvantage PLT computations when the prerender is swapped in.
|
| - // So we set the new start time to current_time - 10s (since the vast
|
| - // majority of PLTs are < 10s), provided that is not before the actual
|
| - // time the prerender was started (so as to not artificially advantage
|
| - // the PLT computation).
|
| - base::Time simulated_new_start_time =
|
| - current_time - base::TimeDelta::FromSeconds(10);
|
| - if (simulated_new_start_time > current_prerender_->start_time)
|
| - current_prerender_->start_time = simulated_new_start_time;
|
| - } else {
|
| - current_prerender_.reset(
|
| - new PrerenderProperties(url_id, url, priority, current_time));
|
| - }
|
| - current_prerender_->actual_start_time = current_time;
|
| -}
|
| -
|
| -void PrerenderLocalPredictor::OnTabHelperURLSeen(
|
| - const GURL& url, WebContents* web_contents) {
|
| - RecordEvent(EVENT_TAB_HELPER_URL_SEEN);
|
| -
|
| - if (prefetch_list_->MarkURLSeen(url, PrefetchList::SEEN_TABCONTENTS_OBSERVER))
|
| - RecordEvent(EVENT_PREFETCH_LIST_SEEN_TABCONTENTS);
|
| - bool browser_navigate_initiated = false;
|
| - const content::NavigationEntry* entry =
|
| - web_contents->GetController().GetPendingEntry();
|
| - if (entry) {
|
| - base::string16 result;
|
| - browser_navigate_initiated =
|
| - entry->GetExtraData(kChromeNavigateExtraDataKey, &result);
|
| - }
|
| -
|
| - // If the namespace matches and the URL matches, we might be able to swap
|
| - // in. However, the actual code initating the swapin is in the renderer
|
| - // and is checking for other criteria (such as POSTs). There may
|
| - // also be conditions when a swapin should happen but does not. By recording
|
| - // the two previous events, we can keep an eye on the magnitude of the
|
| - // discrepancy.
|
| -
|
| - PrerenderProperties* best_matched_prerender = NULL;
|
| - bool session_storage_namespace_matches = false;
|
| - SessionStorageNamespace* tab_session_storage_namespace =
|
| - web_contents->GetController().GetDefaultSessionStorageNamespace();
|
| - for (int i = 0; i < static_cast<int>(issued_prerenders_.size()); i++) {
|
| - PrerenderProperties* p = issued_prerenders_[i];
|
| - DCHECK(p != NULL);
|
| - if (!p->prerender_handle.get() ||
|
| - !p->prerender_handle->Matches(url, NULL) ||
|
| - p->would_have_matched) {
|
| - continue;
|
| - }
|
| - if (!best_matched_prerender || !session_storage_namespace_matches) {
|
| - best_matched_prerender = p;
|
| - session_storage_namespace_matches =
|
| - p->prerender_handle->Matches(url, tab_session_storage_namespace);
|
| - }
|
| - }
|
| - if (best_matched_prerender) {
|
| - RecordEvent(EVENT_TAB_HELPER_URL_SEEN_MATCH);
|
| - if (entry)
|
| - RecordEvent(EVENT_TAB_HELPER_URL_SEEN_MATCH_ENTRY);
|
| - if (browser_navigate_initiated)
|
| - RecordEvent(EVENT_TAB_HELPER_URL_SEEN_MATCH_BROWSER_NAVIGATE);
|
| - best_matched_prerender->would_have_matched = true;
|
| - if (session_storage_namespace_matches) {
|
| - RecordEvent(EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MATCH);
|
| - if (entry)
|
| - RecordEvent(EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MATCH_ENTRY);
|
| - if (browser_navigate_initiated)
|
| - RecordEvent(EVENT_TAB_HELPER_URL_SEEN_NAMESPACE_MATCH_BROWSER_NAVIGATE);
|
| - }
|
| - }
|
| -}
|
| -
|
| -} // namespace prerender
|
|
|