OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <algorithm> | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "base/compiler_specific.h" | |
9 #include "base/logging.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "base/metrics/histogram.h" | |
12 #include "chrome/browser/profiles/profile.h" | |
13 #include "chrome/browser/protector/base_setting_change.h" | |
14 #include "chrome/browser/protector/histograms.h" | |
15 #include "chrome/browser/protector/protector_service.h" | |
16 #include "chrome/browser/protector/protector_service_factory.h" | |
17 #include "chrome/browser/search_engines/template_url.h" | |
18 #include "chrome/browser/search_engines/template_url_prepopulate_data.h" | |
19 #include "chrome/browser/search_engines/template_url_service.h" | |
20 #include "chrome/browser/search_engines/template_url_service_factory.h" | |
21 #include "chrome/browser/search_engines/template_url_service_observer.h" | |
22 #include "chrome/browser/webdata/keyword_table.h" | |
23 #include "chrome/common/chrome_notification_types.h" | |
24 #include "chrome/common/url_constants.h" | |
25 #include "content/public/browser/notification_observer.h" | |
26 #include "content/public/browser/notification_registrar.h" | |
27 #include "content/public/browser/notification_service.h" | |
28 #include "googleurl/src/gurl.h" | |
29 #include "grit/chromium_strings.h" | |
30 #include "grit/generated_resources.h" | |
31 #include "grit/theme_resources.h" | |
32 #include "ui/base/l10n/l10n_util.h" | |
33 | |
34 namespace protector { | |
35 | |
36 namespace { | |
37 | |
38 // Maximum length of the search engine name to be displayed. | |
39 const size_t kMaxDisplayedNameLength = 16; | |
40 | |
41 // Matches TemplateURL with all fields set from the prepopulated data equal | |
42 // to fields in another TemplateURL. | |
43 class TemplateURLIsSame { | |
44 public: | |
45 // Creates a matcher based on |other|. | |
46 explicit TemplateURLIsSame(const TemplateURL* other) : other_(other) { | |
47 } | |
48 | |
49 // Returns true if both |other| and |url| are NULL or have same field values. | |
50 bool operator()(const TemplateURL* url) { | |
51 if (url == other_ ) | |
52 return true; | |
53 if (!url || !other_) | |
54 return false; | |
55 return url->short_name() == other_->short_name() && | |
56 url->HasSameKeywordAs(*other_) && | |
57 url->url() == other_->url() && | |
58 url->suggestions_url() == other_->suggestions_url() && | |
59 url->instant_url() == other_->instant_url() && | |
60 url->safe_for_autoreplace() == other_->safe_for_autoreplace() && | |
61 url->favicon_url() == other_->favicon_url() && | |
62 url->show_in_default_list() == other_->show_in_default_list() && | |
63 url->input_encodings() == other_->input_encodings() && | |
64 url->prepopulate_id() == other_->prepopulate_id(); | |
65 } | |
66 | |
67 private: | |
68 const TemplateURL* other_; | |
69 }; | |
70 | |
71 } // namespace | |
72 | |
73 // Default search engine change tracked by Protector. | |
74 class DefaultSearchProviderChange : public BaseSettingChange, | |
75 public TemplateURLServiceObserver, | |
76 public content::NotificationObserver { | |
77 public: | |
78 DefaultSearchProviderChange(TemplateURL* new_search_provider, | |
79 TemplateURL* backup_search_provider); | |
80 | |
81 // BaseSettingChange overrides: | |
82 virtual bool Init(Profile* profile) OVERRIDE; | |
83 virtual void InitWhenDisabled(Profile* profile) OVERRIDE; | |
84 virtual void Apply(Browser* browser) OVERRIDE; | |
85 virtual void Discard(Browser* browser) OVERRIDE; | |
86 virtual void Timeout() OVERRIDE; | |
87 virtual int GetBadgeIconID() const OVERRIDE; | |
88 virtual int GetMenuItemIconID() const OVERRIDE; | |
89 virtual int GetBubbleIconID() const OVERRIDE; | |
90 virtual string16 GetBubbleTitle() const OVERRIDE; | |
91 virtual string16 GetBubbleMessage() const OVERRIDE; | |
92 virtual string16 GetApplyButtonText() const OVERRIDE; | |
93 virtual string16 GetDiscardButtonText() const OVERRIDE; | |
94 virtual DisplayName GetApplyDisplayName() const OVERRIDE; | |
95 virtual GURL GetNewSettingURL() const OVERRIDE; | |
96 | |
97 private: | |
98 virtual ~DefaultSearchProviderChange(); | |
99 | |
100 // TemplateURLServiceObserver overrides: | |
101 virtual void OnTemplateURLServiceChanged() OVERRIDE; | |
102 | |
103 // content::NotificationObserver overrides: | |
104 virtual void Observe(int type, | |
105 const content::NotificationSource& source, | |
106 const content::NotificationDetails& details) OVERRIDE; | |
107 | |
108 // Sets the default search provider to |*search_provider|. If a matching | |
109 // TemplateURL already exists, it is reused and |*search_provider| is reset. | |
110 // Otherwise, adds |*search_provider| to keywords and releases it. | |
111 // In both cases, |*search_provider| is NULL after this call. | |
112 // Returns the new default search provider (either |*search_provider| or the | |
113 // reused TemplateURL). | |
114 const TemplateURL* SetDefaultSearchProvider( | |
115 scoped_ptr<TemplateURL>* search_provider); | |
116 | |
117 // Returns true if |new_search_provider_| can be used as the default search | |
118 // provider. | |
119 bool NewSearchProviderValid() const; | |
120 | |
121 // Opens the Search engine settings page in a new tab. | |
122 void OpenSearchEngineSettings(Browser* browser); | |
123 | |
124 // Returns the TemplateURLService instance for the Profile this change is | |
125 // related to. | |
126 TemplateURLService* GetTemplateURLService(); | |
127 | |
128 // Histogram ID of the new search provider. | |
129 int new_histogram_id_; | |
130 // Indicates that the default search was restored to the prepopulated default | |
131 // search engines. | |
132 bool is_fallback_; | |
133 // Indicates that the the prepopulated default search is the same as | |
134 // |new_search_provider_|. | |
135 bool fallback_is_new_; | |
136 // ID of |new_search_provider_|. | |
137 TemplateURLID new_id_; | |
138 // The default search at the moment the change was detected. Will be used to | |
139 // restore the new default search back if Apply is called. Will be set to | |
140 // |NULL| if removed from the TemplateURLService. | |
141 TemplateURL* new_search_provider_; | |
142 // Default search provider set by Init for the period until user makes a | |
143 // choice and either Apply or Discard is performed. Never is |NULL| during | |
144 // that period since the change will dismiss itself if this provider gets | |
145 // deleted or stops being the default. | |
146 const TemplateURL* default_search_provider_; | |
147 // Stores backup of the default search until it becomes owned by the | |
148 // TemplateURLService or deleted. | |
149 scoped_ptr<TemplateURL> backup_search_provider_; | |
150 content::NotificationRegistrar registrar_; | |
151 | |
152 DISALLOW_COPY_AND_ASSIGN(DefaultSearchProviderChange); | |
153 }; | |
154 | |
155 DefaultSearchProviderChange::DefaultSearchProviderChange( | |
156 TemplateURL* new_search_provider, | |
157 TemplateURL* backup_search_provider) | |
158 : new_histogram_id_(GetSearchProviderHistogramID(new_search_provider)), | |
159 is_fallback_(false), | |
160 fallback_is_new_(false), | |
161 new_search_provider_(new_search_provider), | |
162 default_search_provider_(NULL), | |
163 backup_search_provider_(backup_search_provider) { | |
164 if (backup_search_provider) { | |
165 UMA_HISTOGRAM_ENUMERATION( | |
166 kProtectorHistogramSearchProviderHijacked, | |
167 new_histogram_id_, | |
168 kProtectorMaxSearchProviderID); | |
169 } else { | |
170 UMA_HISTOGRAM_ENUMERATION( | |
171 kProtectorHistogramSearchProviderCorrupt, | |
172 new_histogram_id_, | |
173 kProtectorMaxSearchProviderID); | |
174 } | |
175 } | |
176 | |
177 DefaultSearchProviderChange::~DefaultSearchProviderChange() { | |
178 if (profile()) | |
179 GetTemplateURLService()->RemoveObserver(this); | |
180 } | |
181 | |
182 bool DefaultSearchProviderChange::Init(Profile* profile) { | |
183 if (!BaseSettingChange::Init(profile)) | |
184 return false; | |
185 | |
186 if (!backup_search_provider_.get() || | |
187 !backup_search_provider_->SupportsReplacement()) { | |
188 // Fallback to a prepopulated default search provider, ignoring any | |
189 // overrides in Prefs. | |
190 backup_search_provider_.reset( | |
191 TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(NULL)); | |
192 is_fallback_ = true; | |
193 VLOG(1) << "Fallback search provider: " | |
194 << backup_search_provider_->short_name(); | |
195 } | |
196 | |
197 default_search_provider_ = SetDefaultSearchProvider(&backup_search_provider_); | |
198 DCHECK(default_search_provider_); | |
199 // |backup_search_provider_| should be |NULL| since now. | |
200 DCHECK(!backup_search_provider_.get()); | |
201 if (is_fallback_ && default_search_provider_ == new_search_provider_) | |
202 fallback_is_new_ = true; | |
203 | |
204 int restored_histogram_id = | |
205 GetSearchProviderHistogramID(default_search_provider_); | |
206 UMA_HISTOGRAM_ENUMERATION( | |
207 kProtectorHistogramSearchProviderRestored, | |
208 restored_histogram_id, | |
209 kProtectorMaxSearchProviderID); | |
210 | |
211 if (is_fallback_) { | |
212 VLOG(1) << "Fallback to search provider: " | |
213 << default_search_provider_->short_name(); | |
214 UMA_HISTOGRAM_ENUMERATION( | |
215 kProtectorHistogramSearchProviderFallback, | |
216 restored_histogram_id, | |
217 kProtectorMaxSearchProviderID); | |
218 } | |
219 | |
220 // Listen for the default search provider changes. | |
221 GetTemplateURLService()->AddObserver(this); | |
222 | |
223 if (new_search_provider_) { | |
224 // Listen for removal of |new_search_provider_|. | |
225 new_id_ = new_search_provider_->id(); | |
226 registrar_.Add( | |
227 this, chrome::NOTIFICATION_TEMPLATE_URL_REMOVED, | |
228 content::Source<Profile>(profile->GetOriginalProfile())); | |
229 } | |
230 | |
231 return true; | |
232 } | |
233 | |
234 void DefaultSearchProviderChange::InitWhenDisabled(Profile* profile) { | |
235 // The --no-protector case is handled in TemplateURLService internals. | |
236 // TODO(ivankr): move it here. | |
237 NOTREACHED(); | |
238 } | |
239 | |
240 void DefaultSearchProviderChange::Apply(Browser* browser) { | |
241 UMA_HISTOGRAM_ENUMERATION( | |
242 kProtectorHistogramSearchProviderApplied, | |
243 new_histogram_id_, | |
244 kProtectorMaxSearchProviderID); | |
245 | |
246 GetTemplateURLService()->RemoveObserver(this); | |
247 if (NewSearchProviderValid()) { | |
248 GetTemplateURLService()->SetDefaultSearchProvider(new_search_provider_); | |
249 } else { | |
250 // Open settings page in case the new setting is invalid. | |
251 OpenSearchEngineSettings(browser); | |
252 } | |
253 } | |
254 | |
255 void DefaultSearchProviderChange::Discard(Browser* browser) { | |
256 UMA_HISTOGRAM_ENUMERATION( | |
257 kProtectorHistogramSearchProviderDiscarded, | |
258 new_histogram_id_, | |
259 kProtectorMaxSearchProviderID); | |
260 | |
261 GetTemplateURLService()->RemoveObserver(this); | |
262 if (is_fallback_) { | |
263 // Open settings page in case the old setting is invalid. | |
264 OpenSearchEngineSettings(browser); | |
265 } | |
266 // Nothing to do otherwise since we have already set the search engine | |
267 // to |old_id_| in |Init|. | |
268 } | |
269 | |
270 void DefaultSearchProviderChange::Timeout() { | |
271 UMA_HISTOGRAM_ENUMERATION( | |
272 kProtectorHistogramSearchProviderTimeout, | |
273 new_histogram_id_, | |
274 kProtectorMaxSearchProviderID); | |
275 } | |
276 | |
277 int DefaultSearchProviderChange::GetBadgeIconID() const { | |
278 return IDR_SEARCH_ENGINE_CHANGE_BADGE; | |
279 } | |
280 | |
281 int DefaultSearchProviderChange::GetMenuItemIconID() const { | |
282 return IDR_SEARCH_ENGINE_CHANGE_MENU; | |
283 } | |
284 | |
285 int DefaultSearchProviderChange::GetBubbleIconID() const { | |
286 return IDR_SEARCH_ENGINE_CHANGE_ALERT; | |
287 } | |
288 | |
289 string16 DefaultSearchProviderChange::GetBubbleTitle() const { | |
290 return l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_CHANGE_TITLE); | |
291 } | |
292 | |
293 string16 DefaultSearchProviderChange::GetBubbleMessage() const { | |
294 if (is_fallback_) { | |
295 return l10n_util::GetStringFUTF16( | |
296 IDS_SEARCH_ENGINE_CHANGE_NO_BACKUP_MESSAGE, | |
297 default_search_provider_->short_name()); | |
298 } | |
299 return l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_CHANGE_MESSAGE); | |
300 } | |
301 | |
302 string16 DefaultSearchProviderChange::GetApplyButtonText() const { | |
303 if (NewSearchProviderValid()) { | |
304 // If backup search engine is lost and fallback was made to the current | |
305 // search provider then there is no need to show this button. | |
306 if (fallback_is_new_) | |
307 return string16(); | |
308 string16 name = new_search_provider_->short_name(); | |
309 return (name.length() > kMaxDisplayedNameLength) ? | |
310 l10n_util::GetStringUTF16(IDS_CHANGE_SEARCH_ENGINE_NO_NAME) : | |
311 l10n_util::GetStringFUTF16(IDS_CHANGE_SEARCH_ENGINE, name); | |
312 } | |
313 if (is_fallback_) { | |
314 // Both settings are lost: don't show this button. | |
315 return string16(); | |
316 } | |
317 // New setting is lost, offer to go to settings. | |
318 return l10n_util::GetStringUTF16(IDS_SELECT_SEARCH_ENGINE); | |
319 } | |
320 | |
321 string16 DefaultSearchProviderChange::GetDiscardButtonText() const { | |
322 if (!is_fallback_) { | |
323 string16 name = default_search_provider_->short_name(); | |
324 return (name.length() > kMaxDisplayedNameLength) ? | |
325 l10n_util::GetStringUTF16(IDS_KEEP_SETTING) : | |
326 l10n_util::GetStringFUTF16(IDS_KEEP_SEARCH_ENGINE, name); | |
327 } | |
328 // Old setting is lost, offer to go to settings. | |
329 return l10n_util::GetStringUTF16(IDS_SELECT_SEARCH_ENGINE); | |
330 } | |
331 | |
332 BaseSettingChange::DisplayName | |
333 DefaultSearchProviderChange::GetApplyDisplayName() const { | |
334 if (NewSearchProviderValid() && | |
335 new_search_provider_->short_name().length() <= kMaxDisplayedNameLength) { | |
336 return DisplayName(kDefaultSearchProviderChangeNamePriority, | |
337 new_search_provider_->short_name()); | |
338 } | |
339 // Return the default empty name. | |
340 return BaseSettingChange::GetApplyDisplayName(); | |
341 } | |
342 | |
343 GURL DefaultSearchProviderChange::GetNewSettingURL() const { | |
344 if (is_fallback_) { | |
345 // Do not collide this change when fallback was made, since the message | |
346 // and Discard behaviour is pretty different. | |
347 return GURL(); | |
348 } | |
349 if (!NewSearchProviderValid()) | |
350 return GURL(); | |
351 return TemplateURLService::GenerateSearchURL(new_search_provider_); | |
352 } | |
353 | |
354 void DefaultSearchProviderChange::OnTemplateURLServiceChanged() { | |
355 TemplateURLService* url_service = GetTemplateURLService(); | |
356 if (url_service->GetDefaultSearchProvider() != default_search_provider_) { | |
357 VLOG(1) << "Default search provider has been changed by user"; | |
358 default_search_provider_ = NULL; | |
359 url_service->RemoveObserver(this); | |
360 // Will delete this DefaultSearchProviderChange instance. | |
361 ProtectorServiceFactory::GetForProfile(profile())->DismissChange(this); | |
362 } | |
363 } | |
364 | |
365 void DefaultSearchProviderChange::Observe( | |
366 int type, | |
367 const content::NotificationSource& source, | |
368 const content::NotificationDetails& details) { | |
369 DCHECK(type == chrome::NOTIFICATION_TEMPLATE_URL_REMOVED); | |
370 TemplateURLID id = *content::Details<TemplateURLID>(details).ptr(); | |
371 if (id == new_id_) | |
372 new_search_provider_ = NULL; | |
373 registrar_.Remove(this, type, source); | |
374 // TODO(ivankr): should the change be dismissed as well? Probably not, | |
375 // since this may happend due to Sync or whatever. In that case, if user | |
376 // clicks on 'Change to...', the Search engine settings page will be opened. | |
377 } | |
378 | |
379 const TemplateURL* DefaultSearchProviderChange::SetDefaultSearchProvider( | |
380 scoped_ptr<TemplateURL>* search_provider) { | |
381 TemplateURLService* url_service = GetTemplateURLService(); | |
382 TemplateURLService::TemplateURLVector urls = url_service->GetTemplateURLs(); | |
383 TemplateURL* new_default_provider = NULL; | |
384 | |
385 // Check if this provider already exists and add it otherwise. | |
386 TemplateURLService::TemplateURLVector::const_iterator i = | |
387 std::find_if(urls.begin(), urls.end(), | |
388 TemplateURLIsSame(search_provider->get())); | |
389 if (i != urls.end()) { | |
390 VLOG(1) << "Provider already exists"; | |
391 new_default_provider = *i; | |
392 search_provider->reset(); | |
393 } else { | |
394 VLOG(1) << "No match, adding new provider"; | |
395 new_default_provider = search_provider->get(); | |
396 url_service->Add(search_provider->release()); | |
397 UMA_HISTOGRAM_ENUMERATION( | |
398 kProtectorHistogramSearchProviderMissing, | |
399 GetSearchProviderHistogramID(new_default_provider), | |
400 kProtectorMaxSearchProviderID); | |
401 } | |
402 | |
403 // TODO(ivankr): handle keyword conflicts with existing providers. | |
404 DCHECK(new_default_provider); | |
405 VLOG(1) << "Default search provider set to: " | |
406 << new_default_provider->short_name(); | |
407 url_service->SetDefaultSearchProvider(new_default_provider); | |
408 return new_default_provider; | |
409 } | |
410 | |
411 bool DefaultSearchProviderChange::NewSearchProviderValid() const { | |
412 return new_search_provider_ && new_search_provider_->SupportsReplacement(); | |
413 } | |
414 | |
415 void DefaultSearchProviderChange::OpenSearchEngineSettings(Browser* browser) { | |
416 ProtectorServiceFactory::GetForProfile(profile())->OpenTab( | |
417 GURL(std::string(chrome::kChromeUISettingsURL) + | |
418 chrome::kSearchEnginesSubPage), browser); | |
419 } | |
420 | |
421 TemplateURLService* DefaultSearchProviderChange::GetTemplateURLService() { | |
422 TemplateURLService* url_service = | |
423 TemplateURLServiceFactory::GetForProfile(profile()); | |
424 DCHECK(url_service); | |
425 return url_service; | |
426 } | |
427 | |
428 BaseSettingChange* CreateDefaultSearchProviderChange(TemplateURL* actual, | |
429 TemplateURL* backup) { | |
430 return new DefaultSearchProviderChange(actual, backup); | |
431 } | |
432 | |
433 } // namespace protector | |
OLD | NEW |