1 // Copyright 2013 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.
5 #include "chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/logging.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/values.h"
18 #include "chrome/app/chrome_command_ids.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/google/google_util.h"
21 #include "chrome/browser/profile_resetter/brandcode_config_fetcher.h"
22 #include "chrome/browser/profile_resetter/profile_reset_global_error.h"
23 #include "chrome/browser/profile_resetter/profile_resetter.h"
24 #include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
27 #include "chrome/browser/search_engines/template_url_service.h"
28 #include "chrome/browser/search_engines/template_url_service_factory.h"
29 #include "chrome/browser/ui/browser.h"
30 #include "chrome/browser/ui/browser_finder.h"
31 #include "chrome/browser/ui/global_error/global_error_service.h"
32 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_service.h"
37 #include "chrome/browser/enumerate_modules_model_win.h"
42 scoped_ptr
<base::DictionaryValue
> BuildSubTreeFromTemplateURL(
43 const TemplateURL
* template_url
) {
44 scoped_ptr
<base::DictionaryValue
> tree(new base::DictionaryValue
);
45 tree
->SetString("search_url", template_url
->url());
46 // If this value contains a placeholder in the pre-populated data, it will
47 // have been replaced as it was loaded into a TemplateURL.
48 // BuildSubTreeFromTemplateURL works with TemplateURL (not TemplateURLData)
49 // in order to maintain this behaviour.
50 // TODO(engedy): Confirm the expected behaviour and convert to use
51 // TemplateURLData if possible."
52 tree
->SetString("search_terms_replacement_key",
53 template_url
->search_terms_replacement_key());
54 tree
->SetString("suggest_url", template_url
->suggestions_url());
55 tree
->SetString("instant_url", template_url
->instant_url());
56 tree
->SetString("image_url", template_url
->image_url());
57 tree
->SetString("new_tab_url", template_url
->new_tab_url());
58 tree
->SetString("search_url_post_params",
59 template_url
->search_url_post_params());
60 tree
->SetString("suggest_url_post_params",
61 template_url
->suggestions_url_post_params());
62 tree
->SetString("instant_url_post_params",
63 template_url
->instant_url_post_params());
64 tree
->SetString("image_url_post_params",
65 template_url
->image_url_post_params());
66 tree
->SetString("icon_url", template_url
->favicon_url().spec());
67 tree
->SetString("name", template_url
->short_name());
68 tree
->SetString("keyword", template_url
->keyword());
69 base::ListValue
* input_encodings
= new base::ListValue
;
70 input_encodings
->AppendStrings(template_url
->input_encodings());
71 tree
->Set("encodings", input_encodings
);
72 tree
->SetString("id", base::Int64ToString(template_url
->id()));
73 tree
->SetString("prepopulate_id",
74 base::IntToString(template_url
->prepopulate_id()));
75 base::ListValue
* alternate_urls
= new base::ListValue
;
76 alternate_urls
->AppendStrings(template_url
->alternate_urls());
77 tree
->Set("alternate_urls", alternate_urls
);
82 void ExtractLoadedModuleNameDigests(
83 const base::ListValue
& module_list
,
84 base::ListValue
* module_name_digests
) {
85 DCHECK(module_name_digests
);
87 // EnumerateModulesModel produces a list of dictionaries.
88 // Each dictionary corresponds to a module and exposes a number of properties.
89 // We care only about 'type' and 'name'.
90 for (size_t i
= 0; i
< module_list
.GetSize(); ++i
) {
91 const base::DictionaryValue
* module_dictionary
= NULL
;
92 if (!module_list
.GetDictionary(i
, &module_dictionary
))
94 ModuleEnumerator::ModuleType module_type
=
95 ModuleEnumerator::LOADED_MODULE
;
96 if (!module_dictionary
->GetInteger(
97 "type", reinterpret_cast<int*>(&module_type
)) ||
98 module_type
!= ModuleEnumerator::LOADED_MODULE
) {
101 std::string module_name
;
102 if (!module_dictionary
->GetString("name", &module_name
))
104 StringToLowerASCII(&module_name
);
105 module_name_digests
->AppendString(base::MD5String(module_name
));
113 // AutomaticProfileResetterDelegateImpl --------------------------------------
115 AutomaticProfileResetterDelegateImpl::AutomaticProfileResetterDelegateImpl(
117 ProfileResetter::ResettableFlags resettable_aspects
)
119 global_error_service_(GlobalErrorServiceFactory::GetForProfile(profile_
)),
120 template_url_service_(TemplateURLServiceFactory::GetForProfile(profile_
)),
121 resettable_aspects_(resettable_aspects
) {
123 if (template_url_service_
) {
124 template_url_service_
->AddObserver(this);
125 // Needed so that |template_url_service_ready_event_| will be signaled even
126 // when TemplateURLService had been already initialized before this point.
127 OnTemplateURLServiceChanged();
131 module_list_
.reset(EnumerateModulesModel::GetInstance()->GetModuleList());
134 // Having a non-empty module list proves that enumeration had been already
135 // performed before this point.
136 modules_have_been_enumerated_event_
.Signal();
139 chrome::NOTIFICATION_MODULE_LIST_ENUMERATED
,
140 content::NotificationService::AllSources());
143 AutomaticProfileResetterDelegateImpl::~AutomaticProfileResetterDelegateImpl() {
144 if (template_url_service_
)
145 template_url_service_
->RemoveObserver(this);
148 void AutomaticProfileResetterDelegateImpl::EnumerateLoadedModulesIfNeeded() {
149 if (!modules_have_been_enumerated_event_
.is_signaled()) {
151 EnumerateModulesModel::GetInstance()->ScanNow();
153 modules_have_been_enumerated_event_
.Signal();
158 void AutomaticProfileResetterDelegateImpl::
159 RequestCallbackWhenLoadedModulesAreEnumerated(
160 const base::Closure
& ready_callback
) const {
161 DCHECK(!ready_callback
.is_null());
162 modules_have_been_enumerated_event_
.Post(FROM_HERE
, ready_callback
);
165 void AutomaticProfileResetterDelegateImpl::LoadTemplateURLServiceIfNeeded() {
166 DCHECK(template_url_service_
);
167 template_url_service_
->Load(); // Safe to call even if it has loaded already.
170 void AutomaticProfileResetterDelegateImpl::
171 RequestCallbackWhenTemplateURLServiceIsLoaded(
172 const base::Closure
& ready_callback
) const {
173 DCHECK(!ready_callback
.is_null());
174 template_url_service_ready_event_
.Post(FROM_HERE
, ready_callback
);
177 void AutomaticProfileResetterDelegateImpl::
178 FetchBrandcodedDefaultSettingsIfNeeded() {
179 if (brandcoded_config_fetcher_
||
180 brandcoded_defaults_fetched_event_
.is_signaled())
183 std::string brandcode
;
184 google_util::GetBrand(&brandcode
);
185 if (brandcode
.empty()) {
186 brandcoded_defaults_
.reset(new BrandcodedDefaultSettings
);
187 brandcoded_defaults_fetched_event_
.Signal();
189 brandcoded_config_fetcher_
.reset(new BrandcodeConfigFetcher(
191 &AutomaticProfileResetterDelegateImpl::OnBrandcodedDefaultsFetched
,
192 base::Unretained(this)),
193 GURL("https://tools.google.com/service/update2"),
198 void AutomaticProfileResetterDelegateImpl::
199 RequestCallbackWhenBrandcodedDefaultsAreFetched(
200 const base::Closure
& ready_callback
) const {
201 DCHECK(!ready_callback
.is_null());
202 brandcoded_defaults_fetched_event_
.Post(FROM_HERE
, ready_callback
);
205 scoped_ptr
<base::ListValue
> AutomaticProfileResetterDelegateImpl::
206 GetLoadedModuleNameDigests() const {
207 DCHECK(modules_have_been_enumerated_event_
.is_signaled());
208 scoped_ptr
<base::ListValue
> result(new base::ListValue
);
211 ExtractLoadedModuleNameDigests(*module_list_
, result
.get());
213 return result
.Pass();
216 scoped_ptr
<base::DictionaryValue
> AutomaticProfileResetterDelegateImpl::
217 GetDefaultSearchProviderDetails() const {
218 DCHECK(template_url_service_
);
219 DCHECK(template_url_service_
->loaded());
221 const TemplateURL
* default_search_provider
=
222 template_url_service_
->GetDefaultSearchProvider();
224 // Having a NULL default search provider is due to either:
225 // 1.) default search providers being disabled by policy,
226 // 2.) directly tampering with the Preferences and/or the SQLite DBs.
227 // In this state, Omnibox non-keyword search functionality is disabled.
228 return default_search_provider
?
229 BuildSubTreeFromTemplateURL(default_search_provider
) :
230 scoped_ptr
<base::DictionaryValue
>(new base::DictionaryValue
);
233 bool AutomaticProfileResetterDelegateImpl::
234 IsDefaultSearchProviderManaged() const {
235 DCHECK(template_url_service_
);
236 DCHECK(template_url_service_
->loaded());
237 return template_url_service_
->is_default_search_managed();
240 scoped_ptr
<base::ListValue
> AutomaticProfileResetterDelegateImpl::
241 GetPrepopulatedSearchProvidersDetails() const {
242 size_t default_search_index
= 0;
243 ScopedVector
<TemplateURLData
> engines(
244 TemplateURLPrepopulateData::GetPrepopulatedEngines(
245 profile_
->GetPrefs(), &default_search_index
));
246 scoped_ptr
<base::ListValue
> engines_details_list(new base::ListValue
);
247 for (ScopedVector
<TemplateURLData
>::const_iterator it
= engines
.begin();
248 it
!= engines
.end(); ++it
) {
249 TemplateURL
template_url(profile_
, **it
);
250 engines_details_list
->Append(
251 BuildSubTreeFromTemplateURL(&template_url
).release());
253 return engines_details_list
.Pass();
256 bool AutomaticProfileResetterDelegateImpl::TriggerPrompt() {
257 DCHECK(global_error_service_
);
259 if (!ProfileResetGlobalError::IsSupportedOnPlatform())
262 ProfileResetGlobalError
* global_error
= new ProfileResetGlobalError(profile_
);
263 global_error_service_
->AddGlobalError(global_error
);
265 // Do not try to show bubble if another GlobalError is already showing one.
266 const GlobalErrorService::GlobalErrorList
& global_errors(
267 global_error_service_
->errors());
268 GlobalErrorService::GlobalErrorList::const_iterator it
;
269 for (it
= global_errors
.begin(); it
!= global_errors
.end(); ++it
) {
270 if ((*it
)->GetBubbleView())
273 if (it
== global_errors
.end()) {
274 Browser
* browser
= chrome::FindTabbedBrowser(
276 false /*match_original_profiles*/,
277 chrome::GetActiveDesktop());
279 global_error
->ShowBubbleView(browser
);
284 void AutomaticProfileResetterDelegateImpl::TriggerProfileSettingsReset(
286 const base::Closure
& completion
) {
287 DCHECK(!profile_resetter_
);
288 DCHECK(!completion
.is_null());
290 profile_resetter_
.reset(new ProfileResetter(profile_
));
291 FetchBrandcodedDefaultSettingsIfNeeded();
292 RequestCallbackWhenBrandcodedDefaultsAreFetched(base::Bind(
293 &AutomaticProfileResetterDelegateImpl::RunProfileSettingsReset
,
299 void AutomaticProfileResetterDelegateImpl::OnTemplateURLServiceChanged() {
300 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
301 DCHECK(template_url_service_
);
302 if (template_url_service_
->loaded() &&
303 !template_url_service_ready_event_
.is_signaled())
304 template_url_service_ready_event_
.Signal();
307 void AutomaticProfileResetterDelegateImpl::DismissPrompt() {
308 DCHECK(global_error_service_
);
309 GlobalError
* global_error
=
310 global_error_service_
->GetGlobalErrorByMenuItemCommandID(
311 IDC_SHOW_SETTINGS_RESET_BUBBLE
);
313 // This will also close/destroy the Bubble UI if it is currently shown.
314 global_error_service_
->RemoveGlobalError(global_error
);
319 void AutomaticProfileResetterDelegateImpl::Observe(
321 const content::NotificationSource
& source
,
322 const content::NotificationDetails
& details
) {
323 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
324 if (type
== chrome::NOTIFICATION_MODULE_LIST_ENUMERATED
&&
325 !modules_have_been_enumerated_event_
.is_signaled()) {
327 module_list_
.reset(EnumerateModulesModel::GetInstance()->GetModuleList());
329 modules_have_been_enumerated_event_
.Signal();
333 void AutomaticProfileResetterDelegateImpl::SendFeedback(
334 const std::string
& report
) const {
335 SendSettingsFeedback(report
, profile_
, PROFILE_RESET_PROMPT
);
338 void AutomaticProfileResetterDelegateImpl::RunProfileSettingsReset(
340 const base::Closure
& completion
) {
341 DCHECK(brandcoded_defaults_
);
342 scoped_ptr
<ResettableSettingsSnapshot
> old_settings_snapshot
;
344 old_settings_snapshot
.reset(new ResettableSettingsSnapshot(profile_
));
345 old_settings_snapshot
->RequestShortcuts(base::Closure());
347 profile_resetter_
->Reset(
349 brandcoded_defaults_
.Pass(),
350 base::Bind(&AutomaticProfileResetterDelegateImpl::
351 OnProfileSettingsResetCompleted
,
354 base::Passed(&old_settings_snapshot
)));
357 void AutomaticProfileResetterDelegateImpl::
358 OnBrandcodedDefaultsFetched() {
359 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
360 DCHECK(brandcoded_config_fetcher_
);
361 DCHECK(!brandcoded_config_fetcher_
->IsActive());
362 brandcoded_defaults_
= brandcoded_config_fetcher_
->GetSettings();
363 if (!brandcoded_defaults_
)
364 brandcoded_defaults_
.reset(new BrandcodedDefaultSettings
);
365 brandcoded_defaults_fetched_event_
.Signal();
368 void AutomaticProfileResetterDelegateImpl::OnProfileSettingsResetCompleted(
369 const base::Closure
& user_callback
,
370 scoped_ptr
<ResettableSettingsSnapshot
> old_settings_snapshot
) {
371 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
372 if (old_settings_snapshot
) {
373 ResettableSettingsSnapshot
new_settings_snapshot(profile_
);
375 old_settings_snapshot
->FindDifferentFields(new_settings_snapshot
);
377 old_settings_snapshot
->Subtract(new_settings_snapshot
);
379 SerializeSettingsReport(*old_settings_snapshot
, difference
);
380 SendFeedback(report
);
383 content::BrowserThread::PostTask(
384 content::BrowserThread::UI
, FROM_HERE
, user_callback
);