1 // Copyright (c) 2012 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 "chrome/renderer/web_apps.h"
10 #include "base/command_line.h"
11 #include "base/json/json_reader.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/web_application_info.h"
19 #include "grit/common_resources.h"
20 #include "grit/generated_resources.h"
21 #include "third_party/WebKit/public/platform/WebString.h"
22 #include "third_party/WebKit/public/platform/WebURL.h"
23 #include "third_party/WebKit/public/web/WebDocument.h"
24 #include "third_party/WebKit/public/web/WebElement.h"
25 #include "third_party/WebKit/public/web/WebFrame.h"
26 #include "third_party/WebKit/public/web/WebNode.h"
27 #include "third_party/WebKit/public/web/WebNodeList.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/base/resource/resource_bundle.h"
30 #include "ui/gfx/size.h"
33 using blink::WebDocument
;
34 using blink::WebElement
;
35 using blink::WebFrame
;
37 using blink::WebNodeList
;
38 using blink::WebString
;
43 // Sizes a single size (the width or height) from a 'sizes' attribute. A size
44 // matches must match the following regex: [1-9][0-9]*.
45 int ParseSingleIconSize(const base::string16
& text
) {
46 // Size must not start with 0, and be between 0 and 9.
47 if (text
.empty() || !(text
[0] >= L
'1' && text
[0] <= L
'9'))
50 // Make sure all chars are from 0-9.
51 for (size_t i
= 1; i
< text
.length(); ++i
) {
52 if (!(text
[i
] >= L
'0' && text
[i
] <= L
'9'))
56 if (!base::StringToInt(text
, &output
))
61 // Parses an icon size. An icon size must match the following regex:
62 // [1-9][0-9]*x[1-9][0-9]*.
63 // If the input couldn't be parsed, a size with a width/height == 0 is returned.
64 gfx::Size
ParseIconSize(const base::string16
& text
) {
65 std::vector
<base::string16
> sizes
;
66 base::SplitStringDontTrim(text
, L
'x', &sizes
);
67 if (sizes
.size() != 2)
70 return gfx::Size(ParseSingleIconSize(sizes
[0]),
71 ParseSingleIconSize(sizes
[1]));
74 void AddInstallIcon(const WebElement
& link
,
75 std::vector
<WebApplicationInfo::IconInfo
>* icons
) {
76 WebString href
= link
.getAttribute("href");
77 if (href
.isNull() || href
.isEmpty())
81 GURL url
= link
.document().completeURL(href
);
85 WebApplicationInfo::IconInfo icon_info
;
87 std::vector
<gfx::Size
> icon_sizes
;
88 if (link
.hasAttribute("sizes") &&
89 ParseIconSizes(link
.getAttribute("sizes"), &icon_sizes
, &is_any
) &&
91 icon_sizes
.size() == 1) {
92 icon_info
.width
= icon_sizes
[0].width();
93 icon_info
.height
= icon_sizes
[0].height();
96 icons
->push_back(icon_info
);
101 bool ParseIconSizes(const base::string16
& text
,
102 std::vector
<gfx::Size
>* sizes
,
105 std::vector
<base::string16
> size_strings
;
106 base::SplitStringAlongWhitespace(text
, &size_strings
);
107 for (size_t i
= 0; i
< size_strings
.size(); ++i
) {
108 if (EqualsASCII(size_strings
[i
], "any")) {
111 gfx::Size size
= ParseIconSize(size_strings
[i
]);
112 if (size
.width() <= 0 || size
.height() <= 0)
113 return false; // Bogus size.
114 sizes
->push_back(size
);
117 if (*is_any
&& !sizes
->empty()) {
118 // If is_any is true, it must occur by itself.
121 return (*is_any
|| !sizes
->empty());
124 bool ParseWebAppFromWebDocument(WebFrame
* frame
,
125 WebApplicationInfo
* app_info
,
126 base::string16
* error
) {
127 WebDocument document
= frame
->document();
128 if (document
.isNull())
131 WebElement head
= document
.head();
135 GURL document_url
= document
.url();
136 WebNodeList children
= head
.childNodes();
137 for (unsigned i
= 0; i
< children
.length(); ++i
) {
138 WebNode child
= children
.item(i
);
139 if (!child
.isElementNode())
141 WebElement elem
= child
.to
<WebElement
>();
143 if (elem
.hasHTMLTagName("link")) {
144 std::string rel
= elem
.getAttribute("rel").utf8();
145 // "rel" attribute may use either "icon" or "shortcut icon".
147 // <http://en.wikipedia.org/wiki/Favicon>
148 // <http://dev.w3.org/html5/spec/Overview.html#rel-icon>
150 // Streamlined Hosted Apps also support "apple-touch-icon" and
151 // "apple-touch-icon-precomposed".
152 if (LowerCaseEqualsASCII(rel
, "icon") ||
153 LowerCaseEqualsASCII(rel
, "shortcut icon") ||
154 (CommandLine::ForCurrentProcess()->
155 HasSwitch(switches::kEnableStreamlinedHostedApps
) &&
156 (LowerCaseEqualsASCII(rel
, "apple-touch-icon") ||
157 LowerCaseEqualsASCII(rel
, "apple-touch-icon-precomposed")))) {
158 AddInstallIcon(elem
, &app_info
->icons
);
160 } else if (elem
.hasHTMLTagName("meta") && elem
.hasAttribute("name")) {
161 std::string name
= elem
.getAttribute("name").utf8();
162 WebString content
= elem
.getAttribute("content");
163 if (name
== "application-name") {
164 app_info
->title
= content
;
165 } else if (name
== "description") {
166 app_info
->description
= content
;
167 } else if (name
== "application-url") {
168 std::string url
= content
.utf8();
169 app_info
->app_url
= document_url
.is_valid() ?
170 document_url
.Resolve(url
) : GURL(url
);
171 if (!app_info
->app_url
.is_valid())
172 app_info
->app_url
= GURL();
180 } // namespace web_apps