1 // Copyright 2014 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 "extensions/common/manifest_handlers/options_page_info.h"
7 #include "base/files/file_util.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "extensions/common/api/extensions_manifest_types.h"
11 #include "extensions/common/error_utils.h"
12 #include "extensions/common/feature_switch.h"
13 #include "extensions/common/file_util.h"
14 #include "extensions/common/manifest_constants.h"
15 #include "extensions/strings/grit/extensions_strings.h"
16 #include "ui/base/l10n/l10n_util.h"
18 using base::ASCIIToUTF16
;
19 using base::DictionaryValue
;
21 namespace extensions
{
23 namespace keys
= manifest_keys
;
24 namespace errors
= manifest_errors
;
26 using api::extensions_manifest_types::OptionsUI
;
30 OptionsPageInfo
* GetOptionsPageInfo(const Extension
* extension
) {
31 return static_cast<OptionsPageInfo
*>(
32 extension
->GetManifestData(keys::kOptionsUI
));
35 // Parses |url_string| into a GURL |result| if it is a valid options page for
36 // this app/extension. If not, it returns the reason in |error|. Because this
37 // handles URLs for both "options_page" and "options_ui.page", the name of the
38 // manifest field must be provided in |manifest_field_name|.
39 bool ParseOptionsUrl(Extension
* extension
,
40 const std::string
& url_string
,
41 const std::string
& manifest_field_name
,
42 base::string16
* error
,
44 if (extension
->is_hosted_app()) {
45 // Hosted apps require an absolute URL.
46 GURL
options_url(url_string
);
47 if (!options_url
.is_valid() || !options_url
.SchemeIsHTTPOrHTTPS()) {
48 *error
= base::ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp
);
51 *result
= options_url
;
55 // Otherwise the options URL should be inside the extension.
56 if (GURL(url_string
).is_valid()) {
57 *error
= base::ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage
);
61 GURL resource_url
= extension
->GetResourceURL(url_string
);
62 if (!resource_url
.is_valid()) {
63 *error
= ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidOptionsPage
,
67 *result
= resource_url
;
73 OptionsPageInfo::OptionsPageInfo(const GURL
& options_page
,
76 : options_page_(options_page
),
77 chrome_styles_(chrome_styles
),
78 open_in_tab_(open_in_tab
) {
81 OptionsPageInfo::~OptionsPageInfo() {
85 const GURL
& OptionsPageInfo::GetOptionsPage(const Extension
* extension
) {
86 OptionsPageInfo
* info
= GetOptionsPageInfo(extension
);
87 return info
? info
->options_page_
: GURL::EmptyGURL();
91 bool OptionsPageInfo::HasOptionsPage(const Extension
* extension
) {
92 return !OptionsPageInfo::GetOptionsPage(extension
).is_empty();
96 bool OptionsPageInfo::ShouldUseChromeStyle(const Extension
* extension
) {
97 OptionsPageInfo
* info
= GetOptionsPageInfo(extension
);
98 return info
&& info
->chrome_styles_
;
102 bool OptionsPageInfo::ShouldOpenInTab(const Extension
* extension
) {
103 OptionsPageInfo
* info
= GetOptionsPageInfo(extension
);
104 return info
&& info
->open_in_tab_
;
107 scoped_ptr
<OptionsPageInfo
> OptionsPageInfo::Create(
108 Extension
* extension
,
109 const base::Value
* options_ui_value
,
110 const std::string
& options_page_string
,
111 std::vector
<InstallWarning
>* install_warnings
,
112 base::string16
* error
) {
114 // Chrome styling is always opt-in.
115 bool chrome_style
= false;
116 // Extensions can opt in or out to opening in a tab, and users can choose via
117 // the --embedded-extension-options flag which should be the default.
118 bool open_in_tab
= !FeatureSwitch::embedded_extension_options()->IsEnabled();
120 // Parse the options_ui object.
121 if (options_ui_value
) {
122 base::string16 options_ui_error
;
124 scoped_ptr
<OptionsUI
> options_ui
=
125 OptionsUI::FromValue(*options_ui_value
, &options_ui_error
);
126 if (!options_ui_error
.empty()) {
127 // OptionsUI::FromValue populates |error| both when there are
128 // errors (in which case |options_ui| will be NULL) and warnings
129 // (in which case |options_ui| will be valid). Either way, show it
130 // as an install warning.
131 install_warnings
->push_back(
132 InstallWarning(base::UTF16ToASCII(options_ui_error
)));
136 base::string16 options_parse_error
;
137 if (!ParseOptionsUrl(extension
,
140 &options_parse_error
,
142 install_warnings
->push_back(
143 InstallWarning(base::UTF16ToASCII(options_parse_error
)));
145 if (options_ui
->chrome_style
.get())
146 chrome_style
= *options_ui
->chrome_style
;
148 if (options_ui
->open_in_tab
.get())
149 open_in_tab
= *options_ui
->open_in_tab
;
153 // Parse the legacy options_page entry if there was no entry for
155 if (!options_page_string
.empty() && !options_page
.is_valid()) {
156 if (!ParseOptionsUrl(extension
,
161 return scoped_ptr
<OptionsPageInfo
>();
165 return make_scoped_ptr(
166 new OptionsPageInfo(options_page
, chrome_style
, open_in_tab
));
169 OptionsPageManifestHandler::OptionsPageManifestHandler() {
172 OptionsPageManifestHandler::~OptionsPageManifestHandler() {
175 bool OptionsPageManifestHandler::Parse(Extension
* extension
,
176 base::string16
* error
) {
177 std::vector
<InstallWarning
> install_warnings
;
178 const Manifest
* manifest
= extension
->manifest();
180 std::string options_page_string
;
181 if (manifest
->HasPath(keys::kOptionsPage
) &&
182 !manifest
->GetString(keys::kOptionsPage
, &options_page_string
)) {
183 *error
= ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidOptionsPage
,
188 const base::Value
* options_ui_value
= NULL
;
189 ignore_result(manifest
->Get(keys::kOptionsUI
, &options_ui_value
));
191 scoped_ptr
<OptionsPageInfo
> info
=
192 OptionsPageInfo::Create(extension
,
200 extension
->AddInstallWarnings(install_warnings
);
201 extension
->SetManifestData(keys::kOptionsUI
, info
.release());
205 bool OptionsPageManifestHandler::Validate(
206 const Extension
* extension
,
208 std::vector
<InstallWarning
>* warnings
) const {
209 // Validate path to the options page. Don't check the URL for hosted apps,
210 // because they are expected to refer to an external URL.
211 if (!OptionsPageInfo::HasOptionsPage(extension
) || extension
->is_hosted_app())
214 base::FilePath options_path
= file_util::ExtensionURLToRelativeFilePath(
215 OptionsPageInfo::GetOptionsPage(extension
));
216 base::FilePath path
= extension
->GetResource(options_path
).GetFilePath();
217 if (path
.empty() || !base::PathExists(path
)) {
218 *error
= l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_OPTIONS_PAGE_FAILED
,
219 options_path
.LossyDisplayName());
225 const std::vector
<std::string
> OptionsPageManifestHandler::Keys() const {
226 static const char* keys
[] = {keys::kOptionsPage
, keys::kOptionsUI
};
227 return std::vector
<std::string
>(keys
, keys
+ arraysize(keys
));
230 } // namespace extensions