Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/manifest/manifest_parser.h" | 5 #include "content/renderer/manifest/manifest_parser.h" |
| 6 | 6 |
| 7 #include "base/json/json_reader.h" | 7 #include "base/json/json_reader.h" |
| 8 #include "base/strings/nullable_string16.h" | 8 #include "base/strings/nullable_string16.h" |
| 9 #include "base/strings/string_number_conversions.h" | 9 #include "base/strings/string_number_conversions.h" |
| 10 #include "base/strings/string_split.h" | 10 #include "base/strings/string_split.h" |
| 11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
| 12 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
| 13 #include "base/values.h" | 13 #include "base/values.h" |
| 14 #include "content/public/common/manifest.h" | 14 #include "content/public/common/manifest.h" |
| 15 #include "content/renderer/manifest/manifest_uma_util.h" | 15 #include "content/renderer/manifest/manifest_uma_util.h" |
| 16 #include "ui/gfx/geometry/size.h" | 16 #include "ui/gfx/geometry/size.h" |
| 17 | 17 |
| 18 namespace content { | 18 namespace content { |
| 19 | 19 |
| 20 namespace { | 20 namespace { |
| 21 | 21 |
| 22 enum TrimType { | 22 // Helper function that returns whether the given |str| is a valid width or |
| 23 Trim, | 23 // height value for an icon sizes per: |
| 24 NoTrim | 24 // https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes |
| 25 }; | 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; | |
| 32 } | |
| 26 | 33 |
| 27 base::NullableString16 ParseString(const base::DictionaryValue& dictionary, | 34 // Parses the 'sizes' attribute of an icon as described in the HTML spec: |
| 28 const std::string& key, | 35 // https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes |
| 29 TrimType trim) { | 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>(); | |
| 44 | |
| 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); | |
| 50 | |
| 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; | |
| 56 } | |
| 57 | |
| 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; | |
| 66 } | |
| 67 | |
| 68 int width, height; | |
| 69 if (!base::StringToInt(size_list[0], &width) || | |
| 70 !base::StringToInt(size_list[1], &height)) { | |
| 71 continue; | |
| 72 } | |
| 73 | |
| 74 sizes.push_back(gfx::Size(width, height)); | |
| 75 } | |
| 76 | |
| 77 return sizes; | |
| 78 } | |
| 79 | |
| 80 const std::string& GetErrorPrefix() { | |
| 81 CR_DEFINE_STATIC_LOCAL(std::string, error_prefix, | |
| 82 ("Manifest parsing error: ")); | |
| 83 return error_prefix; | |
| 84 } | |
| 85 | |
| 86 } // anonymous namespace | |
| 87 | |
| 88 | |
| 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) { | |
| 96 } | |
| 97 | |
| 98 ManifestParser::~ManifestParser() { | |
| 99 } | |
| 100 | |
| 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)); | |
| 106 | |
| 107 if (!value) { | |
| 108 errors_.push_back(GetErrorPrefix() + parse_error); | |
| 109 ManifestUmaUtil::ParseFailed(); | |
| 110 failed_ = true; | |
| 111 return; | |
| 112 } | |
| 113 | |
| 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; | |
| 121 } | |
| 122 CHECK(dictionary); | |
|
Peter Beverloo
2014/11/27 14:37:37
*D*CHECK :-)
mlamouri (slow - plz ping)
2014/11/27 14:44:10
Done.
| |
| 123 | |
| 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 | |
| 132 ManifestUmaUtil::ParseSucceeded(manifest_); | |
| 133 } | |
| 134 | |
| 135 const Manifest& ManifestParser::manifest() const { | |
| 136 return manifest_; | |
| 137 } | |
| 138 | |
| 139 const std::vector<std::string>& ManifestParser::errors() const { | |
| 140 return errors_; | |
| 141 } | |
| 142 | |
| 143 bool ManifestParser::failed() const { | |
| 144 return failed_; | |
| 145 } | |
| 146 | |
| 147 base::NullableString16 ManifestParser::ParseString( | |
| 148 const base::DictionaryValue& dictionary, | |
| 149 const std::string& key, | |
| 150 TrimType trim) { | |
| 30 if (!dictionary.HasKey(key)) | 151 if (!dictionary.HasKey(key)) |
| 31 return base::NullableString16(); | 152 return base::NullableString16(); |
| 32 | 153 |
| 33 base::string16 value; | 154 base::string16 value; |
| 34 if (!dictionary.GetString(key, &value)) { | 155 if (!dictionary.GetString(key, &value)) { |
| 35 // TODO(mlamouri): provide a custom message to the developer console about | 156 errors_.push_back(GetErrorPrefix() + |
| 36 // the property being incorrectly set. | 157 "property '" + key + "' ignored, type string expected."); |
| 37 return base::NullableString16(); | 158 return base::NullableString16(); |
| 38 } | 159 } |
| 39 | 160 |
| 40 if (trim == Trim) | 161 if (trim == Trim) |
| 41 base::TrimWhitespace(value, base::TRIM_ALL, &value); | 162 base::TrimWhitespace(value, base::TRIM_ALL, &value); |
| 42 return base::NullableString16(value, false); | 163 return base::NullableString16(value, false); |
| 43 } | 164 } |
| 44 | 165 |
| 45 // Helper function to parse URLs present on a given |dictionary| in a given | 166 GURL ManifestParser::ParseURL(const base::DictionaryValue& dictionary, |
| 46 // field identified by its |key|. The URL is first parsed as a string then | 167 const std::string& key, |
| 47 // resolved using |base_url|. | 168 const GURL& base_url) { |
| 48 // Returns a GURL. If the parsing failed, the GURL will not be valid. | |
| 49 GURL ParseURL(const base::DictionaryValue& dictionary, | |
| 50 const std::string& key, | |
| 51 const GURL& base_url) { | |
| 52 base::NullableString16 url_str = ParseString(dictionary, key, NoTrim); | 169 base::NullableString16 url_str = ParseString(dictionary, key, NoTrim); |
| 53 if (url_str.is_null()) | 170 if (url_str.is_null()) |
| 54 return GURL(); | 171 return GURL(); |
| 55 | 172 |
| 56 return base_url.Resolve(url_str.string()); | 173 return base_url.Resolve(url_str.string()); |
| 57 } | 174 } |
| 58 | 175 |
| 59 // Parses the 'name' field of the manifest, as defined in: | 176 base::NullableString16 ManifestParser::ParseName( |
| 60 // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-name-member | 177 const base::DictionaryValue& dictionary) { |
| 61 // Returns the parsed string if any, a null string if the parsing failed. | |
| 62 base::NullableString16 ParseName(const base::DictionaryValue& dictionary) { | |
| 63 return ParseString(dictionary, "name", Trim); | 178 return ParseString(dictionary, "name", Trim); |
| 64 } | 179 } |
| 65 | 180 |
| 66 // Parses the 'short_name' field of the manifest, as defined in: | 181 base::NullableString16 ManifestParser::ParseShortName( |
| 67 // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-short-name-member | |
| 68 // Returns the parsed string if any, a null string if the parsing failed. | |
| 69 base::NullableString16 ParseShortName( | |
| 70 const base::DictionaryValue& dictionary) { | 182 const base::DictionaryValue& dictionary) { |
| 71 return ParseString(dictionary, "short_name", Trim); | 183 return ParseString(dictionary, "short_name", Trim); |
| 72 } | 184 } |
| 73 | 185 |
| 74 // Parses the 'start_url' field of the manifest, as defined in: | 186 GURL ManifestParser::ParseStartURL(const base::DictionaryValue& dictionary) { |
| 75 // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-start_url-member | 187 GURL start_url = ParseURL(dictionary, "start_url", manifest_url_); |
| 76 // Returns the parsed GURL if any, an empty GURL if the parsing failed. | |
| 77 GURL ParseStartURL(const base::DictionaryValue& dictionary, | |
| 78 const GURL& manifest_url, | |
| 79 const GURL& document_url) { | |
| 80 GURL start_url = ParseURL(dictionary, "start_url", manifest_url); | |
| 81 if (!start_url.is_valid()) | 188 if (!start_url.is_valid()) |
| 82 return GURL(); | 189 return GURL(); |
| 83 | 190 |
| 84 if (start_url.GetOrigin() != document_url.GetOrigin()) { | 191 if (start_url.GetOrigin() != document_url_.GetOrigin()) { |
| 85 // TODO(mlamouri): provide a custom message to the developer console. | 192 errors_.push_back(GetErrorPrefix() + "property 'start_url' ignored, should " |
| 193 "be same origin as document."); | |
| 86 return GURL(); | 194 return GURL(); |
| 87 } | 195 } |
| 88 | 196 |
| 89 return start_url; | 197 return start_url; |
| 90 } | 198 } |
| 91 | 199 |
| 92 // Parses the 'display' field of the manifest, as defined in: | 200 Manifest::DisplayMode ManifestParser::ParseDisplay( |
| 93 // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-display-member | 201 const base::DictionaryValue& dictionary) { |
| 94 // Returns the parsed DisplayMode if any, DISPLAY_MODE_UNSPECIFIED if the | |
| 95 // parsing failed. | |
| 96 Manifest::DisplayMode ParseDisplay(const base::DictionaryValue& dictionary) { | |
| 97 base::NullableString16 display = ParseString(dictionary, "display", Trim); | 202 base::NullableString16 display = ParseString(dictionary, "display", Trim); |
| 98 if (display.is_null()) | 203 if (display.is_null()) |
| 99 return Manifest::DISPLAY_MODE_UNSPECIFIED; | 204 return Manifest::DISPLAY_MODE_UNSPECIFIED; |
| 100 | 205 |
| 101 if (LowerCaseEqualsASCII(display.string(), "fullscreen")) | 206 if (LowerCaseEqualsASCII(display.string(), "fullscreen")) |
| 102 return Manifest::DISPLAY_MODE_FULLSCREEN; | 207 return Manifest::DISPLAY_MODE_FULLSCREEN; |
| 103 else if (LowerCaseEqualsASCII(display.string(), "standalone")) | 208 else if (LowerCaseEqualsASCII(display.string(), "standalone")) |
| 104 return Manifest::DISPLAY_MODE_STANDALONE; | 209 return Manifest::DISPLAY_MODE_STANDALONE; |
| 105 else if (LowerCaseEqualsASCII(display.string(), "minimal-ui")) | 210 else if (LowerCaseEqualsASCII(display.string(), "minimal-ui")) |
| 106 return Manifest::DISPLAY_MODE_MINIMAL_UI; | 211 return Manifest::DISPLAY_MODE_MINIMAL_UI; |
| 107 else if (LowerCaseEqualsASCII(display.string(), "browser")) | 212 else if (LowerCaseEqualsASCII(display.string(), "browser")) |
| 108 return Manifest::DISPLAY_MODE_BROWSER; | 213 return Manifest::DISPLAY_MODE_BROWSER; |
| 109 else | 214 else { |
| 215 errors_.push_back(GetErrorPrefix() + "unknown 'display' value ignored."); | |
| 110 return Manifest::DISPLAY_MODE_UNSPECIFIED; | 216 return Manifest::DISPLAY_MODE_UNSPECIFIED; |
| 217 } | |
| 111 } | 218 } |
| 112 | 219 |
| 113 // Parses the 'orientation' field of the manifest, as defined in: | 220 blink::WebScreenOrientationLockType ManifestParser::ParseOrientation( |
| 114 // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-orientation-membe r | |
| 115 // Returns the parsed WebScreenOrientationLockType if any, | |
| 116 // WebScreenOrientationLockDefault if the parsing failed. | |
| 117 blink::WebScreenOrientationLockType ParseOrientation( | |
| 118 const base::DictionaryValue& dictionary) { | 221 const base::DictionaryValue& dictionary) { |
| 119 base::NullableString16 orientation = | 222 base::NullableString16 orientation = |
| 120 ParseString(dictionary, "orientation", Trim); | 223 ParseString(dictionary, "orientation", Trim); |
| 121 | 224 |
| 122 if (orientation.is_null()) | 225 if (orientation.is_null()) |
| 123 return blink::WebScreenOrientationLockDefault; | 226 return blink::WebScreenOrientationLockDefault; |
| 124 | 227 |
| 125 if (LowerCaseEqualsASCII(orientation.string(), "any")) | 228 if (LowerCaseEqualsASCII(orientation.string(), "any")) |
| 126 return blink::WebScreenOrientationLockAny; | 229 return blink::WebScreenOrientationLockAny; |
| 127 else if (LowerCaseEqualsASCII(orientation.string(), "natural")) | 230 else if (LowerCaseEqualsASCII(orientation.string(), "natural")) |
| 128 return blink::WebScreenOrientationLockNatural; | 231 return blink::WebScreenOrientationLockNatural; |
| 129 else if (LowerCaseEqualsASCII(orientation.string(), "landscape")) | 232 else if (LowerCaseEqualsASCII(orientation.string(), "landscape")) |
| 130 return blink::WebScreenOrientationLockLandscape; | 233 return blink::WebScreenOrientationLockLandscape; |
| 131 else if (LowerCaseEqualsASCII(orientation.string(), "landscape-primary")) | 234 else if (LowerCaseEqualsASCII(orientation.string(), "landscape-primary")) |
| 132 return blink::WebScreenOrientationLockLandscapePrimary; | 235 return blink::WebScreenOrientationLockLandscapePrimary; |
| 133 else if (LowerCaseEqualsASCII(orientation.string(), "landscape-secondary")) | 236 else if (LowerCaseEqualsASCII(orientation.string(), "landscape-secondary")) |
| 134 return blink::WebScreenOrientationLockLandscapeSecondary; | 237 return blink::WebScreenOrientationLockLandscapeSecondary; |
| 135 else if (LowerCaseEqualsASCII(orientation.string(), "portrait")) | 238 else if (LowerCaseEqualsASCII(orientation.string(), "portrait")) |
| 136 return blink::WebScreenOrientationLockPortrait; | 239 return blink::WebScreenOrientationLockPortrait; |
| 137 else if (LowerCaseEqualsASCII(orientation.string(), "portrait-primary")) | 240 else if (LowerCaseEqualsASCII(orientation.string(), "portrait-primary")) |
| 138 return blink::WebScreenOrientationLockPortraitPrimary; | 241 return blink::WebScreenOrientationLockPortraitPrimary; |
| 139 else if (LowerCaseEqualsASCII(orientation.string(), "portrait-secondary")) | 242 else if (LowerCaseEqualsASCII(orientation.string(), "portrait-secondary")) |
| 140 return blink::WebScreenOrientationLockPortraitSecondary; | 243 return blink::WebScreenOrientationLockPortraitSecondary; |
| 141 else | 244 else { |
| 245 errors_.push_back(GetErrorPrefix() + | |
| 246 "unknown 'orientation' value ignored."); | |
| 142 return blink::WebScreenOrientationLockDefault; | 247 return blink::WebScreenOrientationLockDefault; |
| 248 } | |
| 143 } | 249 } |
| 144 | 250 |
| 145 // Parses the 'src' field of an icon, as defined in: | 251 GURL ManifestParser::ParseIconSrc(const base::DictionaryValue& icon) { |
| 146 // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-src-member-of-an- icon | 252 return ParseURL(icon, "src", manifest_url_); |
| 147 // Returns the parsed GURL if any, an empty GURL if the parsing failed. | |
| 148 GURL ParseIconSrc(const base::DictionaryValue& icon, | |
| 149 const GURL& manifest_url) { | |
| 150 return ParseURL(icon, "src", manifest_url); | |
| 151 } | 253 } |
| 152 | 254 |
| 153 // Parses the 'type' field of an icon, as defined in: | 255 base::NullableString16 ManifestParser::ParseIconType( |
| 154 // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-type-member-of-an -icon | 256 const base::DictionaryValue& icon) { |
| 155 // Returns the parsed string if any, a null string if the parsing failed. | 257 return ParseString(icon, "type", Trim); |
| 156 base::NullableString16 ParseIconType(const base::DictionaryValue& icon) { | |
| 157 return ParseString(icon, "type", Trim); | |
| 158 } | 258 } |
| 159 | 259 |
| 160 // Parses the 'density' field of an icon, as defined in: | 260 double ManifestParser::ParseIconDensity(const base::DictionaryValue& icon) { |
| 161 // http://w3c.github.io/manifest/#dfn-steps-for-processing-a-density-member-of-a n-icon | |
| 162 // Returns the parsed double if any, Manifest::Icon::kDefaultDensity if the | |
| 163 // parsing failed. | |
| 164 double ParseIconDensity(const base::DictionaryValue& icon) { | |
| 165 double density; | 261 double density; |
| 166 if (!icon.GetDouble("density", &density) || density <= 0) | 262 if (!icon.HasKey("density")) |
| 167 return Manifest::Icon::kDefaultDensity; | 263 return Manifest::Icon::kDefaultDensity; |
| 264 | |
| 265 if (!icon.GetDouble("density", &density) || density <= 0) { | |
| 266 errors_.push_back(GetErrorPrefix() + | |
| 267 "icon 'density' ignored, must be float greater than 0."); | |
| 268 return Manifest::Icon::kDefaultDensity; | |
| 269 } | |
| 168 return density; | 270 return density; |
| 169 } | 271 } |
| 170 | 272 |
| 171 // Helper function that returns whether the given |str| is a valid width or | 273 std::vector<gfx::Size> ManifestParser::ParseIconSizes( |
| 172 // height value for an icon sizes per: | 274 const base::DictionaryValue& icon) { |
| 173 // https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes | 275 base::NullableString16 sizes_str = ParseString(icon, "sizes", NoTrim); |
| 174 bool IsValidIconWidthOrHeight(const std::string& str) { | |
| 175 if (str.empty() || str[0] == '0') | |
| 176 return false; | |
| 177 for (size_t i = 0; i < str.size(); ++i) | |
| 178 if (!IsAsciiDigit(str[i])) | |
| 179 return false; | |
| 180 return true; | |
| 181 } | |
| 182 | 276 |
| 183 // Parses the 'sizes' attribute of an icon as described in the HTML spec: | 277 if (sizes_str.is_null()) |
| 184 // https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes | |
| 185 // Return a vector of gfx::Size that contains the valid sizes found. "Any" is | |
| 186 // represented by gfx::Size(0, 0). | |
| 187 // TODO(mlamouri): this is implemented as a separate function because it should | |
| 188 // be refactored with the other icon sizes parsing implementations, see | |
| 189 // http://crbug.com/416477 | |
| 190 std::vector<gfx::Size> ParseIconSizesHTML(const base::string16& sizes_str16) { | |
| 191 if (!base::IsStringASCII(sizes_str16)) | |
| 192 return std::vector<gfx::Size>(); | 278 return std::vector<gfx::Size>(); |
| 193 | 279 |
| 194 std::vector<gfx::Size> sizes; | 280 std::vector<gfx::Size> sizes = ParseIconSizesHTML(sizes_str.string()); |
| 195 std::string sizes_str = | 281 if (sizes.empty()) { |
| 196 base::StringToLowerASCII(base::UTF16ToUTF8(sizes_str16)); | 282 errors_.push_back(GetErrorPrefix() + "found icon with no valid size."); |
| 197 std::vector<std::string> sizes_str_list; | |
| 198 base::SplitStringAlongWhitespace(sizes_str, &sizes_str_list); | |
| 199 | |
| 200 for (size_t i = 0; i < sizes_str_list.size(); ++i) { | |
| 201 std::string& size_str = sizes_str_list[i]; | |
| 202 if (size_str == "any") { | |
| 203 sizes.push_back(gfx::Size(0, 0)); | |
| 204 continue; | |
| 205 } | |
| 206 | |
| 207 // It is expected that [0] => width and [1] => height after the split. | |
| 208 std::vector<std::string> size_list; | |
| 209 base::SplitStringDontTrim(size_str, L'x', &size_list); | |
| 210 if (size_list.size() != 2) | |
| 211 continue; | |
| 212 if (!IsValidIconWidthOrHeight(size_list[0]) || | |
| 213 !IsValidIconWidthOrHeight(size_list[1])) { | |
| 214 continue; | |
| 215 } | |
| 216 | |
| 217 int width, height; | |
| 218 if (!base::StringToInt(size_list[0], &width) || | |
| 219 !base::StringToInt(size_list[1], &height)) { | |
| 220 continue; | |
| 221 } | |
| 222 | |
| 223 sizes.push_back(gfx::Size(width, height)); | |
| 224 } | 283 } |
| 225 | |
| 226 return sizes; | 284 return sizes; |
| 227 } | 285 } |
| 228 | 286 |
| 229 // Parses the 'sizes' field of an icon, as defined in: | 287 std::vector<Manifest::Icon> ManifestParser::ParseIcons( |
| 230 // http://w3c.github.io/manifest/#dfn-steps-for-processing-a-sizes-member-of-an- icon | 288 const base::DictionaryValue& dictionary) { |
| 231 // Returns a vector of gfx::Size with the successfully parsed sizes, if any. An | |
| 232 // empty vector if the field was not present or empty. "Any" is represented by | |
| 233 // gfx::Size(0, 0). | |
| 234 std::vector<gfx::Size> ParseIconSizes(const base::DictionaryValue& icon) { | |
| 235 base::NullableString16 sizes_str = ParseString(icon, "sizes", NoTrim); | |
| 236 | |
| 237 return sizes_str.is_null() ? std::vector<gfx::Size>() | |
| 238 : ParseIconSizesHTML(sizes_str.string()); | |
| 239 } | |
| 240 | |
| 241 // Parses the 'icons' field of a Manifest, as defined in: | |
| 242 // http://w3c.github.io/manifest/#dfn-steps-for-processing-the-icons-member | |
| 243 // Returns a vector of Manifest::Icon with the successfully parsed icons, if | |
| 244 // any. An empty vector if the field was not present or empty. | |
| 245 std::vector<Manifest::Icon> ParseIcons(const base::DictionaryValue& dictionary, | |
| 246 const GURL& manifest_url) { | |
| 247 std::vector<Manifest::Icon> icons; | 289 std::vector<Manifest::Icon> icons; |
| 248 if (!dictionary.HasKey("icons")) | 290 if (!dictionary.HasKey("icons")) |
| 249 return icons; | 291 return icons; |
| 250 | 292 |
| 251 const base::ListValue* icons_list = 0; | 293 const base::ListValue* icons_list = nullptr; |
| 252 if (!dictionary.GetList("icons", &icons_list)) { | 294 if (!dictionary.GetList("icons", &icons_list)) { |
| 253 // TODO(mlamouri): provide a custom message to the developer console about | 295 errors_.push_back(GetErrorPrefix() + |
| 254 // the property being incorrectly set. | 296 "property 'icons' ignored, type array expected."); |
| 255 return icons; | 297 return icons; |
| 256 } | 298 } |
| 257 | 299 |
| 258 for (size_t i = 0; i < icons_list->GetSize(); ++i) { | 300 for (size_t i = 0; i < icons_list->GetSize(); ++i) { |
| 259 const base::DictionaryValue* icon_dictionary = 0; | 301 const base::DictionaryValue* icon_dictionary = nullptr; |
| 260 if (!icons_list->GetDictionary(i, &icon_dictionary)) | 302 if (!icons_list->GetDictionary(i, &icon_dictionary)) |
| 261 continue; | 303 continue; |
| 262 | 304 |
| 263 Manifest::Icon icon; | 305 Manifest::Icon icon; |
| 264 icon.src = ParseIconSrc(*icon_dictionary, manifest_url); | 306 icon.src = ParseIconSrc(*icon_dictionary); |
| 265 // An icon MUST have a valid src. If it does not, it MUST be ignored. | 307 // An icon MUST have a valid src. If it does not, it MUST be ignored. |
| 266 if (!icon.src.is_valid()) | 308 if (!icon.src.is_valid()) |
| 267 continue; | 309 continue; |
| 268 icon.type = ParseIconType(*icon_dictionary); | 310 icon.type = ParseIconType(*icon_dictionary); |
| 269 icon.density = ParseIconDensity(*icon_dictionary); | 311 icon.density = ParseIconDensity(*icon_dictionary); |
| 270 icon.sizes = ParseIconSizes(*icon_dictionary); | 312 icon.sizes = ParseIconSizes(*icon_dictionary); |
| 271 | 313 |
| 272 icons.push_back(icon); | 314 icons.push_back(icon); |
| 273 } | 315 } |
| 274 | 316 |
| 275 return icons; | 317 return icons; |
| 276 } | 318 } |
| 277 | 319 |
| 278 // Parses the 'gcm_sender_id' field of the manifest. | 320 base::NullableString16 ManifestParser::ParseGCMSenderID( |
| 279 // This is a proprietary extension of the Web Manifest specification. | |
| 280 // Returns the parsed string if any, a null string if the parsing failed. | |
| 281 base::NullableString16 ParseGCMSenderID( | |
| 282 const base::DictionaryValue& dictionary) { | 321 const base::DictionaryValue& dictionary) { |
| 283 return ParseString(dictionary, "gcm_sender_id", Trim); | 322 return ParseString(dictionary, "gcm_sender_id", Trim); |
| 284 } | 323 } |
| 285 | 324 |
| 286 } // anonymous namespace | |
| 287 | |
| 288 Manifest ManifestParser::Parse(const base::StringPiece& json, | |
| 289 const GURL& manifest_url, | |
| 290 const GURL& document_url) { | |
| 291 scoped_ptr<base::Value> value(base::JSONReader::Read(json)); | |
| 292 if (!value) { | |
| 293 // TODO(mlamouri): get the JSON parsing error and report it to the developer | |
| 294 // console. | |
| 295 ManifestUmaUtil::ParseFailed(); | |
| 296 return Manifest(); | |
| 297 } | |
| 298 | |
| 299 if (value->GetType() != base::Value::TYPE_DICTIONARY) { | |
| 300 // TODO(mlamouri): provide a custom message to the developer console. | |
| 301 ManifestUmaUtil::ParseFailed(); | |
| 302 return Manifest(); | |
| 303 } | |
| 304 | |
| 305 base::DictionaryValue* dictionary = 0; | |
| 306 value->GetAsDictionary(&dictionary); | |
| 307 if (!dictionary) { | |
| 308 // TODO(mlamouri): provide a custom message to the developer console. | |
| 309 ManifestUmaUtil::ParseFailed(); | |
| 310 return Manifest(); | |
| 311 } | |
| 312 | |
| 313 Manifest manifest; | |
| 314 | |
| 315 manifest.name = ParseName(*dictionary); | |
| 316 manifest.short_name = ParseShortName(*dictionary); | |
| 317 manifest.start_url = ParseStartURL(*dictionary, manifest_url, document_url); | |
| 318 manifest.display = ParseDisplay(*dictionary); | |
| 319 manifest.orientation = ParseOrientation(*dictionary); | |
| 320 manifest.icons = ParseIcons(*dictionary, manifest_url); | |
| 321 manifest.gcm_sender_id = ParseGCMSenderID(*dictionary); | |
| 322 | |
| 323 ManifestUmaUtil::ParseSucceeded(manifest); | |
| 324 | |
| 325 return manifest; | |
| 326 } | |
| 327 | |
| 328 } // namespace content | 325 } // namespace content |
| OLD | NEW |