third_party/re2: Remove remove-static-initializers.patch.
[chromium-blink-merge.git] / content / renderer / manifest / manifest_parser.cc
blobbb4fc4e37f48711a70fcaf6f81a38d4e7a67d92c
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_.related_applications = ParseRelatedApplications(*dictionary);
131 manifest_.prefer_related_applications =
132 ParsePreferRelatedApplications(*dictionary);
133 manifest_.gcm_sender_id = ParseGCMSenderID(*dictionary);
134 manifest_.gcm_user_visible_only = ParseGCMUserVisibleOnly(*dictionary);
136 ManifestUmaUtil::ParseSucceeded(manifest_);
139 const Manifest& ManifestParser::manifest() const {
140 return manifest_;
143 const std::vector<std::string>& ManifestParser::errors() const {
144 return errors_;
147 bool ManifestParser::failed() const {
148 return failed_;
151 bool ManifestParser::ParseBoolean(const base::DictionaryValue& dictionary,
152 const std::string& key,
153 bool default_value) {
154 if (!dictionary.HasKey(key))
155 return default_value;
157 bool value;
158 if (!dictionary.GetBoolean(key, &value)) {
159 errors_.push_back(GetErrorPrefix() +
160 "property '" + key + "' ignored, type boolean expected.");
161 return default_value;
164 return value;
167 base::NullableString16 ManifestParser::ParseString(
168 const base::DictionaryValue& dictionary,
169 const std::string& key,
170 TrimType trim) {
171 if (!dictionary.HasKey(key))
172 return base::NullableString16();
174 base::string16 value;
175 if (!dictionary.GetString(key, &value)) {
176 errors_.push_back(GetErrorPrefix() +
177 "property '" + key + "' ignored, type string expected.");
178 return base::NullableString16();
181 if (trim == Trim)
182 base::TrimWhitespace(value, base::TRIM_ALL, &value);
183 return base::NullableString16(value, false);
186 GURL ManifestParser::ParseURL(const base::DictionaryValue& dictionary,
187 const std::string& key,
188 const GURL& base_url) {
189 base::NullableString16 url_str = ParseString(dictionary, key, NoTrim);
190 if (url_str.is_null())
191 return GURL();
193 return base_url.Resolve(url_str.string());
196 base::NullableString16 ManifestParser::ParseName(
197 const base::DictionaryValue& dictionary) {
198 return ParseString(dictionary, "name", Trim);
201 base::NullableString16 ManifestParser::ParseShortName(
202 const base::DictionaryValue& dictionary) {
203 return ParseString(dictionary, "short_name", Trim);
206 GURL ManifestParser::ParseStartURL(const base::DictionaryValue& dictionary) {
207 GURL start_url = ParseURL(dictionary, "start_url", manifest_url_);
208 if (!start_url.is_valid())
209 return GURL();
211 if (start_url.GetOrigin() != document_url_.GetOrigin()) {
212 errors_.push_back(GetErrorPrefix() + "property 'start_url' ignored, should "
213 "be same origin as document.");
214 return GURL();
217 return start_url;
220 Manifest::DisplayMode ManifestParser::ParseDisplay(
221 const base::DictionaryValue& dictionary) {
222 base::NullableString16 display = ParseString(dictionary, "display", Trim);
223 if (display.is_null())
224 return Manifest::DISPLAY_MODE_UNSPECIFIED;
226 if (LowerCaseEqualsASCII(display.string(), "fullscreen"))
227 return Manifest::DISPLAY_MODE_FULLSCREEN;
228 else if (LowerCaseEqualsASCII(display.string(), "standalone"))
229 return Manifest::DISPLAY_MODE_STANDALONE;
230 else if (LowerCaseEqualsASCII(display.string(), "minimal-ui"))
231 return Manifest::DISPLAY_MODE_MINIMAL_UI;
232 else if (LowerCaseEqualsASCII(display.string(), "browser"))
233 return Manifest::DISPLAY_MODE_BROWSER;
234 else {
235 errors_.push_back(GetErrorPrefix() + "unknown 'display' value ignored.");
236 return Manifest::DISPLAY_MODE_UNSPECIFIED;
240 blink::WebScreenOrientationLockType ManifestParser::ParseOrientation(
241 const base::DictionaryValue& dictionary) {
242 base::NullableString16 orientation =
243 ParseString(dictionary, "orientation", Trim);
245 if (orientation.is_null())
246 return blink::WebScreenOrientationLockDefault;
248 if (LowerCaseEqualsASCII(orientation.string(), "any"))
249 return blink::WebScreenOrientationLockAny;
250 else if (LowerCaseEqualsASCII(orientation.string(), "natural"))
251 return blink::WebScreenOrientationLockNatural;
252 else if (LowerCaseEqualsASCII(orientation.string(), "landscape"))
253 return blink::WebScreenOrientationLockLandscape;
254 else if (LowerCaseEqualsASCII(orientation.string(), "landscape-primary"))
255 return blink::WebScreenOrientationLockLandscapePrimary;
256 else if (LowerCaseEqualsASCII(orientation.string(), "landscape-secondary"))
257 return blink::WebScreenOrientationLockLandscapeSecondary;
258 else if (LowerCaseEqualsASCII(orientation.string(), "portrait"))
259 return blink::WebScreenOrientationLockPortrait;
260 else if (LowerCaseEqualsASCII(orientation.string(), "portrait-primary"))
261 return blink::WebScreenOrientationLockPortraitPrimary;
262 else if (LowerCaseEqualsASCII(orientation.string(), "portrait-secondary"))
263 return blink::WebScreenOrientationLockPortraitSecondary;
264 else {
265 errors_.push_back(GetErrorPrefix() +
266 "unknown 'orientation' value ignored.");
267 return blink::WebScreenOrientationLockDefault;
271 GURL ManifestParser::ParseIconSrc(const base::DictionaryValue& icon) {
272 return ParseURL(icon, "src", manifest_url_);
275 base::NullableString16 ManifestParser::ParseIconType(
276 const base::DictionaryValue& icon) {
277 return ParseString(icon, "type", Trim);
280 double ManifestParser::ParseIconDensity(const base::DictionaryValue& icon) {
281 double density;
282 if (!icon.HasKey("density"))
283 return Manifest::Icon::kDefaultDensity;
285 if (!icon.GetDouble("density", &density) || density <= 0) {
286 errors_.push_back(GetErrorPrefix() +
287 "icon 'density' ignored, must be float greater than 0.");
288 return Manifest::Icon::kDefaultDensity;
290 return density;
293 std::vector<gfx::Size> ManifestParser::ParseIconSizes(
294 const base::DictionaryValue& icon) {
295 base::NullableString16 sizes_str = ParseString(icon, "sizes", NoTrim);
297 if (sizes_str.is_null())
298 return std::vector<gfx::Size>();
300 std::vector<gfx::Size> sizes = ParseIconSizesHTML(sizes_str.string());
301 if (sizes.empty()) {
302 errors_.push_back(GetErrorPrefix() + "found icon with no valid size.");
304 return sizes;
307 std::vector<Manifest::Icon> ManifestParser::ParseIcons(
308 const base::DictionaryValue& dictionary) {
309 std::vector<Manifest::Icon> icons;
310 if (!dictionary.HasKey("icons"))
311 return icons;
313 const base::ListValue* icons_list = nullptr;
314 if (!dictionary.GetList("icons", &icons_list)) {
315 errors_.push_back(GetErrorPrefix() +
316 "property 'icons' ignored, type array expected.");
317 return icons;
320 for (size_t i = 0; i < icons_list->GetSize(); ++i) {
321 const base::DictionaryValue* icon_dictionary = nullptr;
322 if (!icons_list->GetDictionary(i, &icon_dictionary))
323 continue;
325 Manifest::Icon icon;
326 icon.src = ParseIconSrc(*icon_dictionary);
327 // An icon MUST have a valid src. If it does not, it MUST be ignored.
328 if (!icon.src.is_valid())
329 continue;
330 icon.type = ParseIconType(*icon_dictionary);
331 icon.density = ParseIconDensity(*icon_dictionary);
332 icon.sizes = ParseIconSizes(*icon_dictionary);
334 icons.push_back(icon);
337 return icons;
340 base::NullableString16 ManifestParser::ParseRelatedApplicationPlatform(
341 const base::DictionaryValue& application) {
342 return ParseString(application, "platform", Trim);
345 GURL ManifestParser::ParseRelatedApplicationURL(
346 const base::DictionaryValue& application) {
347 return ParseURL(application, "url", manifest_url_);
350 base::NullableString16 ManifestParser::ParseRelatedApplicationId(
351 const base::DictionaryValue& application) {
352 return ParseString(application, "id", Trim);
355 std::vector<Manifest::RelatedApplication>
356 ManifestParser::ParseRelatedApplications(
357 const base::DictionaryValue& dictionary) {
358 std::vector<Manifest::RelatedApplication> applications;
359 if (!dictionary.HasKey("related_applications"))
360 return applications;
362 const base::ListValue* applications_list = nullptr;
363 if (!dictionary.GetList("related_applications", &applications_list)) {
364 errors_.push_back(
365 GetErrorPrefix() +
366 "property 'related_applications' ignored, type array expected.");
367 return applications;
370 for (size_t i = 0; i < applications_list->GetSize(); ++i) {
371 const base::DictionaryValue* application_dictionary = nullptr;
372 if (!applications_list->GetDictionary(i, &application_dictionary))
373 continue;
375 Manifest::RelatedApplication application;
376 application.platform =
377 ParseRelatedApplicationPlatform(*application_dictionary);
378 // "If platform is undefined, move onto the next item if any are left."
379 if (application.platform.is_null()) {
380 errors_.push_back(
381 GetErrorPrefix() +
382 "'platform' is a required field, related application ignored.");
383 continue;
386 application.id = ParseRelatedApplicationId(*application_dictionary);
387 application.url = ParseRelatedApplicationURL(*application_dictionary);
388 // "If both id and url are undefined, move onto the next item if any are
389 // left."
390 if (application.url.is_empty() && application.id.is_null()) {
391 errors_.push_back(
392 GetErrorPrefix() +
393 "one of 'url' or 'id' is required, related application ignored.");
394 continue;
397 applications.push_back(application);
400 return applications;
403 bool ManifestParser::ParsePreferRelatedApplications(
404 const base::DictionaryValue& dictionary) {
405 return ParseBoolean(dictionary, "prefer_related_applications", false);
408 base::NullableString16 ManifestParser::ParseGCMSenderID(
409 const base::DictionaryValue& dictionary) {
410 return ParseString(dictionary, "gcm_sender_id", Trim);
413 bool ManifestParser::ParseGCMUserVisibleOnly(
414 const base::DictionaryValue& dictionary) {
415 return ParseBoolean(dictionary, "gcm_user_visible_only", false);
418 } // namespace content