[Ozone-Gbm] Explicitly crash if trying software rendering on GBM
[chromium-blink-merge.git] / content / renderer / manifest / manifest_parser.cc
blob9a3e8520932cc7d18b6054066deda1d8f0a65c29
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 "content/renderer/manifest/manifest_parser.h"
7 #include "base/json/json_reader.h"
8 #include "base/strings/nullable_string16.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_split.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "content/public/common/manifest.h"
15 #include "content/renderer/manifest/manifest_uma_util.h"
16 #include "ui/gfx/geometry/size.h"
18 namespace content {
20 namespace {
22 // Helper function that returns whether the given |str| is a valid width or
23 // height value for an icon sizes per:
24 // https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes
25 bool IsValidIconWidthOrHeight(const std::string& str) {
26 if (str.empty() || str[0] == '0')
27 return false;
28 for (size_t i = 0; i < str.size(); ++i)
29 if (!IsAsciiDigit(str[i]))
30 return false;
31 return true;
34 // Parses the 'sizes' attribute of an icon as described in the HTML spec:
35 // https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes
36 // Return a vector of gfx::Size that contains the valid sizes found. "Any" is
37 // represented by gfx::Size(0, 0).
38 // TODO(mlamouri): this is implemented as a separate function because it should
39 // be refactored with the other icon sizes parsing implementations, see
40 // http://crbug.com/416477
41 std::vector<gfx::Size> ParseIconSizesHTML(const base::string16& sizes_str16) {
42 if (!base::IsStringASCII(sizes_str16))
43 return std::vector<gfx::Size>();
45 std::vector<gfx::Size> sizes;
46 std::string sizes_str =
47 base::StringToLowerASCII(base::UTF16ToUTF8(sizes_str16));
48 std::vector<std::string> sizes_str_list;
49 base::SplitStringAlongWhitespace(sizes_str, &sizes_str_list);
51 for (size_t i = 0; i < sizes_str_list.size(); ++i) {
52 std::string& size_str = sizes_str_list[i];
53 if (size_str == "any") {
54 sizes.push_back(gfx::Size(0, 0));
55 continue;
58 // It is expected that [0] => width and [1] => height after the split.
59 std::vector<std::string> size_list;
60 base::SplitStringDontTrim(size_str, L'x', &size_list);
61 if (size_list.size() != 2)
62 continue;
63 if (!IsValidIconWidthOrHeight(size_list[0]) ||
64 !IsValidIconWidthOrHeight(size_list[1])) {
65 continue;
68 int width, height;
69 if (!base::StringToInt(size_list[0], &width) ||
70 !base::StringToInt(size_list[1], &height)) {
71 continue;
74 sizes.push_back(gfx::Size(width, height));
77 return sizes;
80 const std::string& GetErrorPrefix() {
81 CR_DEFINE_STATIC_LOCAL(std::string, error_prefix,
82 ("Manifest parsing error: "));
83 return error_prefix;
86 } // anonymous namespace
89 ManifestParser::ManifestParser(const base::StringPiece& data,
90 const GURL& manifest_url,
91 const GURL& document_url)
92 : data_(data),
93 manifest_url_(manifest_url),
94 document_url_(document_url),
95 failed_(false) {
98 ManifestParser::~ManifestParser() {
101 void ManifestParser::Parse() {
102 std::string parse_error;
103 scoped_ptr<base::Value> value(
104 base::JSONReader::ReadAndReturnError(data_, base::JSON_PARSE_RFC,
105 nullptr, &parse_error));
107 if (!value) {
108 errors_.push_back(GetErrorPrefix() + parse_error);
109 ManifestUmaUtil::ParseFailed();
110 failed_ = true;
111 return;
114 base::DictionaryValue* dictionary = nullptr;
115 if (!value->GetAsDictionary(&dictionary)) {
116 errors_.push_back(GetErrorPrefix() +
117 "root element must be a valid JSON object.");
118 ManifestUmaUtil::ParseFailed();
119 failed_ = true;
120 return;
122 DCHECK(dictionary);
124 manifest_.name = ParseName(*dictionary);
125 manifest_.short_name = ParseShortName(*dictionary);
126 manifest_.start_url = ParseStartURL(*dictionary);
127 manifest_.display = ParseDisplay(*dictionary);
128 manifest_.orientation = ParseOrientation(*dictionary);
129 manifest_.icons = ParseIcons(*dictionary);
130 manifest_.gcm_sender_id = ParseGCMSenderID(*dictionary);
131 manifest_.gcm_user_visible_only = ParseGCMUserVisibleOnly(*dictionary);
133 ManifestUmaUtil::ParseSucceeded(manifest_);
136 const Manifest& ManifestParser::manifest() const {
137 return manifest_;
140 const std::vector<std::string>& ManifestParser::errors() const {
141 return errors_;
144 bool ManifestParser::failed() const {
145 return failed_;
148 bool ManifestParser::ParseBoolean(const base::DictionaryValue& dictionary,
149 const std::string& key,
150 bool default_value) {
151 if (!dictionary.HasKey(key))
152 return default_value;
154 bool value;
155 if (!dictionary.GetBoolean(key, &value)) {
156 errors_.push_back(GetErrorPrefix() +
157 "property '" + key + "' ignored, type boolean expected.");
158 return default_value;
161 return value;
164 base::NullableString16 ManifestParser::ParseString(
165 const base::DictionaryValue& dictionary,
166 const std::string& key,
167 TrimType trim) {
168 if (!dictionary.HasKey(key))
169 return base::NullableString16();
171 base::string16 value;
172 if (!dictionary.GetString(key, &value)) {
173 errors_.push_back(GetErrorPrefix() +
174 "property '" + key + "' ignored, type string expected.");
175 return base::NullableString16();
178 if (trim == Trim)
179 base::TrimWhitespace(value, base::TRIM_ALL, &value);
180 return base::NullableString16(value, false);
183 GURL ManifestParser::ParseURL(const base::DictionaryValue& dictionary,
184 const std::string& key,
185 const GURL& base_url) {
186 base::NullableString16 url_str = ParseString(dictionary, key, NoTrim);
187 if (url_str.is_null())
188 return GURL();
190 return base_url.Resolve(url_str.string());
193 base::NullableString16 ManifestParser::ParseName(
194 const base::DictionaryValue& dictionary) {
195 return ParseString(dictionary, "name", Trim);
198 base::NullableString16 ManifestParser::ParseShortName(
199 const base::DictionaryValue& dictionary) {
200 return ParseString(dictionary, "short_name", Trim);
203 GURL ManifestParser::ParseStartURL(const base::DictionaryValue& dictionary) {
204 GURL start_url = ParseURL(dictionary, "start_url", manifest_url_);
205 if (!start_url.is_valid())
206 return GURL();
208 if (start_url.GetOrigin() != document_url_.GetOrigin()) {
209 errors_.push_back(GetErrorPrefix() + "property 'start_url' ignored, should "
210 "be same origin as document.");
211 return GURL();
214 return start_url;
217 Manifest::DisplayMode ManifestParser::ParseDisplay(
218 const base::DictionaryValue& dictionary) {
219 base::NullableString16 display = ParseString(dictionary, "display", Trim);
220 if (display.is_null())
221 return Manifest::DISPLAY_MODE_UNSPECIFIED;
223 if (LowerCaseEqualsASCII(display.string(), "fullscreen"))
224 return Manifest::DISPLAY_MODE_FULLSCREEN;
225 else if (LowerCaseEqualsASCII(display.string(), "standalone"))
226 return Manifest::DISPLAY_MODE_STANDALONE;
227 else if (LowerCaseEqualsASCII(display.string(), "minimal-ui"))
228 return Manifest::DISPLAY_MODE_MINIMAL_UI;
229 else if (LowerCaseEqualsASCII(display.string(), "browser"))
230 return Manifest::DISPLAY_MODE_BROWSER;
231 else {
232 errors_.push_back(GetErrorPrefix() + "unknown 'display' value ignored.");
233 return Manifest::DISPLAY_MODE_UNSPECIFIED;
237 blink::WebScreenOrientationLockType ManifestParser::ParseOrientation(
238 const base::DictionaryValue& dictionary) {
239 base::NullableString16 orientation =
240 ParseString(dictionary, "orientation", Trim);
242 if (orientation.is_null())
243 return blink::WebScreenOrientationLockDefault;
245 if (LowerCaseEqualsASCII(orientation.string(), "any"))
246 return blink::WebScreenOrientationLockAny;
247 else if (LowerCaseEqualsASCII(orientation.string(), "natural"))
248 return blink::WebScreenOrientationLockNatural;
249 else if (LowerCaseEqualsASCII(orientation.string(), "landscape"))
250 return blink::WebScreenOrientationLockLandscape;
251 else if (LowerCaseEqualsASCII(orientation.string(), "landscape-primary"))
252 return blink::WebScreenOrientationLockLandscapePrimary;
253 else if (LowerCaseEqualsASCII(orientation.string(), "landscape-secondary"))
254 return blink::WebScreenOrientationLockLandscapeSecondary;
255 else if (LowerCaseEqualsASCII(orientation.string(), "portrait"))
256 return blink::WebScreenOrientationLockPortrait;
257 else if (LowerCaseEqualsASCII(orientation.string(), "portrait-primary"))
258 return blink::WebScreenOrientationLockPortraitPrimary;
259 else if (LowerCaseEqualsASCII(orientation.string(), "portrait-secondary"))
260 return blink::WebScreenOrientationLockPortraitSecondary;
261 else {
262 errors_.push_back(GetErrorPrefix() +
263 "unknown 'orientation' value ignored.");
264 return blink::WebScreenOrientationLockDefault;
268 GURL ManifestParser::ParseIconSrc(const base::DictionaryValue& icon) {
269 return ParseURL(icon, "src", manifest_url_);
272 base::NullableString16 ManifestParser::ParseIconType(
273 const base::DictionaryValue& icon) {
274 return ParseString(icon, "type", Trim);
277 double ManifestParser::ParseIconDensity(const base::DictionaryValue& icon) {
278 double density;
279 if (!icon.HasKey("density"))
280 return Manifest::Icon::kDefaultDensity;
282 if (!icon.GetDouble("density", &density) || density <= 0) {
283 errors_.push_back(GetErrorPrefix() +
284 "icon 'density' ignored, must be float greater than 0.");
285 return Manifest::Icon::kDefaultDensity;
287 return density;
290 std::vector<gfx::Size> ManifestParser::ParseIconSizes(
291 const base::DictionaryValue& icon) {
292 base::NullableString16 sizes_str = ParseString(icon, "sizes", NoTrim);
294 if (sizes_str.is_null())
295 return std::vector<gfx::Size>();
297 std::vector<gfx::Size> sizes = ParseIconSizesHTML(sizes_str.string());
298 if (sizes.empty()) {
299 errors_.push_back(GetErrorPrefix() + "found icon with no valid size.");
301 return sizes;
304 std::vector<Manifest::Icon> ManifestParser::ParseIcons(
305 const base::DictionaryValue& dictionary) {
306 std::vector<Manifest::Icon> icons;
307 if (!dictionary.HasKey("icons"))
308 return icons;
310 const base::ListValue* icons_list = nullptr;
311 if (!dictionary.GetList("icons", &icons_list)) {
312 errors_.push_back(GetErrorPrefix() +
313 "property 'icons' ignored, type array expected.");
314 return icons;
317 for (size_t i = 0; i < icons_list->GetSize(); ++i) {
318 const base::DictionaryValue* icon_dictionary = nullptr;
319 if (!icons_list->GetDictionary(i, &icon_dictionary))
320 continue;
322 Manifest::Icon icon;
323 icon.src = ParseIconSrc(*icon_dictionary);
324 // An icon MUST have a valid src. If it does not, it MUST be ignored.
325 if (!icon.src.is_valid())
326 continue;
327 icon.type = ParseIconType(*icon_dictionary);
328 icon.density = ParseIconDensity(*icon_dictionary);
329 icon.sizes = ParseIconSizes(*icon_dictionary);
331 icons.push_back(icon);
334 return icons;
337 base::NullableString16 ManifestParser::ParseGCMSenderID(
338 const base::DictionaryValue& dictionary) {
339 return ParseString(dictionary, "gcm_sender_id", Trim);
342 bool ManifestParser::ParseGCMUserVisibleOnly(
343 const base::DictionaryValue& dictionary) {
344 return ParseBoolean(dictionary, "gcm_user_visible_only", false);
347 } // namespace content