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.
4
5#include "chrome/browser/extensions/api/settings_overrides/settings_overrides_api.h"
6
7#include "base/lazy_instance.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/browser/extensions/api/preference/preference_api.h"
10#include "chrome/browser/prefs/session_startup_pref.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/search_engines/template_url.h"
13#include "chrome/browser/search_engines/template_url_prepopulate_data.h"
14#include "chrome/browser/search_engines/template_url_service_factory.h"
15#include "chrome/common/extensions/manifest_handlers/settings_overrides_handler.h"
16#include "chrome/common/pref_names.h"
17#include "extensions/browser/extension_prefs.h"
18#include "extensions/browser/extension_prefs_factory.h"
19#include "extensions/browser/extension_registry.h"
20#include "extensions/common/error_utils.h"
21#include "extensions/common/manifest_constants.h"
22
23namespace extensions {
24
25namespace {
26
27base::LazyInstance<BrowserContextKeyedAPIFactory<SettingsOverridesAPI> >
28    g_factory = LAZY_INSTANCE_INITIALIZER;
29
30const char kManyStartupPagesWarning[] = "* specifies more than 1 startup URL. "
31    "All but the first will be ignored.";
32
33using api::manifest_types::ChromeSettingsOverrides;
34
35std::string SubstituteInstallParam(std::string str,
36                                   const std::string& install_parameter) {
37  ReplaceSubstringsAfterOffset(&str, 0, "__PARAM__", install_parameter);
38  return str;
39}
40
41// Find the prepopulated search engine with the given id.
42bool GetPrepopulatedSearchProvider(PrefService* prefs,
43                                   int prepopulated_id,
44                                   TemplateURLData* data) {
45  DCHECK(data);
46  size_t default_index;
47  ScopedVector<TemplateURLData> engines =
48      TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs, &default_index);
49  for (ScopedVector<TemplateURLData>::iterator i = engines.begin();
50       i != engines.end();
51       ++i) {
52    if ((*i)->prepopulate_id == prepopulated_id) {
53      *data = **i;
54      return true;
55    }
56  }
57  return false;
58}
59
60TemplateURLData ConvertSearchProvider(
61    PrefService* prefs,
62    const ChromeSettingsOverrides::Search_provider& search_provider,
63    const std::string& install_parameter) {
64  TemplateURLData data;
65  if (search_provider.prepopulated_id) {
66    if (!GetPrepopulatedSearchProvider(prefs, *search_provider.prepopulated_id,
67                                       &data)) {
68      VLOG(1) << "Settings Overrides API can't recognize prepopulated_id="
69          << *search_provider.prepopulated_id;
70    }
71  }
72
73  if (search_provider.name)
74    data.short_name = base::UTF8ToUTF16(*search_provider.name);
75  if (search_provider.keyword)
76    data.SetKeyword(base::UTF8ToUTF16(*search_provider.keyword));
77  data.SetURL(SubstituteInstallParam(search_provider.search_url,
78                                     install_parameter));
79  if (search_provider.suggest_url) {
80    data.suggestions_url =
81        SubstituteInstallParam(*search_provider.suggest_url, install_parameter);
82  }
83  if (search_provider.instant_url) {
84    data.instant_url =
85        SubstituteInstallParam(*search_provider.instant_url, install_parameter);
86  }
87  if (search_provider.image_url) {
88    data.image_url =
89        SubstituteInstallParam(*search_provider.image_url, install_parameter);
90  }
91  if (search_provider.search_url_post_params)
92    data.search_url_post_params = *search_provider.search_url_post_params;
93  if (search_provider.suggest_url_post_params)
94    data.suggestions_url_post_params = *search_provider.suggest_url_post_params;
95  if (search_provider.instant_url_post_params)
96    data.instant_url_post_params = *search_provider.instant_url_post_params;
97  if (search_provider.image_url_post_params)
98    data.image_url_post_params = *search_provider.image_url_post_params;
99  if (search_provider.favicon_url) {
100    data.favicon_url = GURL(SubstituteInstallParam(*search_provider.favicon_url,
101                                                   install_parameter));
102  }
103  data.safe_for_autoreplace = false;
104  if (search_provider.encoding) {
105    data.input_encodings.clear();
106    data.input_encodings.push_back(*search_provider.encoding);
107  }
108  data.date_created = base::Time();
109  data.last_modified = base::Time();
110  data.prepopulate_id = 0;
111  if (search_provider.alternate_urls) {
112    data.alternate_urls.clear();
113    for (size_t i = 0; i < search_provider.alternate_urls->size(); ++i) {
114      if (!search_provider.alternate_urls->at(i).empty())
115        data.alternate_urls.push_back(SubstituteInstallParam(
116            search_provider.alternate_urls->at(i), install_parameter));
117    }
118  }
119  return data;
120}
121
122}  // namespace
123
124SettingsOverridesAPI::SettingsOverridesAPI(content::BrowserContext* context)
125    : profile_(Profile::FromBrowserContext(context)),
126      url_service_(TemplateURLServiceFactory::GetForProfile(profile_)),
127      extension_registry_observer_(this) {
128  extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
129}
130
131SettingsOverridesAPI::~SettingsOverridesAPI() {
132}
133
134BrowserContextKeyedAPIFactory<SettingsOverridesAPI>*
135SettingsOverridesAPI::GetFactoryInstance() {
136  return g_factory.Pointer();
137}
138
139void SettingsOverridesAPI::SetPref(const std::string& extension_id,
140                                   const std::string& pref_key,
141                                   base::Value* value) {
142  PreferenceAPI* prefs = PreferenceAPI::Get(profile_);
143  if (!prefs)
144    return;  // Expected in unit tests.
145  prefs->SetExtensionControlledPref(extension_id,
146                                    pref_key,
147                                    kExtensionPrefsScopeRegular,
148                                    value);
149}
150
151void SettingsOverridesAPI::UnsetPref(const std::string& extension_id,
152                                     const std::string& pref_key) {
153  PreferenceAPI* prefs = PreferenceAPI::Get(profile_);
154  if (!prefs)
155    return;  // Expected in unit tests.
156  prefs->RemoveExtensionControlledPref(
157      extension_id,
158      pref_key,
159      kExtensionPrefsScopeRegular);
160}
161
162void SettingsOverridesAPI::OnExtensionLoaded(
163    content::BrowserContext* browser_context,
164    const Extension* extension) {
165  const SettingsOverrides* settings = SettingsOverrides::Get(extension);
166  if (settings) {
167    std::string install_parameter =
168        ExtensionPrefs::Get(profile_)->GetInstallParam(extension->id());
169    if (settings->homepage) {
170      SetPref(extension->id(),
171              prefs::kHomePage,
172              new base::StringValue(SubstituteInstallParam(
173                  settings->homepage->spec(), install_parameter)));
174      SetPref(extension->id(),
175              prefs::kHomePageIsNewTabPage,
176              new base::FundamentalValue(false));
177    }
178    if (!settings->startup_pages.empty()) {
179      SetPref(extension->id(),
180              prefs::kRestoreOnStartup,
181              new base::FundamentalValue(SessionStartupPref::kPrefValueURLs));
182      if (settings->startup_pages.size() > 1) {
183        VLOG(1) << extensions::ErrorUtils::FormatErrorMessage(
184                       kManyStartupPagesWarning,
185                       manifest_keys::kSettingsOverride);
186      }
187      scoped_ptr<base::ListValue> url_list(new base::ListValue);
188      url_list->Append(new base::StringValue(SubstituteInstallParam(
189          settings->startup_pages[0].spec(), install_parameter)));
190      SetPref(
191          extension->id(), prefs::kURLsToRestoreOnStartup, url_list.release());
192    }
193    if (settings->search_engine) {
194      // Bring the preference to the correct state. Before this code set it
195      // to "true" for all search engines. Thus, we should overwrite it for
196      // all search engines.
197      if (settings->search_engine->is_default) {
198        SetPref(extension->id(),
199                prefs::kDefaultSearchProviderEnabled,
200                new base::FundamentalValue(true));
201      } else {
202        UnsetPref(extension->id(), prefs::kDefaultSearchProviderEnabled);
203      }
204      DCHECK(url_service_);
205      if (url_service_->loaded()) {
206        RegisterSearchProvider(extension);
207      } else {
208        if (!template_url_sub_) {
209          template_url_sub_ = url_service_->RegisterOnLoadedCallback(
210              base::Bind(&SettingsOverridesAPI::OnTemplateURLsLoaded,
211                         base::Unretained(this)));
212        }
213        url_service_->Load();
214        pending_extensions_.insert(extension);
215      }
216    }
217  }
218}
219void SettingsOverridesAPI::OnExtensionUnloaded(
220    content::BrowserContext* browser_context,
221    const Extension* extension,
222    UnloadedExtensionInfo::Reason reason) {
223  const SettingsOverrides* settings = SettingsOverrides::Get(extension);
224  if (settings) {
225    if (settings->homepage) {
226      UnsetPref(extension->id(), prefs::kHomePage);
227      UnsetPref(extension->id(), prefs::kHomePageIsNewTabPage);
228    }
229    if (!settings->startup_pages.empty()) {
230      UnsetPref(extension->id(), prefs::kRestoreOnStartup);
231      UnsetPref(extension->id(), prefs::kURLsToRestoreOnStartup);
232    }
233    if (settings->search_engine) {
234      DCHECK(url_service_);
235      if (url_service_->loaded())
236        url_service_->RemoveExtensionControlledTURL(extension->id());
237      else
238        pending_extensions_.erase(extension);
239    }
240  }
241}
242
243void SettingsOverridesAPI::Shutdown() {
244  template_url_sub_.reset();
245}
246
247void SettingsOverridesAPI::OnTemplateURLsLoaded() {
248  // Register search providers for pending extensions.
249  template_url_sub_.reset();
250  for (PendingExtensions::const_iterator i(pending_extensions_.begin());
251       i != pending_extensions_.end(); ++i) {
252    RegisterSearchProvider(*i);
253  }
254  pending_extensions_.clear();
255}
256
257void SettingsOverridesAPI::RegisterSearchProvider(
258    const Extension* extension) const {
259  DCHECK(url_service_);
260  DCHECK(extension);
261  const SettingsOverrides* settings = SettingsOverrides::Get(extension);
262  DCHECK(settings);
263  DCHECK(settings->search_engine);
264  scoped_ptr<AssociatedExtensionInfo> info(new AssociatedExtensionInfo);
265  info->extension_id = extension->id();
266  info->wants_to_be_default_engine = settings->search_engine->is_default;
267  ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
268  info->install_time = prefs->GetInstallTime(extension->id());
269  std::string install_parameter = prefs->GetInstallParam(extension->id());
270  TemplateURLData data = ConvertSearchProvider(
271      profile_->GetPrefs(), *settings->search_engine, install_parameter);
272  data.show_in_default_list = info->wants_to_be_default_engine;
273  url_service_->AddExtensionControlledTURL(new TemplateURL(data), info.Pass());
274}
275
276template <>
277void BrowserContextKeyedAPIFactory<
278    SettingsOverridesAPI>::DeclareFactoryDependencies() {
279  DependsOn(ExtensionPrefsFactory::GetInstance());
280  DependsOn(PreferenceAPI::GetFactoryInstance());
281  DependsOn(TemplateURLServiceFactory::GetInstance());
282}
283
284}  // namespace extensions
285