Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(204)

Side by Side Diff: chrome/common/web_apps.cc

Issue 4979003: Implement web app definition parsing. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: cleanup Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 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/web_apps.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "app/l10n_util.h"
11 #include "app/resource_bundle.h"
12 #include "base/json/json_reader.h"
13 #include "base/string16.h"
14 #include "base/string_number_conversions.h"
15 #include "base/string_split.h"
16 #include "base/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/common/json_schema_validator.h"
19 #include "gfx/size.h"
20 #include "googleurl/src/gurl.h"
21 #include "grit/common_resources.h"
22 #include "grit/generated_resources.h"
23 #include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
24 #include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
25 #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
26 #include "third_party/WebKit/WebKit/chromium/public/WebNode.h"
27 #include "third_party/WebKit/WebKit/chromium/public/WebNodeList.h"
28 #include "third_party/WebKit/WebKit/chromium/public/WebString.h"
29 #include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
30 #include "webkit/glue/dom_operations.h"
31
32 using WebKit::WebDocument;
33 using WebKit::WebElement;
34 using WebKit::WebFrame;
35 using WebKit::WebNode;
36 using WebKit::WebNodeList;
37 using WebKit::WebString;
38
39 namespace {
40
41 // Sizes a single size (the width or height) from a 'sizes' attribute. A size
42 // matches must match the following regex: [1-9][0-9]*.
43 static int ParseSingleIconSize(const string16& text) {
44 // Size must not start with 0, and be between 0 and 9.
45 if (text.empty() || !(text[0] >= L'1' && text[0] <= L'9'))
46 return 0;
Erik does not do reviews 2010/11/15 19:45:36 the header indicates a size < 0 if it's invalid
Aaron Boodman 2010/11/15 19:52:34 I forgot to mention this in the review, but ParseS
Aaron Boodman 2010/11/16 03:44:45 Not my code, but fixed.
47 // Make sure all chars are from 0-9.
Erik does not do reviews 2010/11/15 19:45:36 nit: newline above comment
Aaron Boodman 2010/11/16 03:44:45 Done.
48 for (size_t i = 1; i < text.length(); ++i) {
49 if (!(text[i] >= L'0' && text[i] <= L'9'))
50 return 0;
51 }
52 int output;
53 if (!base::StringToInt(text, &output))
54 return 0;
55 return output;
Erik does not do reviews 2010/11/15 19:45:36 do we want a min/max value for icon sizes?
Aaron Boodman 2010/11/16 03:44:45 <shrug> -- this code is only used by the create ap
56 }
57
58 void AddInstallIcon(const WebElement& link,
59 std::vector<WebApplicationInfo::IconInfo>* icons) {
60 WebString href = link.getAttribute("href");
61 if (href.isNull() || href.isEmpty())
62 return;
63
64 // Get complete url.
65 GURL url = link.document().completeURL(href);
66 if (!url.is_valid())
67 return;
68
69 if (!link.hasAttribute("sizes"))
70 return;
71
72 bool is_any = false;
73 std::vector<gfx::Size> icon_sizes;
74 if (!ParseIconSizes(link.getAttribute("sizes"), &icon_sizes, &is_any) ||
75 is_any ||
76 icon_sizes.size() != 1) {
Erik does not do reviews 2010/11/15 19:45:36 so this means we're explicitly not allowing things
Aaron Boodman 2010/11/16 03:44:45 Also only used by create application shortcuts fea
77 return;
78 }
79 WebApplicationInfo::IconInfo icon_info;
80 icon_info.width = icon_sizes[0].width();
81 icon_info.height = icon_sizes[0].height();
82 icon_info.url = url;
83 icons->push_back(icon_info);
84 }
85
86 }
87
88 const char WebApplicationInfo::kInvalidDefinitionURL[] =
89 "Invalid application definition URL. Must be a valid relative URL or "
90 "an absolute URL with the same origin as the document.";
91 const char WebApplicationInfo::kInvalidLaunchURL[] =
92 "Invalid value for property 'launch_url'. Must be a valid relative URL or "
93 "an absolute URL with the same origin as the application definition.";
94 const char WebApplicationInfo::kInvalidURL[] =
95 "Invalid value for property 'urls[*]'. Must be a valid relative URL or "
96 "an absolute URL with the same origin as the application definition.";
97 const char WebApplicationInfo::kInvalidIconURL[] =
98 "Invalid value for property 'icons.*'. Must be a valid relative URL or "
99 "an absolute URL with the same origin as the application definition.";
100
101 WebApplicationInfo::WebApplicationInfo() {
102 }
103
104 WebApplicationInfo::~WebApplicationInfo() {
105 }
106
107 gfx::Size ParseIconSize(const string16& text) {
108 std::vector<string16> sizes;
109 base::SplitStringDontTrim(text, L'x', &sizes);
110 if (sizes.size() != 2)
111 return gfx::Size();
112
113 return gfx::Size(ParseSingleIconSize(sizes[0]),
114 ParseSingleIconSize(sizes[1]));
115 }
116
117 bool ParseIconSizes(const string16& text,
118 std::vector<gfx::Size>* sizes,
119 bool* is_any) {
120 *is_any = false;
121 std::vector<string16> size_strings;
122 SplitStringAlongWhitespace(text, &size_strings);
123 for (size_t i = 0; i < size_strings.size(); ++i) {
124 if (EqualsASCII(size_strings[i], "any")) {
125 *is_any = true;
126 } else {
127 gfx::Size size = ParseIconSize(size_strings[i]);
128 if (size.width() <= 0 || size.height() <= 0)
129 return false; // Bogus size.
130 sizes->push_back(size);
131 }
132 }
133 if (*is_any && !sizes->empty()) {
134 // If is_any is true, it must occur by itself.
Erik does not do reviews 2010/11/15 19:45:36 Without displaying error messages to the developer
Aaron Boodman 2010/11/16 03:44:45 This is only used by the create application shortc
135 return false;
136 }
137 return (*is_any || !sizes->empty());
138 }
139
140 bool ParseWebAppFromWebDocument(WebFrame* frame,
141 WebApplicationInfo* app_info,
142 string16* error) {
143 WebDocument document = frame->document();
144 if (document.isNull())
145 return true;
146
147 WebElement head = document.head();
148 if (head.isNull())
149 return true;
150
151 GURL frame_url = frame->url();
152 WebNodeList children = head.childNodes();
153 for (unsigned i = 0; i < children.length(); ++i) {
154 WebNode child = children.item(i);
155 if (!child.isElementNode())
156 continue;
157 WebElement elem = child.to<WebElement>();
158
159 if (elem.hasTagName("link")) {
160 std::string rel = elem.getAttribute("rel").utf8();
161 // "rel" attribute may use either "icon" or "shortcut icon".
162 // see also
163 // <http://en.wikipedia.org/wiki/Favicon>
164 // <http://dev.w3.org/html5/spec/Overview.html#rel-icon>
165 if (LowerCaseEqualsASCII(rel, "icon") ||
166 LowerCaseEqualsASCII(rel, "shortcut icon")) {
167 AddInstallIcon(elem, &app_info->icons);
168 } else if (LowerCaseEqualsASCII(rel, "chrome-application-definition")) {
169 std::string definition_url_string(elem.getAttribute("href").utf8());
170 GURL definition_url;
171 if (!(definition_url =
172 frame_url.Resolve(definition_url_string)).is_valid() ||
173 definition_url.GetOrigin() != frame_url.GetOrigin()) {
174 *error = UTF8ToUTF16(WebApplicationInfo::kInvalidDefinitionURL);
175 return false;
176 }
177
178 // If there is a definition file, all attributes come from it.
179 *app_info = WebApplicationInfo();
180 app_info->manifest_url = definition_url;
181 return true;
182 }
183 } else if (elem.hasTagName("meta") && elem.hasAttribute("name")) {
184 std::string name = elem.getAttribute("name").utf8();
185 WebString content = elem.getAttribute("content");
186 if (name == "application-name") {
187 app_info->title = content;
188 } else if (name == "description") {
189 app_info->description = content;
190 } else if (name == "application-url") {
191 std::string url = content.utf8();
192 app_info->app_url = frame_url.is_valid() ?
193 frame_url.Resolve(url) : GURL(url);
194 if (!app_info->app_url.is_valid())
195 app_info->app_url = GURL();
196 }
197 }
198 }
199
200 return true;
201 }
202
203 bool ParseWebAppFromDefinitionFile(const DictionaryValue& definition,
204 WebApplicationInfo* web_app,
205 string16* error) {
206 CHECK(web_app->manifest_url.is_valid());
207
208 int error_code = 0;
209 std::string error_message;
210 scoped_ptr<Value> schema(
211 base::JSONReader::ReadAndReturnError(
212 ResourceBundle::GetSharedInstance().GetRawDataResource(
213 IDR_WEB_APP_SCHEMA).as_string(),
214 false, // disallow trailing comma
215 &error_code,
216 &error_message));
217 CHECK(schema.get())
218 << "Error parsing JSON schema: " << error_code << ": " << error_message;
219 CHECK(schema->IsType(Value::TYPE_DICTIONARY))
220 << "schema root must be dictionary.";
221
222 JSONSchemaValidator validator(static_cast<DictionaryValue*>(schema.get()));
223
224 // We allow extra properties in the schema for easy compat with other systems,
225 // and for forward compat with ourselves.
226 validator.set_default_allow_additional_properties(true);
227
228 if (!validator.Validate(const_cast<DictionaryValue*>(&definition))) {
229 *error = UTF8ToUTF16(
230 validator.errors()[0].path + ": " + validator.errors()[0].message);
231 return false;
232 }
233
234 // Parse launch URL. It must be a valid URL in the same origin as the
235 // manifest.
236 std::string app_url_string;
237 GURL app_url;
238 CHECK(definition.GetString("launch_url", &app_url_string));
239 if (!(app_url = web_app->manifest_url.Resolve(app_url_string)).is_valid() ||
240 app_url.GetOrigin() != web_app->manifest_url.GetOrigin()) {
241 *error = UTF8ToUTF16(WebApplicationInfo::kInvalidLaunchURL);
242 return false;
243 }
244
245 // Parse out the privileges if present.
246 std::vector<std::string> privileges;
247 ListValue* privileges_value = NULL;
248 if (definition.GetList("privileges", &privileges_value)) {
249 for (size_t i = 0; i < privileges_value->GetSize(); ++i) {
250 std::string privilege;
251 CHECK(privileges_value->GetString(i, &privilege));
Erik does not do reviews 2010/11/15 19:45:36 isn't this covered by the schema validation?
Aaron Boodman 2010/11/16 03:44:45 The CHECK is just a quick assertion. I prefer that
252 privileges.push_back(privilege);
253 }
254 }
255
256 // Parse out the URLs if present.
257 std::vector<GURL> urls;
258 ListValue* urls_value = NULL;
259 if (definition.GetList("urls", &urls_value)) {
260 for (size_t i = 0; i < urls_value->GetSize(); ++i) {
261 std::string url_string;
262 GURL url;
263 CHECK(urls_value->GetString(i, &url_string));
Erik does not do reviews 2010/11/15 19:45:36 again, is this redundant?
264 if (!(url = web_app->manifest_url.Resolve(url_string)).is_valid() ||
265 url.GetOrigin() != web_app->manifest_url.GetOrigin()) {
266 *error = UTF8ToUTF16(
267 JSONSchemaValidator::FormatErrorMessage(
268 WebApplicationInfo::kInvalidURL, base::UintToString(i)));
269 return false;
270 }
271 urls.push_back(url);
272 }
273 }
274
275 // Parse out the icons if present.
276 std::vector<WebApplicationInfo::IconInfo> icons;
277 DictionaryValue* icons_value = NULL;
278 if (definition.GetDictionary("icons", &icons_value)) {
279 for (DictionaryValue::key_iterator iter = icons_value->begin_keys();
280 iter != icons_value->end_keys(); ++iter) {
281 // Ignore unknown properties. Better for forward compat.
282 int size = 0;
283 if (!base::StringToInt(*iter, &size) || size < 0 || size > 128)
Erik does not do reviews 2010/11/15 19:45:36 128 seems too small as a max. 512 is pretty stand
Aaron Boodman 2010/11/16 03:44:45 The extension system doesn't currently support any
Erik does not do reviews 2010/11/16 23:43:02 I forgot that we had that hard-coded. I'll file a
284 continue;
285
286 std::string icon_url_string;
287 GURL icon_url;
288 if (!icons_value->GetString(*iter, &icon_url_string) ||
289 !(icon_url = web_app->manifest_url.Resolve(
290 icon_url_string)).is_valid()) {
291 *error = UTF8ToUTF16(
292 JSONSchemaValidator::FormatErrorMessage(
293 WebApplicationInfo::kInvalidIconURL, base::IntToString(size)));
294 return false;
295 }
296
297 WebApplicationInfo::IconInfo icon;
298 icon.url = icon_url;
299 icon.width = size;
300 icon.height = size;
301
302 icons.push_back(icon);
303 }
304 }
305
306 CHECK(definition.GetString("name", &web_app->title));
307 definition.GetString("description", &web_app->description);
308 definition.GetString("launch_container", &web_app->launch_container);
309 web_app->app_url = app_url;
310 web_app->urls = urls;
311 web_app->privileges = privileges;
312 web_app->icons = icons;
313
314 return true;
315
316 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698