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::StringPiece16
& 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::StringPiece16
> sizes
= base::SplitStringPiece(
63 text
, base::string16(1, 'x'),
64 base::KEEP_WHITESPACE
, base::SPLIT_WANT_ALL
);
65 if (sizes
.size() != 2)
68 return gfx::Size(ParseSingleIconSize(sizes
[0]),
69 ParseSingleIconSize(sizes
[1]));
72 void AddInstallIcon(const WebElement
& link
,
73 std::vector
<WebApplicationInfo::IconInfo
>* icons
) {
74 WebString href
= link
.getAttribute("href");
75 if (href
.isNull() || href
.isEmpty())
79 GURL url
= link
.document().completeURL(href
);
83 WebApplicationInfo::IconInfo icon_info
;
85 std::vector
<gfx::Size
> icon_sizes
;
86 if (link
.hasAttribute("sizes") &&
87 ParseIconSizes(link
.getAttribute("sizes"), &icon_sizes
, &is_any
) &&
89 icon_sizes
.size() == 1) {
90 icon_info
.width
= icon_sizes
[0].width();
91 icon_info
.height
= icon_sizes
[0].height();
94 icons
->push_back(icon_info
);
99 bool ParseIconSizes(const base::string16
& text
,
100 std::vector
<gfx::Size
>* sizes
,
103 std::vector
<base::string16
> size_strings
= base::SplitString(
104 text
, base::kWhitespaceASCIIAs16
,
105 base::KEEP_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
);
106 for (size_t i
= 0; i
< size_strings
.size(); ++i
) {
107 if (base::EqualsASCII(size_strings
[i
], "any")) {
110 gfx::Size size
= ParseIconSize(size_strings
[i
]);
111 if (size
.width() <= 0 || size
.height() <= 0)
112 return false; // Bogus size.
113 sizes
->push_back(size
);
116 if (*is_any
&& !sizes
->empty()) {
117 // If is_any is true, it must occur by itself.
120 return (*is_any
|| !sizes
->empty());
123 void ParseWebAppFromWebDocument(WebFrame
* frame
,
124 WebApplicationInfo
* app_info
) {
125 WebDocument document
= frame
->document();
126 if (document
.isNull())
129 WebElement head
= document
.head();
133 GURL document_url
= document
.url();
134 WebNodeList children
= head
.childNodes();
135 for (unsigned i
= 0; i
< children
.length(); ++i
) {
136 WebNode child
= children
.item(i
);
137 if (!child
.isElementNode())
139 WebElement elem
= child
.to
<WebElement
>();
141 if (elem
.hasHTMLTagName("link")) {
142 std::string rel
= elem
.getAttribute("rel").utf8();
143 // "rel" attribute may use either "icon" or "shortcut icon".
145 // <http://en.wikipedia.org/wiki/Favicon>
146 // <http://dev.w3.org/html5/spec/Overview.html#rel-icon>
148 // Bookmark apps also support "apple-touch-icon" and
149 // "apple-touch-icon-precomposed".
150 #if defined(OS_MACOSX)
151 bool bookmark_apps_enabled
= base::CommandLine::ForCurrentProcess()->
152 HasSwitch(switches::kEnableNewBookmarkApps
);
154 bool bookmark_apps_enabled
= !base::CommandLine::ForCurrentProcess()->
155 HasSwitch(switches::kDisableNewBookmarkApps
);
157 if (base::LowerCaseEqualsASCII(rel
, "icon") ||
158 base::LowerCaseEqualsASCII(rel
, "shortcut icon") ||
159 (bookmark_apps_enabled
&&
160 (base::LowerCaseEqualsASCII(rel
, "apple-touch-icon") ||
161 base::LowerCaseEqualsASCII(rel
, "apple-touch-icon-precomposed")))) {
162 AddInstallIcon(elem
, &app_info
->icons
);
164 } else if (elem
.hasHTMLTagName("meta") && elem
.hasAttribute("name")) {
165 std::string name
= elem
.getAttribute("name").utf8();
166 WebString content
= elem
.getAttribute("content");
167 if (name
== "application-name") {
168 app_info
->title
= content
;
169 } else if (name
== "description") {
170 app_info
->description
= content
;
171 } else if (name
== "application-url") {
172 std::string url
= content
.utf8();
173 app_info
->app_url
= document_url
.is_valid() ?
174 document_url
.Resolve(url
) : GURL(url
);
175 if (!app_info
->app_url
.is_valid())
176 app_info
->app_url
= GURL();
177 } else if (name
== "mobile-web-app-capable" &&
178 base::LowerCaseEqualsASCII(base::StringPiece16(content
),
180 app_info
->mobile_capable
= WebApplicationInfo::MOBILE_CAPABLE
;
181 } else if (name
== "apple-mobile-web-app-capable" &&
182 base::LowerCaseEqualsASCII(
183 base::StringPiece16(content
), "yes") &&
184 app_info
->mobile_capable
==
185 WebApplicationInfo::MOBILE_CAPABLE_UNSPECIFIED
) {
186 app_info
->mobile_capable
= WebApplicationInfo::MOBILE_CAPABLE_APPLE
;
192 } // namespace web_apps