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