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 "chrome/common/extensions/manifest_url_handler.h"
6
7#include "base/files/file_util.h"
8#include "base/lazy_instance.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/strings/string_util.h"
11#include "base/strings/stringprintf.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/values.h"
14#include "chrome/common/chrome_constants.h"
15#include "chrome/common/url_constants.h"
16#include "chrome/grit/generated_resources.h"
17#include "extensions/common/error_utils.h"
18#include "extensions/common/extension_urls.h"
19#include "extensions/common/file_util.h"
20#include "extensions/common/manifest.h"
21#include "extensions/common/manifest_constants.h"
22#include "extensions/common/manifest_handlers/permissions_parser.h"
23#include "extensions/common/manifest_handlers/shared_module_info.h"
24#include "extensions/common/permissions/api_permission.h"
25#include "extensions/common/permissions/api_permission_set.h"
26#include "ui/base/l10n/l10n_util.h"
27
28#if defined(USE_AURA)
29#include "ui/keyboard/keyboard_constants.h"
30#endif
31
32namespace extensions {
33
34namespace keys = manifest_keys;
35namespace errors = manifest_errors;
36
37namespace {
38
39const char kOverrideExtentUrlPatternFormat[] = "chrome://%s/*";
40
41const GURL& GetManifestURL(const Extension* extension,
42                           const std::string& key) {
43  ManifestURL* manifest_url =
44      static_cast<ManifestURL*>(extension->GetManifestData(key));
45  return manifest_url ? manifest_url->url_ : GURL::EmptyGURL();
46}
47
48}  // namespace
49
50// static
51const GURL& ManifestURL::GetDevToolsPage(const Extension* extension) {
52  return GetManifestURL(extension, keys::kDevToolsPage);
53}
54
55// static
56const GURL ManifestURL::GetHomepageURL(const Extension* extension) {
57  const GURL& homepage_url = GetManifestURL(extension, keys::kHomepageURL);
58  if (homepage_url.is_valid())
59    return homepage_url;
60  bool use_webstore_url = UpdatesFromGallery(extension) &&
61                          !SharedModuleInfo::IsSharedModule(extension);
62  return use_webstore_url
63             ? GURL(extension_urls::GetWebstoreItemDetailURLPrefix() +
64                    extension->id())
65             : GURL::EmptyGURL();
66}
67
68// static
69bool ManifestURL::SpecifiedHomepageURL(const Extension* extension) {
70  return GetManifestURL(extension, keys::kHomepageURL).is_valid();
71}
72
73// static
74const GURL& ManifestURL::GetUpdateURL(const Extension* extension) {
75  return GetManifestURL(extension, keys::kUpdateURL);
76}
77
78// static
79bool ManifestURL::UpdatesFromGallery(const Extension* extension) {
80  return extension_urls::IsWebstoreUpdateUrl(GetUpdateURL(extension));
81}
82
83// static
84bool  ManifestURL::UpdatesFromGallery(const base::DictionaryValue* manifest) {
85  std::string url;
86  if (!manifest->GetString(keys::kUpdateURL, &url))
87    return false;
88  return extension_urls::IsWebstoreUpdateUrl(GURL(url));
89}
90
91// static
92const GURL& ManifestURL::GetAboutPage(const Extension* extension) {
93  return GetManifestURL(extension, keys::kAboutPage);
94}
95
96// static
97const GURL ManifestURL::GetDetailsURL(const Extension* extension) {
98  return extension->from_webstore() ?
99      GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + extension->id()) :
100      GURL::EmptyGURL();
101}
102
103URLOverrides::URLOverrides() {
104}
105
106URLOverrides::~URLOverrides() {
107}
108
109static base::LazyInstance<URLOverrides::URLOverrideMap> g_empty_url_overrides =
110    LAZY_INSTANCE_INITIALIZER;
111
112// static
113const URLOverrides::URLOverrideMap&
114    URLOverrides::GetChromeURLOverrides(const Extension* extension) {
115  URLOverrides* url_overrides = static_cast<URLOverrides*>(
116      extension->GetManifestData(keys::kChromeURLOverrides));
117  return url_overrides ?
118         url_overrides->chrome_url_overrides_ :
119         g_empty_url_overrides.Get();
120}
121
122DevToolsPageHandler::DevToolsPageHandler() {
123}
124
125DevToolsPageHandler::~DevToolsPageHandler() {
126}
127
128bool DevToolsPageHandler::Parse(Extension* extension, base::string16* error) {
129  scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
130  std::string devtools_str;
131  if (!extension->manifest()->GetString(keys::kDevToolsPage, &devtools_str)) {
132    *error = base::ASCIIToUTF16(errors::kInvalidDevToolsPage);
133    return false;
134  }
135  manifest_url->url_ = extension->GetResourceURL(devtools_str);
136  extension->SetManifestData(keys::kDevToolsPage, manifest_url.release());
137  PermissionsParser::AddAPIPermission(extension, APIPermission::kDevtools);
138  return true;
139}
140
141const std::vector<std::string> DevToolsPageHandler::Keys() const {
142  return SingleKey(keys::kDevToolsPage);
143}
144
145HomepageURLHandler::HomepageURLHandler() {
146}
147
148HomepageURLHandler::~HomepageURLHandler() {
149}
150
151bool HomepageURLHandler::Parse(Extension* extension, base::string16* error) {
152  scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
153  std::string homepage_url_str;
154  if (!extension->manifest()->GetString(keys::kHomepageURL,
155                                        &homepage_url_str)) {
156    *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidHomepageURL,
157                                                 std::string());
158    return false;
159  }
160  manifest_url->url_ = GURL(homepage_url_str);
161  if (!manifest_url->url_.is_valid() ||
162      !manifest_url->url_.SchemeIsHTTPOrHTTPS()) {
163    *error = ErrorUtils::FormatErrorMessageUTF16(
164        errors::kInvalidHomepageURL, homepage_url_str);
165    return false;
166  }
167  extension->SetManifestData(keys::kHomepageURL, manifest_url.release());
168  return true;
169}
170
171const std::vector<std::string> HomepageURLHandler::Keys() const {
172  return SingleKey(keys::kHomepageURL);
173}
174
175UpdateURLHandler::UpdateURLHandler() {
176}
177
178UpdateURLHandler::~UpdateURLHandler() {
179}
180
181bool UpdateURLHandler::Parse(Extension* extension, base::string16* error) {
182  scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
183  std::string tmp_update_url;
184
185  if (!extension->manifest()->GetString(keys::kUpdateURL, &tmp_update_url)) {
186    *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidUpdateURL,
187                                                 std::string());
188    return false;
189  }
190
191  manifest_url->url_ = GURL(tmp_update_url);
192  if (!manifest_url->url_.is_valid() ||
193      manifest_url->url_.has_ref()) {
194    *error = ErrorUtils::FormatErrorMessageUTF16(
195        errors::kInvalidUpdateURL, tmp_update_url);
196    return false;
197  }
198
199  extension->SetManifestData(keys::kUpdateURL, manifest_url.release());
200  return true;
201}
202
203const std::vector<std::string> UpdateURLHandler::Keys() const {
204  return SingleKey(keys::kUpdateURL);
205}
206
207AboutPageHandler::AboutPageHandler() {
208}
209
210AboutPageHandler::~AboutPageHandler() {
211}
212
213bool AboutPageHandler::Parse(Extension* extension, base::string16* error) {
214  scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
215  std::string about_str;
216  if (!extension->manifest()->GetString(keys::kAboutPage, &about_str)) {
217    *error = base::ASCIIToUTF16(errors::kInvalidAboutPage);
218    return false;
219  }
220
221  GURL absolute(about_str);
222  if (absolute.is_valid()) {
223    *error = base::ASCIIToUTF16(errors::kInvalidAboutPageExpectRelativePath);
224    return false;
225  }
226  manifest_url->url_ = extension->GetResourceURL(about_str);
227  if (!manifest_url->url_.is_valid()) {
228    *error = base::ASCIIToUTF16(errors::kInvalidAboutPage);
229    return false;
230  }
231  extension->SetManifestData(keys::kAboutPage, manifest_url.release());
232  return true;
233}
234
235bool AboutPageHandler::Validate(const Extension* extension,
236                                std::string* error,
237                                std::vector<InstallWarning>* warnings) const {
238  // Validate path to the options page.
239  if (!extensions::ManifestURL::GetAboutPage(extension).is_empty()) {
240    const base::FilePath about_path =
241        extensions::file_util::ExtensionURLToRelativeFilePath(
242            extensions::ManifestURL::GetAboutPage(extension));
243    const base::FilePath path =
244        extension->GetResource(about_path).GetFilePath();
245    if (path.empty() || !base::PathExists(path)) {
246      *error = l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_ABOUT_PAGE_FAILED,
247                                         about_path.LossyDisplayName());
248      return false;
249    }
250  }
251  return true;
252}
253
254const std::vector<std::string> AboutPageHandler::Keys() const {
255  return SingleKey(keys::kAboutPage);
256}
257
258URLOverridesHandler::URLOverridesHandler() {
259}
260
261URLOverridesHandler::~URLOverridesHandler() {
262}
263
264bool URLOverridesHandler::Parse(Extension* extension, base::string16* error) {
265  const base::DictionaryValue* overrides = NULL;
266  if (!extension->manifest()->GetDictionary(keys::kChromeURLOverrides,
267                                            &overrides)) {
268    *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
269    return false;
270  }
271  scoped_ptr<URLOverrides> url_overrides(new URLOverrides);
272  // Validate that the overrides are all strings
273  for (base::DictionaryValue::Iterator iter(*overrides); !iter.IsAtEnd();
274         iter.Advance()) {
275    std::string page = iter.key();
276    std::string val;
277    // Restrict override pages to a list of supported URLs.
278    bool is_override = (page != chrome::kChromeUINewTabHost &&
279                        page != chrome::kChromeUIBookmarksHost &&
280                        page != chrome::kChromeUIHistoryHost);
281#if defined(OS_CHROMEOS)
282    is_override = (is_override &&
283                   page != chrome::kChromeUIActivationMessageHost);
284#endif
285#if defined(OS_CHROMEOS)
286    is_override = (is_override && page != keyboard::kKeyboardHost);
287#endif
288
289    if (is_override || !iter.value().GetAsString(&val)) {
290      *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
291      return false;
292    }
293    // Replace the entry with a fully qualified chrome-extension:// URL.
294    url_overrides->chrome_url_overrides_[page] = extension->GetResourceURL(val);
295
296    // For component extensions, add override URL to extent patterns.
297    if (extension->is_legacy_packaged_app() &&
298        extension->location() == Manifest::COMPONENT) {
299      URLPattern pattern(URLPattern::SCHEME_CHROMEUI);
300      std::string url = base::StringPrintf(kOverrideExtentUrlPatternFormat,
301                                           page.c_str());
302      if (pattern.Parse(url) != URLPattern::PARSE_SUCCESS) {
303        *error = ErrorUtils::FormatErrorMessageUTF16(
304            errors::kInvalidURLPatternError, url);
305        return false;
306      }
307      extension->AddWebExtentPattern(pattern);
308    }
309  }
310
311  // An extension may override at most one page.
312  if (overrides->size() > 1) {
313    *error = base::ASCIIToUTF16(errors::kMultipleOverrides);
314    return false;
315  }
316  extension->SetManifestData(keys::kChromeURLOverrides,
317                             url_overrides.release());
318  return true;
319}
320
321const std::vector<std::string> URLOverridesHandler::Keys() const {
322  return SingleKey(keys::kChromeURLOverrides);
323}
324
325}  // namespace extensions
326