blob: 588e068fc7f14409b3e389a107c23807dfb7635d [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/web_state/ui/favicon_util.h"
#include <CoreFoundation/CoreFoundation.h>
#import <WebKit/WebKit.h>
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/values.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace web {
bool ExtractFaviconURL(const base::DictionaryValue* favicon_url_message,
const GURL& page_origin,
std::vector<web::FaviconURL>* urls) {
const base::Value* favicons_value = favicon_url_message->FindKey("favicons");
if (!favicons_value || !favicons_value->is_list()) {
DLOG(WARNING) << "JS message parameter not found: favicons";
return false;
}
const std::vector<base::Value>& favicons = favicons_value->GetList();
BOOL has_favicon = NO;
for (const base::Value& favicon : favicons) {
if (!favicon.is_dict())
return false;
const base::Value* href_value =
favicon.FindKeyOfType("href", base::Value::Type::STRING);
if (!href_value) {
DLOG(WARNING) << "JS message parameter not found: href";
return false;
}
auto href = href_value->GetString();
const base::Value* rel_value =
favicon.FindKeyOfType("rel", base::Value::Type::STRING);
if (!rel_value) {
DLOG(WARNING) << "JS message parameter not found: rel";
return false;
}
auto rel = rel_value->GetString();
std::vector<gfx::Size> sizes;
if (const base::Value* size_value =
favicon.FindKeyOfType("sizes", base::Value::Type::STRING)) {
auto sizes_string = size_value->GetString();
// Parse the sizes attribute. It should consist of one or multiple
// elements of the form "76x76", separated by a whitespace. So "76x76" or
// "120x120 192x192" are legit.
auto split_sizes = base::SplitStringPiece(
sizes_string, base::kWhitespaceASCII, base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
for (const auto& cut : split_sizes) {
std::vector<base::StringPiece> pieces = base::SplitStringPiece(
cut, "x", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
int width = 0, height = 0;
if (pieces.size() != 2 || !base::StringToInt(pieces[0], &width) ||
!base::StringToInt(pieces[1], &height)) {
DLOG(WARNING) << "JS message parameter sizes incorrectly formatted.";
continue;
}
if (width > 0 && height > 0)
sizes.push_back(gfx::Size(width, height));
}
}
BOOL is_apple_touch = YES;
web::FaviconURL::IconType icon_type = web::FaviconURL::IconType::kFavicon;
if (rel == "apple-touch-icon")
icon_type = web::FaviconURL::IconType::kTouchIcon;
else if (rel == "apple-touch-icon-precomposed")
icon_type = web::FaviconURL::IconType::kTouchPrecomposedIcon;
else
is_apple_touch = NO;
GURL url(href);
if (url.is_valid()) {
urls->push_back(web::FaviconURL(url, icon_type, sizes));
has_favicon = has_favicon || !is_apple_touch;
}
}
if (!has_favicon) {
// If an HTTP(S)? webpage does not reference a "favicon" of a type different
// from apple touch, then search for a file named "favicon.ico" at the root
// of the website (legacy). http://en.wikipedia.org/wiki/Favicon
if (page_origin.is_valid() && page_origin.SchemeIsHTTPOrHTTPS()) {
GURL::Replacements replacements;
replacements.SetPathStr("/favicon.ico");
replacements.ClearQuery();
replacements.ClearRef();
urls->push_back(web::FaviconURL(
page_origin.ReplaceComponents(replacements),
web::FaviconURL::IconType::kFavicon, std::vector<gfx::Size>()));
}
}
return true;
}
} // namespace web