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

Side by Side Diff: chrome/browser/google_url_tracker.cc

Issue 3171019: Reland r56483 - Monitor network change in GoogleURLTracker (Closed)
Patch Set: check Chrome Frame Created 10 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/google_url_tracker.h ('k') | chrome/browser/google_url_tracker_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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/google_url_tracker.h" 5 #include "chrome/browser/google_url_tracker.h"
6 6
7 #include <vector> 7 #include <vector>
8 8
9 #include "app/l10n_util.h"
9 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
10 #include "base/string_util.h" 11 #include "base/string_util.h"
11 #include "base/utf_string_conversions.h" 12 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/pref_service.h" 14 #include "chrome/browser/pref_service.h"
14 #include "chrome/browser/profile.h" 15 #include "chrome/browser/profile.h"
16 #include "chrome/browser/search_engines/template_url.h"
17 #include "chrome/browser/tab_contents/infobar_delegate.h"
18 #include "chrome/browser/tab_contents/navigation_controller.h"
19 #include "chrome/browser/tab_contents/tab_contents.h"
20 #include "chrome/common/net/url_fetcher_protect.h"
15 #include "chrome/common/notification_service.h" 21 #include "chrome/common/notification_service.h"
16 #include "chrome/common/pref_names.h" 22 #include "chrome/common/pref_names.h"
23 #include "grit/generated_resources.h"
17 #include "net/base/load_flags.h" 24 #include "net/base/load_flags.h"
18 #include "net/url_request/url_request_status.h" 25 #include "net/url_request/url_request_status.h"
19 26
20 const char GoogleURLTracker::kDefaultGoogleHomepage[] = 27 const char GoogleURLTracker::kDefaultGoogleHomepage[] =
21 "http://www.google.com/"; 28 "http://www.google.com/";
29 const char GoogleURLTracker::kSearchDomainCheckURL[] =
30 "https://www.google.com/searchdomaincheck?format=domain&type=chrome";
31
32 namespace {
33
34 class GoogleURLTrackerInfoBarDelegate : public ConfirmInfoBarDelegate {
35 public:
36 GoogleURLTrackerInfoBarDelegate(TabContents* tab_contents,
37 GoogleURLTracker* google_url_tracker,
38 const GURL& new_google_url)
39 : ConfirmInfoBarDelegate(tab_contents),
40 google_url_tracker_(google_url_tracker),
41 new_google_url_(new_google_url) {}
42
43 // ConfirmInfoBarDelegate
44 virtual string16 GetMessageText() const {
45 // TODO(ukai): change new_google_url to google_base_domain?
46 return l10n_util::GetStringFUTF16(IDS_GOOGLE_URL_TRACKER_INFOBAR_MESSAGE,
47 UTF8ToUTF16(new_google_url_.spec()));
48 }
49
50 virtual int GetButtons() const {
51 return BUTTON_OK | BUTTON_CANCEL;
52 }
53
54 virtual string16 GetButtonLabel(InfoBarButton button) const {
55 return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
56 IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL :
57 IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL);
58 }
59
60 virtual bool Accept() {
61 google_url_tracker_->AcceptGoogleURL(new_google_url_);
62 google_url_tracker_->RedoSearch();
63 return true;
64 }
65
66 virtual void InfoBarClosed() {
67 google_url_tracker_->InfoBarClosed();
68 delete this;
69 }
70
71 private:
72 virtual ~GoogleURLTrackerInfoBarDelegate() {}
73
74 GoogleURLTracker* google_url_tracker_;
75 const GURL new_google_url_;
76
77 DISALLOW_COPY_AND_ASSIGN(GoogleURLTrackerInfoBarDelegate);
78 };
79
80 } // anonymous namespace
81
82 InfoBarDelegate* GoogleURLTracker::InfoBarDelegateFactory::CreateInfoBar(
83 TabContents* tab_contents,
84 GoogleURLTracker* google_url_tracker,
85 const GURL& new_google_url) {
86 InfoBarDelegate* infobar =
87 new GoogleURLTrackerInfoBarDelegate(tab_contents,
88 google_url_tracker,
89 new_google_url);
90 tab_contents->AddInfoBar(infobar);
91 return infobar;
92 }
22 93
23 GoogleURLTracker::GoogleURLTracker() 94 GoogleURLTracker::GoogleURLTracker()
24 : google_url_(g_browser_process->local_state()->GetString( 95 : google_url_(g_browser_process->local_state()->GetString(
25 prefs::kLastKnownGoogleURL)), 96 prefs::kLastKnownGoogleURL)),
26 ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_factory_(this)), 97 ALLOW_THIS_IN_INITIALIZER_LIST(runnable_method_factory_(this)),
98 fetcher_id_(0),
27 in_startup_sleep_(true), 99 in_startup_sleep_(true),
28 already_fetched_(false), 100 already_fetched_(false),
29 need_to_fetch_(false), 101 need_to_fetch_(false),
30 request_context_available_(!!Profile::GetDefaultRequestContext()) { 102 request_context_available_(!!Profile::GetDefaultRequestContext()),
103 need_to_prompt_(false),
104 controller_(NULL),
105 infobar_factory_(new InfoBarDelegateFactory),
106 infobar_(NULL) {
31 registrar_.Add(this, NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE, 107 registrar_.Add(this, NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE,
32 NotificationService::AllSources()); 108 NotificationService::AllSources());
33 109
110 net::NetworkChangeNotifier::AddObserver(this);
111
112 // Configure to max_retries at most kMaxRetries times for 5xx errors.
113 URLFetcherProtectEntry* protect =
114 URLFetcherProtectManager::GetInstance()->Register(
115 GURL(kSearchDomainCheckURL).host());
116 static const int kMaxRetries = 5;
117 protect->SetMaxRetries(kMaxRetries);
118
34 // Because this function can be called during startup, when kicking off a URL 119 // Because this function can be called during startup, when kicking off a URL
35 // fetch can eat up 20 ms of time, we delay five seconds, which is hopefully 120 // fetch can eat up 20 ms of time, we delay five seconds, which is hopefully
36 // long enough to be after startup, but still get results back quickly. 121 // long enough to be after startup, but still get results back quickly.
37 // Ideally, instead of this timer, we'd do something like "check if the 122 // Ideally, instead of this timer, we'd do something like "check if the
38 // browser is starting up, and if so, come back later", but there is currently 123 // browser is starting up, and if so, come back later", but there is currently
39 // no function to do this. 124 // no function to do this.
40 static const int kStartFetchDelayMS = 5000; 125 static const int kStartFetchDelayMS = 5000;
41 MessageLoop::current()->PostDelayedTask(FROM_HERE, 126 MessageLoop::current()->PostDelayedTask(FROM_HERE,
42 fetcher_factory_.NewRunnableMethod(&GoogleURLTracker::FinishSleep), 127 runnable_method_factory_.NewRunnableMethod(
128 &GoogleURLTracker::FinishSleep),
43 kStartFetchDelayMS); 129 kStartFetchDelayMS);
44 } 130 }
45 131
46 GoogleURLTracker::~GoogleURLTracker() { 132 GoogleURLTracker::~GoogleURLTracker() {
133 runnable_method_factory_.RevokeAll();
134 net::NetworkChangeNotifier::RemoveObserver(this);
47 } 135 }
48 136
49 // static 137 // static
50 GURL GoogleURLTracker::GoogleURL() { 138 GURL GoogleURLTracker::GoogleURL() {
51 const GoogleURLTracker* const tracker = 139 const GoogleURLTracker* const tracker =
52 g_browser_process->google_url_tracker(); 140 g_browser_process->google_url_tracker();
53 return tracker ? tracker->google_url_ : GURL(kDefaultGoogleHomepage); 141 return tracker ? tracker->google_url_ : GURL(kDefaultGoogleHomepage);
54 } 142 }
55 143
56 // static 144 // static
57 void GoogleURLTracker::RequestServerCheck() { 145 void GoogleURLTracker::RequestServerCheck() {
58 GoogleURLTracker* const tracker = g_browser_process->google_url_tracker(); 146 GoogleURLTracker* const tracker = g_browser_process->google_url_tracker();
59 if (tracker) 147 if (tracker)
60 tracker->SetNeedToFetch(); 148 tracker->SetNeedToFetch();
61 } 149 }
62 150
63 // static 151 // static
64 void GoogleURLTracker::RegisterPrefs(PrefService* prefs) { 152 void GoogleURLTracker::RegisterPrefs(PrefService* prefs) {
65 prefs->RegisterStringPref(prefs::kLastKnownGoogleURL, 153 prefs->RegisterStringPref(prefs::kLastKnownGoogleURL,
66 kDefaultGoogleHomepage); 154 kDefaultGoogleHomepage);
155 prefs->RegisterStringPref(prefs::kLastPromptedGoogleURL, std::string());
67 } 156 }
68 157
69 // static 158 // static
70 bool GoogleURLTracker::CheckAndConvertToGoogleBaseURL(const GURL& url, 159 void GoogleURLTracker::GoogleURLSearchCommitted() {
71 GURL* base_url) { 160 GoogleURLTracker* tracker = g_browser_process->google_url_tracker();
72 // Only allow updates if the new URL appears to be on google.xx, google.co.xx, 161 if (tracker)
73 // or google.com.xx. Cases other than this are either malicious, or doorway 162 tracker->SearchCommitted();
74 // pages for hotel WiFi connections and the like.
75 // NOTE: Obviously the above is not as secure as whitelisting all known Google
76 // frontpage domains, but for now we're trying to prevent login pages etc.
77 // from ruining the user experience, rather than preventing hijacking.
78 std::vector<std::string> host_components;
79 SplitStringDontTrim(url.host(), '.', &host_components);
80 if (host_components.size() < 2)
81 return false;
82 size_t google_component = host_components.size() - 2;
83 const std::string& component = host_components[google_component];
84 if (component != "google") {
85 if ((host_components.size() < 3) ||
86 ((component != "co") && (component != "com")))
87 return false;
88 google_component = host_components.size() - 3;
89 if (host_components[google_component] != "google")
90 return false;
91 }
92 // For Google employees only: If the URL appears to be on
93 // [*.]corp.google.com, it's likely a doorway (e.g.
94 // wifi.corp.google.com), so ignore it.
95 if ((google_component > 0) &&
96 (host_components[google_component - 1] == "corp"))
97 return false;
98
99 // If the url's path does not begin "/intl/", reset it to "/". Other paths
100 // represent services such as iGoogle that are irrelevant to the baseURL.
101 *base_url = url.path().compare(0, 6, "/intl/") ? url.GetWithEmptyPath() : url;
102 return true;
103 } 163 }
104 164
105 void GoogleURLTracker::SetNeedToFetch() { 165 void GoogleURLTracker::SetNeedToFetch() {
106 need_to_fetch_ = true; 166 need_to_fetch_ = true;
107 StartFetchIfDesirable(); 167 StartFetchIfDesirable();
108 } 168 }
109 169
110 void GoogleURLTracker::FinishSleep() { 170 void GoogleURLTracker::FinishSleep() {
111 in_startup_sleep_ = false; 171 in_startup_sleep_ = false;
112 StartFetchIfDesirable(); 172 StartFetchIfDesirable();
113 } 173 }
114 174
115 void GoogleURLTracker::StartFetchIfDesirable() { 175 void GoogleURLTracker::StartFetchIfDesirable() {
116 // Bail if a fetch isn't appropriate right now. This function will be called 176 // Bail if a fetch isn't appropriate right now. This function will be called
117 // again each time one of the preconditions changes, so we'll fetch 177 // again each time one of the preconditions changes, so we'll fetch
118 // immediately once all of them are met. 178 // immediately once all of them are met.
119 // 179 //
120 // See comments in header on the class, on RequestServerCheck(), and on the 180 // See comments in header on the class, on RequestServerCheck(), and on the
121 // various members here for more detail on exactly what the conditions are. 181 // various members here for more detail on exactly what the conditions are.
122 if (in_startup_sleep_ || already_fetched_ || !need_to_fetch_ || 182 if (in_startup_sleep_ || already_fetched_ || !need_to_fetch_ ||
123 !request_context_available_) 183 !request_context_available_)
124 return; 184 return;
125 185
126 need_to_fetch_ = false; 186 already_fetched_ = true;
127 already_fetched_ = true; // If fetching fails, we don't bother to reset this 187 fetcher_.reset(URLFetcher::Create(fetcher_id_, GURL(kSearchDomainCheckURL),
128 // flag; we just live with an outdated URL for this 188 URLFetcher::GET, this));
129 // run of the browser. 189 ++fetcher_id_;
130 fetcher_.reset(new URLFetcher(GURL(kDefaultGoogleHomepage), URLFetcher::HEAD,
131 this));
132 // We don't want this fetch to affect existing state in the profile. For 190 // We don't want this fetch to affect existing state in the profile. For
133 // example, if a user has no Google cookies, this automatic check should not 191 // example, if a user has no Google cookies, this automatic check should not
134 // cause one to be set, lest we alarm the user. 192 // cause one to be set, lest we alarm the user.
135 fetcher_->set_load_flags(net::LOAD_DISABLE_CACHE | 193 fetcher_->set_load_flags(net::LOAD_DISABLE_CACHE |
136 net::LOAD_DO_NOT_SAVE_COOKIES); 194 net::LOAD_DO_NOT_SAVE_COOKIES);
137 fetcher_->set_request_context(Profile::GetDefaultRequestContext()); 195 fetcher_->set_request_context(Profile::GetDefaultRequestContext());
138 fetcher_->Start(); 196 fetcher_->Start();
139 } 197 }
140 198
141 void GoogleURLTracker::OnURLFetchComplete(const URLFetcher* source, 199 void GoogleURLTracker::OnURLFetchComplete(const URLFetcher* source,
142 const GURL& url, 200 const GURL& url,
143 const URLRequestStatus& status, 201 const URLRequestStatus& status,
144 int response_code, 202 int response_code,
145 const ResponseCookies& cookies, 203 const ResponseCookies& cookies,
146 const std::string& data) { 204 const std::string& data) {
147 // Delete the fetcher on this function's exit. 205 // Delete the fetcher on this function's exit.
148 scoped_ptr<URLFetcher> clean_up_fetcher(fetcher_.release()); 206 scoped_ptr<URLFetcher> clean_up_fetcher(fetcher_.release());
149 207
150 // Don't update the URL if the request didn't succeed. 208 // Don't update the URL if the request didn't succeed.
151 if (!status.is_success() || (response_code != 200)) 209 if (!status.is_success() || (response_code != 200)) {
210 already_fetched_ = false;
211 return;
212 }
213
214 // See if the response data was one we want to use, and if so, convert to the
215 // appropriate Google base URL.
216 std::string url_str;
217 TrimWhitespace(data, TRIM_ALL, &url_str);
218
219 if (!StartsWithASCII(url_str, ".google.", false))
152 return; 220 return;
153 221
154 // See if the response URL was one we want to use, and if so, convert to the 222 fetched_google_url_ = GURL("http://www" + url_str);
155 // appropriate Google base URL. 223 GURL last_prompted_url(
156 GURL base_url; 224 g_browser_process->local_state()->GetString(
157 if (!CheckAndConvertToGoogleBaseURL(url, &base_url)) 225 prefs::kLastPromptedGoogleURL));
226 need_to_prompt_ = false;
227 // On the very first run of Chrome, when we've never looked up the URL at all,
228 // we should just silently switch over to whatever we get immediately.
229 if (last_prompted_url.is_empty()) {
230 AcceptGoogleURL(fetched_google_url_);
231 // Set fetched_google_url_ as an initial value of last prompted URL.
232 g_browser_process->local_state()->SetString(prefs::kLastPromptedGoogleURL,
233 fetched_google_url_.spec());
158 return; 234 return;
235 }
159 236
160 // Update the saved base URL if it has changed. 237 if (fetched_google_url_ == last_prompted_url)
161 const std::string base_url_str(base_url.spec()); 238 return;
162 if (g_browser_process->local_state()->GetString(prefs::kLastKnownGoogleURL) != 239 if (fetched_google_url_ == google_url_) {
163 base_url_str) { 240 // The user came back to their original location after having temporarily
164 g_browser_process->local_state()->SetString(prefs::kLastKnownGoogleURL, 241 // moved. Reset the prompted URL so we'll prompt again if they move again.
165 base_url_str); 242 g_browser_process->local_state()->SetString(prefs::kLastPromptedGoogleURL,
166 google_url_ = base_url; 243 fetched_google_url_.spec());
167 NotificationService::current()->Notify(NotificationType::GOOGLE_URL_UPDATED, 244 return;
168 NotificationService::AllSources(),
169 NotificationService::NoDetails());
170 } 245 }
246
247 need_to_prompt_ = true;
248 }
249
250 void GoogleURLTracker::AcceptGoogleURL(const GURL& new_google_url) {
251 google_url_ = new_google_url;
252 g_browser_process->local_state()->SetString(prefs::kLastKnownGoogleURL,
253 google_url_.spec());
254 NotificationService::current()->Notify(NotificationType::GOOGLE_URL_UPDATED,
255 NotificationService::AllSources(),
256 NotificationService::NoDetails());
257 need_to_prompt_ = false;
258 }
259
260 void GoogleURLTracker::InfoBarClosed() {
261 registrar_.RemoveAll();
262 controller_ = NULL;
263 infobar_ = NULL;
264 search_url_ = GURL();
265 }
266
267 void GoogleURLTracker::RedoSearch() {
268 // re-do the user's search on the new domain.
269 DCHECK(controller_);
270 url_canon::Replacements<char> replacements;
271 replacements.SetHost(google_url_.host().data(),
272 url_parse::Component(0, google_url_.host().length()));
273 search_url_ = search_url_.ReplaceComponents(replacements);
274 if (search_url_.is_valid())
275 controller_->tab_contents()->OpenURL(search_url_, GURL(), CURRENT_TAB,
276 PageTransition::GENERATED);
171 } 277 }
172 278
173 void GoogleURLTracker::Observe(NotificationType type, 279 void GoogleURLTracker::Observe(NotificationType type,
174 const NotificationSource& source, 280 const NotificationSource& source,
175 const NotificationDetails& details) { 281 const NotificationDetails& details) {
176 DCHECK_EQ(NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE, type.value); 282 switch (type.value) {
177 request_context_available_ = true; 283 case NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE:
284 request_context_available_ = true;
285 StartFetchIfDesirable();
286 break;
287
288 case NotificationType::NAV_ENTRY_PENDING:
289 // If we've already received a notification for the same controller, we
290 // should reset infobar as that indicates that the page is being
291 // re-loaded
292 if (!infobar_ &&
293 controller_ == Source<NavigationController>(source).ptr()) {
294 infobar_ = NULL;
295 } else if (!controller_) {
296 controller_ = Source<NavigationController>(source).ptr();
297 NavigationEntry* entry = controller_->pending_entry();
298 DCHECK(entry);
299 search_url_ = entry->url();
300
301 // Start listening for the commit notification. We also need to listen
302 // for the tab close command since that means the load will never
303 // commit!
304 registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
305 Source<NavigationController>(controller_));
306 registrar_.Add(this, NotificationType::TAB_CLOSED,
307 Source<NavigationController>(controller_));
308 }
309 break;
310
311 case NotificationType::NAV_ENTRY_COMMITTED:
312 DCHECK(controller_);
313 registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED,
314 Source<NavigationController>(controller_));
315 ShowGoogleURLInfoBarIfNecessary(controller_->tab_contents());
316 break;
317
318 case NotificationType::TAB_CLOSED:
319 registrar_.RemoveAll();
320 controller_ = NULL;
321 infobar_ = NULL;
322 break;
323
324 default:
325 NOTREACHED() << "Unknown notification received:" << type.value;
326 }
327 }
328
329 void GoogleURLTracker::OnIPAddressChanged() {
330 already_fetched_ = false;
178 StartFetchIfDesirable(); 331 StartFetchIfDesirable();
179 } 332 }
333
334 void GoogleURLTracker::SearchCommitted() {
335 registrar_.Add(this, NotificationType::NAV_ENTRY_PENDING,
336 NotificationService::AllSources());
337 }
338
339 void GoogleURLTracker::ShowGoogleURLInfoBarIfNecessary(
340 TabContents* tab_contents) {
341 if (!need_to_prompt_)
342 return;
343 if (infobar_)
344 return;
345 DCHECK(!fetched_google_url_.is_empty());
346 DCHECK(infobar_factory_.get());
347
348 infobar_ = infobar_factory_->CreateInfoBar(tab_contents,
349 this,
350 fetched_google_url_);
351 g_browser_process->local_state()->SetString(prefs::kLastPromptedGoogleURL,
352 fetched_google_url_.spec());
353 }
OLDNEW
« no previous file with comments | « chrome/browser/google_url_tracker.h ('k') | chrome/browser/google_url_tracker_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698