blob: fce813c5a082c01eca81489ed82815a1856231b4 [file] [log] [blame]
// Copyright 2014 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 "components/google/core/browser/google_url_tracker.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_util.h"
#include "components/google/core/browser/google_pref_names.h"
#include "components/google/core/browser/google_switches.h"
#include "components/google/core/browser/google_util.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request_status.h"
const char GoogleURLTracker::kDefaultGoogleHomepage[] =
"http://www.google.com/";
const char GoogleURLTracker::kSearchDomainCheckURL[] =
"https://www.google.com/searchdomaincheck?format=url&type=chrome";
GoogleURLTracker::GoogleURLTracker(scoped_ptr<GoogleURLTrackerClient> client,
Mode mode)
: client_(client.Pass()),
google_url_(mode == UNIT_TEST_MODE ?
kDefaultGoogleHomepage :
client_->GetPrefs()->GetString(prefs::kLastKnownGoogleURL)),
fetcher_id_(0),
in_startup_sleep_(true),
already_fetched_(false),
need_to_fetch_(false),
weak_ptr_factory_(this) {
net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
client_->set_google_url_tracker(this);
// Because this function can be called during startup, when kicking off a URL
// fetch can eat up 20 ms of time, we delay five seconds, which is hopefully
// long enough to be after startup, but still get results back quickly.
// Ideally, instead of this timer, we'd do something like "check if the
// browser is starting up, and if so, come back later", but there is currently
// no function to do this.
//
// In UNIT_TEST_MODE, where we want to explicitly control when the tracker
// "wakes up", we do nothing at all.
if (mode == NORMAL_MODE) {
static const int kStartFetchDelayMS = 5000;
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&GoogleURLTracker::FinishSleep,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kStartFetchDelayMS));
}
}
GoogleURLTracker::~GoogleURLTracker() {
}
void GoogleURLTracker::RequestServerCheck(bool force) {
// If this instance already has a fetcher, SetNeedToFetch() is unnecessary,
// and changing |already_fetched_| is wrong.
if (!fetcher_) {
if (force)
already_fetched_ = false;
SetNeedToFetch();
}
}
scoped_ptr<GoogleURLTracker::Subscription> GoogleURLTracker::RegisterCallback(
const OnGoogleURLUpdatedCallback& cb) {
return callback_list_.Add(cb);
}
void GoogleURLTracker::OnURLFetchComplete(const net::URLFetcher* source) {
// Delete the fetcher on this function's exit.
scoped_ptr<net::URLFetcher> clean_up_fetcher(fetcher_.release());
// Don't update the URL if the request didn't succeed.
if (!source->GetStatus().is_success() || (source->GetResponseCode() != 200)) {
already_fetched_ = false;
return;
}
// See if the response data was valid. It should be
// "<scheme>://[www.]google.<TLD>/".
std::string url_str;
source->GetResponseAsString(&url_str);
base::TrimWhitespace(url_str, base::TRIM_ALL, &url_str);
GURL url(url_str);
if (!url.is_valid() || (url.path().length() > 1) || url.has_query() ||
url.has_ref() ||
!google_util::IsGoogleDomainUrl(url,
google_util::DISALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS))
return;
if (url != google_url_) {
google_url_ = url;
client_->GetPrefs()->SetString(prefs::kLastKnownGoogleURL,
google_url_.spec());
callback_list_.Notify();
}
}
void GoogleURLTracker::OnNetworkChanged(
net::NetworkChangeNotifier::ConnectionType type) {
// Ignore destructive signals.
if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
return;
already_fetched_ = false;
StartFetchIfDesirable();
}
void GoogleURLTracker::Shutdown() {
client_.reset();
fetcher_.reset();
weak_ptr_factory_.InvalidateWeakPtrs();
net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}
void GoogleURLTracker::SetNeedToFetch() {
need_to_fetch_ = true;
StartFetchIfDesirable();
}
void GoogleURLTracker::FinishSleep() {
in_startup_sleep_ = false;
StartFetchIfDesirable();
}
void GoogleURLTracker::StartFetchIfDesirable() {
// Bail if a fetch isn't appropriate right now. This function will be called
// again each time one of the preconditions changes, so we'll fetch
// immediately once all of them are met.
//
// See comments in header on the class, on RequestServerCheck(), and on the
// various members here for more detail on exactly what the conditions are.
if (in_startup_sleep_ || already_fetched_ || !need_to_fetch_)
return;
// Some switches should disable the Google URL tracker entirely. If we can't
// do background networking, we can't do the necessary fetch, and if the user
// specified a Google base URL manually, we shouldn't bother to look up any
// alternatives or offer to switch to them.
if (!client_->IsBackgroundNetworkingEnabled() ||
CommandLine::ForCurrentProcess()->HasSwitch(switches::kGoogleBaseURL))
return;
already_fetched_ = true;
fetcher_.reset(net::URLFetcher::Create(
fetcher_id_, GURL(kSearchDomainCheckURL), net::URLFetcher::GET, this));
++fetcher_id_;
// We don't want this fetch to set new entries in the cache or cookies, lest
// we alarm the user.
fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE |
net::LOAD_DO_NOT_SAVE_COOKIES);
fetcher_->SetRequestContext(client_->GetRequestContext());
// Configure to retry at most kMaxRetries times for 5xx errors.
static const int kMaxRetries = 5;
fetcher_->SetMaxRetriesOn5xx(kMaxRetries);
// Also retry kMaxRetries times on network change errors. A network change can
// propagate through Chrome in various stages, so it's possible for this code
// to be reached via OnNetworkChanged(), and then have the fetch we kick off
// be canceled due to e.g. the DNS server changing at a later time. In general
// it's not possible to ensure that by the time we reach here any requests we
// start won't be canceled in this fashion, so retrying is the best we can do.
fetcher_->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries);
fetcher_->Start();
}