summaryrefslogtreecommitdiffstats
path: root/chrome/browser/google_url_tracker.cc
blob: 6dfdbd668e0164af4e906be6331083f74f34994f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright (c) 2006-2008 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/google_url_tracker.h"

#include "base/compiler_specific.h"
#include "base/string_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "net/base/load_flags.h"

const char GoogleURLTracker::kDefaultGoogleHomepage[] =
    "http://www.google.com/";

GoogleURLTracker::GoogleURLTracker()
    : google_url_(WideToUTF8(g_browser_process->local_state()->GetString(
          prefs::kLastKnownGoogleURL))),
      ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_factory_(this)),
      in_startup_sleep_(true),
      already_fetched_(false),
      need_to_fetch_(false),
      request_context_available_(!!Profile::GetDefaultRequestContext()) {
  NotificationService::current()->AddObserver(this,
      NOTIFY_DEFAULT_REQUEST_CONTEXT_AVAILABLE,
      NotificationService::AllSources());

  // 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.
  static const int kStartFetchDelayMS = 5000;
  MessageLoop::current()->PostDelayedTask(FROM_HERE,
      fetcher_factory_.NewRunnableMethod(&GoogleURLTracker::FinishSleep),
      kStartFetchDelayMS);
}

GoogleURLTracker::~GoogleURLTracker() {
  NotificationService::current()->RemoveObserver(this,
      NOTIFY_DEFAULT_REQUEST_CONTEXT_AVAILABLE,
      NotificationService::AllSources());
}

// static
GURL GoogleURLTracker::GoogleURL() {
  const GoogleURLTracker* const tracker =
      g_browser_process->google_url_tracker();
  return tracker ? tracker->google_url_ : GURL(kDefaultGoogleHomepage);
}

// static
void GoogleURLTracker::RequestServerCheck() {
  GoogleURLTracker* const tracker = g_browser_process->google_url_tracker();
  if (tracker)
    tracker->SetNeedToFetch();
}

// static
void GoogleURLTracker::RegisterPrefs(PrefService* prefs) {
  prefs->RegisterStringPref(prefs::kLastKnownGoogleURL,
                            ASCIIToWide(kDefaultGoogleHomepage));
}

// static
bool GoogleURLTracker::CheckAndConvertToGoogleBaseURL(const GURL& url,
                                                      GURL* base_url) {
  // Only allow updates if the new URL appears to be on google.xx, google.co.xx,
  // or google.com.xx.  Cases other than this are either malicious, or doorway
  // pages for hotel WiFi connections and the like.
  // NOTE: Obviously the above is not as secure as whitelisting all known Google
  // frontpage domains, but for now we're trying to prevent login pages etc.
  // from ruining the user experience, rather than preventing hijacking.
  std::vector<std::string> host_components;
  SplitStringDontTrim(url.host(), '.', &host_components);
  if (host_components.size() < 2)
    return false;
  std::string& component = host_components[host_components.size() - 2];
  if (component != "google") {
    if ((host_components.size() < 3) ||
        ((component != "co") && (component != "com")))
      return false;
    if (host_components[host_components.size() - 3] != "google")
      return false;
  }

  // If the url's path does not begin "/intl/", reset it to "/".  Other paths
  // represent services such as iGoogle that are irrelevant to the baseURL.
  *base_url = url.path().compare(0, 6, "/intl/") ? url.GetWithEmptyPath() : url;
  return true;
}

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_ ||
      !request_context_available_)
    return;

  need_to_fetch_ = false;
  already_fetched_ = true;  // If fetching fails, we don't bother to reset this
                            // flag; we just live with an outdated URL for this
                            // run of the browser.
  fetcher_.reset(new URLFetcher(GURL(kDefaultGoogleHomepage), URLFetcher::HEAD,
                                this));
  // We don't want this fetch to affect existing state in the profile.  For
  // example, if a user has no Google cookies, this automatic check should not
  // cause one to be set, lest we alarm the user.
  fetcher_->set_load_flags(net::LOAD_DISABLE_CACHE |
                           net::LOAD_DO_NOT_SAVE_COOKIES);
  fetcher_->set_request_context(Profile::GetDefaultRequestContext());
  fetcher_->Start();
}

void GoogleURLTracker::OnURLFetchComplete(const URLFetcher* source,
                                          const GURL& url,
                                          const URLRequestStatus& status,
                                          int response_code,
                                          const ResponseCookies& cookies,
                                          const std::string& data) {
  // Delete the fetcher on this function's exit.
  scoped_ptr<URLFetcher> clean_up_fetcher(fetcher_.release());

  // Don't update the URL if the request didn't succeed.
  if (!status.is_success() || (response_code != 200))
    return;

  // See if the response URL was one we want to use, and if so, convert to the
  // appropriate Google base URL.
  GURL base_url;
  if (!CheckAndConvertToGoogleBaseURL(url, &base_url))
    return;

  // Update the saved base URL if it has changed.
  const std::wstring base_url_str(UTF8ToWide(base_url.spec()));
  if (g_browser_process->local_state()->GetString(prefs::kLastKnownGoogleURL) !=
      base_url_str) {
    g_browser_process->local_state()->SetString(prefs::kLastKnownGoogleURL,
                                                base_url_str);
    google_url_ = base_url;
    NotificationService::current()->Notify(NOTIFY_GOOGLE_URL_UPDATED,
                                           NotificationService::AllSources(),
                                           NotificationService::NoDetails());
  }
}

void GoogleURLTracker::Observe(NotificationType type,
                               const NotificationSource& source,
                               const NotificationDetails& details) {
  DCHECK_EQ(NOTIFY_DEFAULT_REQUEST_CONTEXT_AVAILABLE, type);
  request_context_available_ = true;
  StartFetchIfDesirable();
}