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/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/common/web_application_info.h"
20 #include "third_party/WebKit/public/platform/WebString.h"
21 #include "third_party/WebKit/public/platform/WebURL.h"
22 #include "third_party/WebKit/public/web/WebDocument.h"
23 #include "third_party/WebKit/public/web/WebElement.h"
24 #include "third_party/WebKit/public/web/WebFrame.h"
25 #include "third_party/WebKit/public/web/WebNode.h"
26 #include "third_party/WebKit/public/web/WebNodeList.h"
27 #include "ui/gfx/geometry/size.h"
30 using blink::WebDocument
;
31 using blink::WebElement
;
32 using blink::WebFrame
;
34 using blink::WebNodeList
;
35 using blink::WebString
;
40 // Sizes a single size (the width or height) from a 'sizes' attribute. A size
41 // matches must match the following regex: [1-9][0-9]*.
42 int ParseSingleIconSize(const base::string16
& text
) {
43 // Size must not start with 0, and be between 0 and 9.
44 if (text
.empty() || !(text
[0] >= L
'1' && text
[0] <= L
'9'))
47 // Make sure all chars are from 0-9.
48 for (size_t i
= 1; i
< text
.length(); ++i
) {
49 if (!(text
[i
] >= L
'0' && text
[i
] <= L
'9'))
53 if (!base::StringToInt(text
, &output
))
58 // Parses an icon size. An icon size must match the following regex:
59 // [1-9][0-9]*x[1-9][0-9]*.
60 // If the input couldn't be parsed, a size with a width/height == 0 is returned.
61 gfx::Size
ParseIconSize(const base::string16
& text
) {
62 std::vector
<base::string16
> sizes
;
63 base::SplitStringDontTrim(text
, L
'x', &sizes
);
64 if (sizes
.size() != 2)
67 return gfx::Size(ParseSingleIconSize(sizes
[0]),
68 ParseSingleIconSize(sizes
[1]));
71 void AddInstallIcon(const WebElement
& link
,
72 std::vector
<WebApplicationInfo::IconInfo
>* icons
) {
73 WebString href
= link
.getAttribute("href");
74 if (href
.isNull() || href
.isEmpty())
78 GURL url
= link
.document().completeURL(href
);
82 WebApplicationInfo::IconInfo icon_info
;
84 std::vector
<gfx::Size
> icon_sizes
;
85 if (link
.hasAttribute("sizes") &&
86 ParseIconSizes(link
.getAttribute("sizes"), &icon_sizes
, &is_any
) &&
88 icon_sizes
.size() == 1) {
89 icon_info
.width
= icon_sizes
[0].width();
90 icon_info
.height
= icon_sizes
[0].height();
93 icons
->push_back(icon_info
);
98 bool ParseIconSizes(const base::string16
& text
,
99 std::vector
<gfx::Size
>* sizes
,
102 std::vector
<base::string16
> size_strings
;
103 base::SplitStringAlongWhitespace(text
, &size_strings
);
104 for (size_t i
= 0; i
< size_strings
.size(); ++i
) {
105 if (EqualsASCII(size_strings
[i
], "any")) {
108 gfx::Size size
= ParseIconSize(size_strings
[i
]);
109 if (size
.width() <= 0 || size
.height() <= 0)
110 return false; // Bogus size.
111 sizes
->push_back(size
);
114 if (*is_any
&& !sizes
->empty()) {
115 // If is_any is true, it must occur by itself.
118 return (*is_any
|| !sizes
->empty());
121 void ParseWebAppFromWebDocument(WebFrame
* frame
,
122 WebApplicationInfo
* app_info
) {
123 WebDocument document
= frame
->document();
124 if (document
.isNull())
127 WebElement head
= document
.head();
131 GURL document_url
= document
.url();
132 WebNodeList children
= head
.childNodes();
133 for (unsigned i
= 0; i
< children
.length(); ++i
) {
134 WebNode child
= children
.item(i
);
135 if (!child
.isElementNode())
137 WebElement elem
= child
.to
<WebElement
>();
139 if (elem
.hasHTMLTagName("link")) {
140 std::string rel
= elem
.getAttribute("rel").utf8();
141 // "rel" attribute may use either "icon" or "shortcut icon".
143 // <http://en.wikipedia.org/wiki/Favicon>
144 // <http://dev.w3.org/html5/spec/Overview.html#rel-icon>
146 // Streamlined Hosted Apps also support "apple-touch-icon" and
147 // "apple-touch-icon-precomposed".
148 if (LowerCaseEqualsASCII(rel
, "icon") ||
149 LowerCaseEqualsASCII(rel
, "shortcut icon") ||
150 (base::CommandLine::ForCurrentProcess()->HasSwitch(
151 switches::kEnableNewBookmarkApps
) &&
152 (LowerCaseEqualsASCII(rel
, "apple-touch-icon") ||
153 LowerCaseEqualsASCII(rel
, "apple-touch-icon-precomposed")))) {
154 AddInstallIcon(elem
, &app_info
->icons
);
156 } else if (elem
.hasHTMLTagName("meta") && elem
.hasAttribute("name")) {
157 std::string name
= elem
.getAttribute("name").utf8();
158 WebString content
= elem
.getAttribute("content");
159 if (name
== "application-name") {
160 app_info
->title
= content
;
161 } else if (name
== "description") {
162 app_info
->description
= content
;
163 } else if (name
== "application-url") {
164 std::string url
= content
.utf8();
165 app_info
->app_url
= document_url
.is_valid() ?
166 document_url
.Resolve(url
) : GURL(url
);
167 if (!app_info
->app_url
.is_valid())
168 app_info
->app_url
= GURL();
169 } else if (name
== "mobile-web-app-capable" &&
170 LowerCaseEqualsASCII(content
, "yes")) {
171 app_info
->mobile_capable
= WebApplicationInfo::MOBILE_CAPABLE
;
172 } else if (name
== "apple-mobile-web-app-capable" &&
173 LowerCaseEqualsASCII(content
, "yes") &&
174 app_info
->mobile_capable
==
175 WebApplicationInfo::MOBILE_CAPABLE_UNSPECIFIED
) {
176 app_info
->mobile_capable
= WebApplicationInfo::MOBILE_CAPABLE_APPLE
;
182 } // namespace web_apps