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

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: fix nacl64 build 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 -1;
47
48 // Make sure all chars are from 0-9.
49 for (size_t i = 1; i < text.length(); ++i) {
50 if (!(text[i] >= L'0' && text[i] <= L'9'))
51 return -1;
52 }
53 int output;
54 if (!base::StringToInt(text, &output))
55 return -1;
56 return output;
57 }
58
59 void AddInstallIcon(const WebElement& link,
60 std::vector<WebApplicationInfo::IconInfo>* icons) {
61 WebString href = link.getAttribute("href");
62 if (href.isNull() || href.isEmpty())
63 return;
64
65 // Get complete url.
66 GURL url = link.document().completeURL(href);
67 if (!url.is_valid())
68 return;
69
70 if (!link.hasAttribute("sizes"))
71 return;
72
73 bool is_any = false;
74 std::vector<gfx::Size> icon_sizes;
75 if (!ParseIconSizes(link.getAttribute("sizes"), &icon_sizes, &is_any) ||
76 is_any ||
77 icon_sizes.size() != 1) {
78 return;
79 }
80 WebApplicationInfo::IconInfo icon_info;
81 icon_info.width = icon_sizes[0].width();
82 icon_info.height = icon_sizes[0].height();
83 icon_info.url = url;
84 icons->push_back(icon_info);
85 }
86
87 }
88
89 const char WebApplicationInfo::kInvalidDefinitionURL[] =
90 "Invalid application definition URL. Must be a valid relative URL or "
91 "an absolute URL with the same origin as the document.";
92 const char WebApplicationInfo::kInvalidLaunchURL[] =
93 "Invalid value for property 'launch_url'. Must be a valid relative URL or "
94 "an absolute URL with the same origin as the application definition.";
95 const char WebApplicationInfo::kInvalidURL[] =
96 "Invalid value for property 'urls[*]'. Must be a valid relative URL or "
97 "an absolute URL with the same origin as the application definition.";
98 const char WebApplicationInfo::kInvalidIconURL[] =
99 "Invalid value for property 'icons.*'. Must be a valid relative URL or "
100 "an absolute URL with the same origin as the application definition.";
101
102 WebApplicationInfo::WebApplicationInfo() {
103 }
104
105 WebApplicationInfo::~WebApplicationInfo() {
106 }
107
108 gfx::Size ParseIconSize(const string16& text) {
109 std::vector<string16> sizes;
110 base::SplitStringDontTrim(text, L'x', &sizes);
111 if (sizes.size() != 2)
112 return gfx::Size();
113
114 return gfx::Size(ParseSingleIconSize(sizes[0]),
115 ParseSingleIconSize(sizes[1]));
116 }
117
118 bool ParseIconSizes(const string16& text,
119 std::vector<gfx::Size>* sizes,
120 bool* is_any) {
121 *is_any = false;
122 std::vector<string16> size_strings;
123 SplitStringAlongWhitespace(text, &size_strings);
124 for (size_t i = 0; i < size_strings.size(); ++i) {
125 if (EqualsASCII(size_strings[i], "any")) {
126 *is_any = true;
127 } else {
128 gfx::Size size = ParseIconSize(size_strings[i]);
129 if (size.width() <= 0 || size.height() <= 0)
130 return false; // Bogus size.
131 sizes->push_back(size);
132 }
133 }
134 if (*is_any && !sizes->empty()) {
135 // If is_any is true, it must occur by itself.
136 return false;
137 }
138 return (*is_any || !sizes->empty());
139 }
140
141 bool ParseWebAppFromWebDocument(WebFrame* frame,
142 WebApplicationInfo* app_info,
143 string16* error) {
144 WebDocument document = frame->document();
145 if (document.isNull())
146 return true;
147
148 WebElement head = document.head();
149 if (head.isNull())
150 return true;
151
152 GURL frame_url = frame->url();
153 WebNodeList children = head.childNodes();
154 for (unsigned i = 0; i < children.length(); ++i) {
155 WebNode child = children.item(i);
156 if (!child.isElementNode())
157 continue;
158 WebElement elem = child.to<WebElement>();
159
160 if (elem.hasTagName("link")) {
161 std::string rel = elem.getAttribute("rel").utf8();
162 // "rel" attribute may use either "icon" or "shortcut icon".
163 // see also
164 // <http://en.wikipedia.org/wiki/Favicon>
165 // <http://dev.w3.org/html5/spec/Overview.html#rel-icon>
166 if (LowerCaseEqualsASCII(rel, "icon") ||
167 LowerCaseEqualsASCII(rel, "shortcut icon")) {
168 AddInstallIcon(elem, &app_info->icons);
169 } else if (LowerCaseEqualsASCII(rel, "chrome-application-definition")) {
170 std::string definition_url_string(elem.getAttribute("href").utf8());
171 GURL definition_url;
172 if (!(definition_url =
173 frame_url.Resolve(definition_url_string)).is_valid() ||
174 definition_url.GetOrigin() != frame_url.GetOrigin()) {
175 *error = UTF8ToUTF16(WebApplicationInfo::kInvalidDefinitionURL);
176 return false;
177 }
178
179 // If there is a definition file, all attributes come from it.
180 *app_info = WebApplicationInfo();
181 app_info->manifest_url = definition_url;
182 return true;
183 }
184 } else if (elem.hasTagName("meta") && elem.hasAttribute("name")) {
185 std::string name = elem.getAttribute("name").utf8();
186 WebString content = elem.getAttribute("content");
187 if (name == "application-name") {
188 app_info->title = content;
189 } else if (name == "description") {
190 app_info->description = content;
191 } else if (name == "application-url") {
192 std::string url = content.utf8();
193 app_info->app_url = frame_url.is_valid() ?
194 frame_url.Resolve(url) : GURL(url);
195 if (!app_info->app_url.is_valid())
196 app_info->app_url = GURL();
197 }
198 }
199 }
200
201 return true;
202 }
203
204 bool ParseWebAppFromDefinitionFile(const DictionaryValue& definition,
205 WebApplicationInfo* web_app,
206 string16* error) {
207 CHECK(web_app->manifest_url.is_valid());
208
209 int error_code = 0;
210 std::string error_message;
211 scoped_ptr<Value> schema(
212 base::JSONReader::ReadAndReturnError(
213 ResourceBundle::GetSharedInstance().GetRawDataResource(
214 IDR_WEB_APP_SCHEMA).as_string(),
215 false, // disallow trailing comma
216 &error_code,
217 &error_message));
218 CHECK(schema.get())
219 << "Error parsing JSON schema: " << error_code << ": " << error_message;
220 CHECK(schema->IsType(Value::TYPE_DICTIONARY))
221 << "schema root must be dictionary.";
222
223 JSONSchemaValidator validator(static_cast<DictionaryValue*>(schema.get()));
224
225 // We allow extra properties in the schema for easy compat with other systems,
226 // and for forward compat with ourselves.
227 validator.set_default_allow_additional_properties(true);
228
229 if (!validator.Validate(const_cast<DictionaryValue*>(&definition))) {
230 *error = UTF8ToUTF16(
231 validator.errors()[0].path + ": " + validator.errors()[0].message);
232 return false;
233 }
234
235 // Parse launch URL. It must be a valid URL in the same origin as the
236 // manifest.
237 std::string app_url_string;
238 GURL app_url;
239 CHECK(definition.GetString("launch_url", &app_url_string));
240 if (!(app_url = web_app->manifest_url.Resolve(app_url_string)).is_valid() ||
241 app_url.GetOrigin() != web_app->manifest_url.GetOrigin()) {
242 *error = UTF8ToUTF16(WebApplicationInfo::kInvalidLaunchURL);
243 return false;
244 }
245
246 // Parse out the permissions if present.
247 std::vector<std::string> permissions;
248 ListValue* permissions_value = NULL;
249 if (definition.GetList("permissions", &permissions_value)) {
250 for (size_t i = 0; i < permissions_value->GetSize(); ++i) {
251 std::string permission;
252 CHECK(permissions_value->GetString(i, &permission));
253 permissions.push_back(permission);
254 }
255 }
256
257 // Parse out the URLs if present.
258 std::vector<GURL> urls;
259 ListValue* urls_value = NULL;
260 if (definition.GetList("urls", &urls_value)) {
261 for (size_t i = 0; i < urls_value->GetSize(); ++i) {
262 std::string url_string;
263 GURL url;
264 CHECK(urls_value->GetString(i, &url_string));
265 if (!(url = web_app->manifest_url.Resolve(url_string)).is_valid() ||
266 url.GetOrigin() != web_app->manifest_url.GetOrigin()) {
267 *error = UTF8ToUTF16(
268 JSONSchemaValidator::FormatErrorMessage(
269 WebApplicationInfo::kInvalidURL, base::Uint64ToString(i)));
270 return false;
271 }
272 urls.push_back(url);
273 }
274 }
275
276 // Parse out the icons if present.
277 std::vector<WebApplicationInfo::IconInfo> icons;
278 DictionaryValue* icons_value = NULL;
279 if (definition.GetDictionary("icons", &icons_value)) {
280 for (DictionaryValue::key_iterator iter = icons_value->begin_keys();
281 iter != icons_value->end_keys(); ++iter) {
282 // Ignore unknown properties. Better for forward compat.
283 int size = 0;
284 if (!base::StringToInt(*iter, &size) || size < 0 || size > 128)
285 continue;
286
287 std::string icon_url_string;
288 GURL icon_url;
289 if (!icons_value->GetString(*iter, &icon_url_string) ||
290 !(icon_url = web_app->manifest_url.Resolve(
291 icon_url_string)).is_valid()) {
292 *error = UTF8ToUTF16(
293 JSONSchemaValidator::FormatErrorMessage(
294 WebApplicationInfo::kInvalidIconURL, base::IntToString(size)));
295 return false;
296 }
297
298 WebApplicationInfo::IconInfo icon;
299 icon.url = icon_url;
300 icon.width = size;
301 icon.height = size;
302
303 icons.push_back(icon);
304 }
305 }
306
307 CHECK(definition.GetString("name", &web_app->title));
308 definition.GetString("description", &web_app->description);
309 definition.GetString("launch_container", &web_app->launch_container);
310 web_app->app_url = app_url;
311 web_app->urls = urls;
312 web_app->permissions = permissions;
313 web_app->icons = icons;
314
315 return true;
316
317 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698