settings_overrides_handler.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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/common/extensions/manifest_handlers/settings_overrides_handler.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "base/strings/string_util.h"
9#include "base/strings/stringprintf.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/values.h"
12#include "extensions/common/error_utils.h"
13#include "extensions/common/extension_set.h"
14#include "extensions/common/feature_switch.h"
15#include "extensions/common/manifest_constants.h"
16#include "extensions/common/permissions/api_permission_set.h"
17#include "extensions/common/permissions/manifest_permission.h"
18#include "extensions/common/permissions/permissions_data.h"
19#include "extensions/common/permissions/permissions_info.h"
20#include "extensions/common/permissions/settings_override_permission.h"
21#include "grit/generated_resources.h"
22#include "ipc/ipc_message.h"
23#include "ipc/ipc_message_utils.h"
24#include "ui/base/l10n/l10n_util.h"
25#include "url/gurl.h"
26
27using extensions::api::manifest_types::ChromeSettingsOverrides;
28
29namespace extensions {
30namespace {
31
32const char* kWwwPrefix = "www.";
33
34scoped_ptr<GURL> CreateManifestURL(const std::string& url) {
35  scoped_ptr<GURL> manifest_url(new GURL(url));
36  if (!manifest_url->is_valid() ||
37      !manifest_url->SchemeIsHTTPOrHTTPS())
38    return scoped_ptr<GURL>();
39  return manifest_url.Pass();
40}
41
42scoped_ptr<GURL> ParseHomepage(const ChromeSettingsOverrides& overrides,
43                               base::string16* error) {
44  if (!overrides.homepage)
45    return scoped_ptr<GURL>();
46  scoped_ptr<GURL> manifest_url = CreateManifestURL(*overrides.homepage);
47  if (!manifest_url) {
48    *error = extensions::ErrorUtils::FormatErrorMessageUTF16(
49        manifest_errors::kInvalidHomepageOverrideURL, *overrides.homepage);
50  }
51  return manifest_url.Pass();
52}
53
54std::vector<GURL> ParseStartupPage(const ChromeSettingsOverrides& overrides,
55                                   base::string16* error) {
56  std::vector<GURL> urls;
57  if (!overrides.startup_pages)
58    return urls;
59
60  for (std::vector<std::string>::const_iterator i =
61       overrides.startup_pages->begin(); i != overrides.startup_pages->end();
62       ++i) {
63    scoped_ptr<GURL> manifest_url = CreateManifestURL(*i);
64    if (!manifest_url) {
65      *error = extensions::ErrorUtils::FormatErrorMessageUTF16(
66          manifest_errors::kInvalidStartupOverrideURL, *i);
67    } else {
68      urls.push_back(GURL());
69      urls.back().Swap(manifest_url.get());
70    }
71  }
72  return urls;
73}
74
75scoped_ptr<ChromeSettingsOverrides::Search_provider> ParseSearchEngine(
76    ChromeSettingsOverrides* overrides,
77    base::string16* error) {
78  if (!overrides->search_provider)
79    return scoped_ptr<ChromeSettingsOverrides::Search_provider>();
80  if (!CreateManifestURL(overrides->search_provider->favicon_url)) {
81    *error = extensions::ErrorUtils::FormatErrorMessageUTF16(
82        manifest_errors::kInvalidSearchEngineURL,
83        overrides->search_provider->favicon_url);
84    return scoped_ptr<ChromeSettingsOverrides::Search_provider>();
85  }
86  if (!CreateManifestURL(overrides->search_provider->search_url)) {
87    *error = extensions::ErrorUtils::FormatErrorMessageUTF16(
88        manifest_errors::kInvalidSearchEngineURL,
89        overrides->search_provider->search_url);
90    return scoped_ptr<ChromeSettingsOverrides::Search_provider>();
91  }
92  return overrides->search_provider.Pass();
93}
94
95// A www. prefix is not informative and thus not worth the limited real estate
96// in the permissions UI.
97std::string RemoveWwwPrefix(const std::string& url) {
98  if (StartsWithASCII(url, kWwwPrefix, false))
99    return url.substr(strlen(kWwwPrefix));
100  return url;
101}
102
103}  // namespace
104
105// The manifest permission implementation supports a permission for overriding
106// the bookmark UI.
107class SettingsOverridesHandler::ManifestPermissionImpl
108    : public ManifestPermission {
109 public:
110  explicit ManifestPermissionImpl(bool override_bookmarks_ui_permission)
111      : override_bookmarks_ui_permission_(override_bookmarks_ui_permission) {}
112
113  // extensions::ManifestPermission overrides.
114  virtual std::string name() const OVERRIDE {
115    return manifest_keys::kSettingsOverride;
116  }
117
118  virtual std::string id() const OVERRIDE {
119    return name();
120  }
121
122  virtual bool HasMessages() const OVERRIDE {
123    return override_bookmarks_ui_permission_;
124  }
125
126  virtual PermissionMessages GetMessages() const OVERRIDE {
127    PermissionMessages result;
128    if (override_bookmarks_ui_permission_) {
129      result.push_back(PermissionMessage(
130          PermissionMessage::kOverrideBookmarksUI,
131          l10n_util::GetStringUTF16(
132              IDS_EXTENSION_PROMPT_WARNING_OVERRIDE_BOOKMARKS_UI)));
133    }
134    return result;
135  }
136
137  virtual bool FromValue(const base::Value* value) OVERRIDE {
138    return value && value->GetAsBoolean(&override_bookmarks_ui_permission_);
139  }
140
141  virtual scoped_ptr<base::Value> ToValue() const OVERRIDE {
142    return scoped_ptr<base::Value>(
143        new base::FundamentalValue(override_bookmarks_ui_permission_)).Pass();
144  }
145
146  virtual ManifestPermission* Clone() const OVERRIDE {
147    return scoped_ptr<ManifestPermissionImpl>(
148        new ManifestPermissionImpl(
149            override_bookmarks_ui_permission_)).release();
150  }
151
152  virtual ManifestPermission* Diff(const ManifestPermission* rhs) const
153      OVERRIDE {
154    const ManifestPermissionImpl* other =
155        static_cast<const ManifestPermissionImpl*>(rhs);
156
157    return scoped_ptr<ManifestPermissionImpl>(new ManifestPermissionImpl(
158        override_bookmarks_ui_permission_ &&
159        !other->override_bookmarks_ui_permission_)).release();
160  }
161
162  virtual ManifestPermission* Union(const ManifestPermission* rhs) const
163      OVERRIDE {
164    const ManifestPermissionImpl* other =
165        static_cast<const ManifestPermissionImpl*>(rhs);
166
167    return scoped_ptr<ManifestPermissionImpl>(new ManifestPermissionImpl(
168        override_bookmarks_ui_permission_ ||
169        other->override_bookmarks_ui_permission_)).release();
170  }
171
172  virtual ManifestPermission* Intersect(const ManifestPermission* rhs) const
173      OVERRIDE {
174    const ManifestPermissionImpl* other =
175        static_cast<const ManifestPermissionImpl*>(rhs);
176
177    return scoped_ptr<ManifestPermissionImpl>(new ManifestPermissionImpl(
178        override_bookmarks_ui_permission_ &&
179        other->override_bookmarks_ui_permission_)).release();
180  }
181
182  virtual bool Contains(const ManifestPermission* rhs) const OVERRIDE {
183    const ManifestPermissionImpl* other =
184        static_cast<const ManifestPermissionImpl*>(rhs);
185
186    return !other->override_bookmarks_ui_permission_ ||
187        override_bookmarks_ui_permission_;
188  }
189
190  virtual bool Equal(const ManifestPermission* rhs) const OVERRIDE {
191    const ManifestPermissionImpl* other =
192        static_cast<const ManifestPermissionImpl*>(rhs);
193
194    return override_bookmarks_ui_permission_ ==
195        other->override_bookmarks_ui_permission_;
196  }
197
198  virtual void Write(IPC::Message* m) const OVERRIDE {
199    IPC::WriteParam(m, override_bookmarks_ui_permission_);
200  }
201
202  virtual bool Read(const IPC::Message* m, PickleIterator* iter) OVERRIDE {
203    return IPC::ReadParam(m, iter, &override_bookmarks_ui_permission_);
204  }
205
206  virtual void Log(std::string* log) const OVERRIDE {
207    IPC::LogParam(override_bookmarks_ui_permission_, log);
208  }
209
210 private:
211  bool override_bookmarks_ui_permission_;
212};
213
214SettingsOverrides::SettingsOverrides() {}
215
216SettingsOverrides::~SettingsOverrides() {}
217
218// static
219const SettingsOverrides* SettingsOverrides::Get(
220    const Extension* extension) {
221  return static_cast<SettingsOverrides*>(
222      extension->GetManifestData(manifest_keys::kSettingsOverride));
223}
224
225// static
226bool SettingsOverrides::RemovesBookmarkButton(
227    const SettingsOverrides& settings_overrides) {
228  return settings_overrides.bookmarks_ui &&
229      settings_overrides.bookmarks_ui->remove_button &&
230      *settings_overrides.bookmarks_ui->remove_button;
231}
232
233// static
234bool SettingsOverrides::RemovesBookmarkShortcut(
235    const SettingsOverrides& settings_overrides) {
236  return settings_overrides.bookmarks_ui &&
237      settings_overrides.bookmarks_ui->remove_bookmark_shortcut &&
238      *settings_overrides.bookmarks_ui->remove_bookmark_shortcut;
239}
240
241bool SettingsOverrides::RemovesBookmarkOpenPagesShortcut(
242    const SettingsOverrides& settings_overrides) {
243  return settings_overrides.bookmarks_ui &&
244      settings_overrides.bookmarks_ui->remove_bookmark_open_pages_shortcut &&
245      *settings_overrides.bookmarks_ui->remove_bookmark_open_pages_shortcut;
246}
247
248SettingsOverridesHandler::SettingsOverridesHandler() {}
249
250SettingsOverridesHandler::~SettingsOverridesHandler() {}
251
252bool SettingsOverridesHandler::Parse(Extension* extension,
253                                     base::string16* error) {
254  const base::Value* dict = NULL;
255  CHECK(extension->manifest()->Get(manifest_keys::kSettingsOverride, &dict));
256  scoped_ptr<ChromeSettingsOverrides> settings(
257      ChromeSettingsOverrides::FromValue(*dict, error));
258  if (!settings)
259    return false;
260
261  scoped_ptr<SettingsOverrides> info(new SettingsOverrides);
262  info->bookmarks_ui.swap(settings->bookmarks_ui);
263  // Support backward compatibility for deprecated key
264  // chrome_settings_overrides.bookmarks_ui.hide_bookmark_button.
265  if (info->bookmarks_ui && !info->bookmarks_ui->remove_button &&
266      info->bookmarks_ui->hide_bookmark_button) {
267    info->bookmarks_ui->remove_button.reset(
268        new bool(*info->bookmarks_ui->hide_bookmark_button));
269  }
270  info->homepage = ParseHomepage(*settings, error);
271  info->search_engine = ParseSearchEngine(settings.get(), error);
272  info->startup_pages = ParseStartupPage(*settings, error);
273  if (!info->bookmarks_ui && !info->homepage &&
274      !info->search_engine && info->startup_pages.empty()) {
275    *error = ErrorUtils::FormatErrorMessageUTF16(
276        manifest_errors::kInvalidEmptyDictionary,
277        manifest_keys::kSettingsOverride);
278    return false;
279  }
280  info->manifest_permission.reset(new ManifestPermissionImpl(
281      SettingsOverrides::RemovesBookmarkButton(*info)));
282
283  APIPermissionSet* permission_set =
284      PermissionsData::GetInitialAPIPermissions(extension);
285  DCHECK(permission_set);
286  if (info->search_engine) {
287    permission_set->insert(new SettingsOverrideAPIPermission(
288        PermissionsInfo::GetInstance()->GetByID(APIPermission::kSearchProvider),
289        RemoveWwwPrefix(CreateManifestURL(info->search_engine->search_url)->
290            GetOrigin().host())));
291  }
292  if (!info->startup_pages.empty()) {
293    permission_set->insert(new SettingsOverrideAPIPermission(
294        PermissionsInfo::GetInstance()->GetByID(APIPermission::kStartupPages),
295        // We only support one startup page even though the type of the manifest
296        // property is a list, only the first one is used.
297        RemoveWwwPrefix(info->startup_pages[0].GetContent())));
298  }
299  if (info->homepage) {
300    permission_set->insert(new SettingsOverrideAPIPermission(
301        PermissionsInfo::GetInstance()->GetByID(APIPermission::kHomepage),
302        RemoveWwwPrefix(info->homepage.get()->GetContent())));
303  }
304  extension->SetManifestData(manifest_keys::kSettingsOverride,
305                             info.release());
306  return true;
307}
308
309bool SettingsOverridesHandler::Validate(
310    const Extension* extension,
311    std::string* error,
312    std::vector<InstallWarning>* warnings) const {
313  const SettingsOverrides* settings_overrides =
314      SettingsOverrides::Get(extension);
315
316  if (settings_overrides && settings_overrides->bookmarks_ui) {
317    if (!FeatureSwitch::enable_override_bookmarks_ui()->IsEnabled()) {
318      warnings->push_back(InstallWarning(
319          ErrorUtils::FormatErrorMessage(
320              manifest_errors::kUnrecognizedManifestProperty,
321              manifest_keys::kBookmarkUI,
322              manifest_keys::kSettingsOverride)));
323    } else if (settings_overrides->bookmarks_ui->hide_bookmark_button) {
324      warnings->push_back(InstallWarning(
325            ErrorUtils::FormatErrorMessage(
326                manifest_errors::kKeyIsDeprecatedWithReplacement,
327                manifest_keys::kHideBookmarkButton,
328                manifest_keys::kRemoveButton)));
329    }
330  }
331
332  return true;
333}
334
335ManifestPermission* SettingsOverridesHandler::CreatePermission() {
336  return new ManifestPermissionImpl(false);
337}
338
339ManifestPermission* SettingsOverridesHandler::CreateInitialRequiredPermission(
340    const Extension* extension) {
341  const SettingsOverrides* data = SettingsOverrides::Get(extension);
342  if (data)
343    return data->manifest_permission->Clone();
344  return NULL;
345}
346const std::vector<std::string> SettingsOverridesHandler::Keys() const {
347  return SingleKey(manifest_keys::kSettingsOverride);
348}
349
350}  // namespace extensions
351