summaryrefslogtreecommitdiffstats
path: root/components/favicon_base
diff options
context:
space:
mode:
authorblundell <blundell@chromium.org>2015-07-08 03:07:45 -0700
committerCommit bot <commit-bot@chromium.org>2015-07-08 10:08:22 +0000
commitee0ade20e42f6ff67ec7402c80610e3d0b7f960a (patch)
tree60056d8d047c4bf82f6bf31ce0ec8ca286ef268c /components/favicon_base
parent0c714f7ca6d4f027501aa8ef8ae91ebc99788b62 (diff)
downloadchromium_src-ee0ade20e42f6ff67ec7402c80610e3d0b7f960a.zip
chromium_src-ee0ade20e42f6ff67ec7402c80610e3d0b7f960a.tar.gz
chromium_src-ee0ade20e42f6ff67ec7402c80610e3d0b7f960a.tar.bz2
Componentize //chrome/common/favicon
This code is used by iOS; move it into //components/favicon_base. TBR=sky, rdsmith Review URL: https://codereview.chromium.org/1223603002 Cr-Commit-Position: refs/heads/master@{#337794}
Diffstat (limited to 'components/favicon_base')
-rw-r--r--components/favicon_base/BUILD.gn10
-rw-r--r--components/favicon_base/DEPS1
-rw-r--r--components/favicon_base/fallback_icon_url_parser.cc137
-rw-r--r--components/favicon_base/fallback_icon_url_parser.h72
-rw-r--r--components/favicon_base/fallback_icon_url_parser_unittest.cc283
-rw-r--r--components/favicon_base/favicon_url_parser.cc127
-rw-r--r--components/favicon_base/favicon_url_parser.h40
-rw-r--r--components/favicon_base/favicon_url_parser_unittest.cc165
-rw-r--r--components/favicon_base/large_icon_url_parser.cc43
-rw-r--r--components/favicon_base/large_icon_url_parser.h46
-rw-r--r--components/favicon_base/large_icon_url_parser_unittest.cc61
11 files changed, 985 insertions, 0 deletions
diff --git a/components/favicon_base/BUILD.gn b/components/favicon_base/BUILD.gn
index 2fcfb9c..8f92d7a 100644
--- a/components/favicon_base/BUILD.gn
+++ b/components/favicon_base/BUILD.gn
@@ -6,19 +6,26 @@ source_set("favicon_base") {
sources = [
"fallback_icon_style.cc",
"fallback_icon_style.h",
+ "fallback_icon_url_parser.cc",
+ "fallback_icon_url_parser.h",
"favicon_callback.h",
"favicon_types.cc",
"favicon_types.h",
+ "favicon_url_parser.cc",
+ "favicon_url_parser.h",
"favicon_usage_data.cc",
"favicon_usage_data.h",
"favicon_util.cc",
"favicon_util.h",
+ "large_icon_url_parser.cc",
+ "large_icon_url_parser.h",
"select_favicon_frames.cc",
"select_favicon_frames.h",
]
deps = [
"//base",
+ "//net",
"//skia",
"//ui/base",
"//ui/gfx",
@@ -30,6 +37,9 @@ source_set("favicon_base") {
source_set("unit_tests") {
testonly = true
sources = [
+ "fallback_icon_url_parser_unittest.cc",
+ "favicon_url_parser_unittest.cc",
+ "large_icon_url_parser_unittest.cc",
"select_favicon_frames_unittest.cc",
]
diff --git a/components/favicon_base/DEPS b/components/favicon_base/DEPS
index 0265afa..f5a0bac 100644
--- a/components/favicon_base/DEPS
+++ b/components/favicon_base/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+net",
"+skia/ext",
"+third_party/skia/include",
"+ui/base",
diff --git a/components/favicon_base/fallback_icon_url_parser.cc b/components/favicon_base/fallback_icon_url_parser.cc
new file mode 100644
index 0000000..ba9f2321
--- /dev/null
+++ b/components/favicon_base/fallback_icon_url_parser.cc
@@ -0,0 +1,137 @@
+// Copyright 2015 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.
+
+#include "components/favicon_base/fallback_icon_url_parser.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "third_party/skia/include/utils/SkParse.h"
+#include "ui/gfx/favicon_size.h"
+
+namespace {
+
+// List of sizes corresponding to RGB, ARGB, RRGGBB, AARRGGBB.
+const size_t kValidHexColorSizes[] = {3, 4, 6, 8};
+
+// Returns whether |color_str| is a valid CSS color in hex format if we prepend
+// '#', i.e., whether |color_str| matches /^[0-9A-Fa-f]{3,4,6,8}$/.
+bool IsHexColorString(const std::string& color_str) {
+ size_t len = color_str.length();
+ const size_t* end = kValidHexColorSizes + arraysize(kValidHexColorSizes);
+ if (std::find(kValidHexColorSizes, end, len) == end)
+ return false;
+ for (auto ch : color_str) {
+ if (!base::IsHexDigit(ch))
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace chrome {
+
+ParsedFallbackIconPath::ParsedFallbackIconPath()
+ : size_in_pixels_(gfx::kFaviconSize) {
+}
+
+ParsedFallbackIconPath::~ParsedFallbackIconPath() {
+}
+
+bool ParsedFallbackIconPath::Parse(const std::string& path) {
+ if (path.empty())
+ return false;
+
+ size_t slash = path.find("/", 0);
+ if (slash == std::string::npos)
+ return false;
+ std::string spec_str = path.substr(0, slash);
+ if (!ParseSpecs(spec_str, &size_in_pixels_, &style_))
+ return false; // Parse failed.
+
+ // Need to store the index of the URL field, so Instant Extended can translate
+ // fallback icon URLs using advanced parameters.
+ // Example:
+ // "chrome-search://fallback-icon/48/<renderer-id>/<most-visited-id>"
+ // would be translated to:
+ // "chrome-search://fallback-icon/48/<most-visited-item-with-given-id>".
+ path_index_ = slash + 1;
+ url_string_ = path.substr(path_index_);
+ return true;
+}
+
+// static
+bool ParsedFallbackIconPath::ParseSpecs(
+ const std::string& specs_str,
+ int *size,
+ favicon_base::FallbackIconStyle* style) {
+ DCHECK(size);
+ DCHECK(style);
+
+ std::vector<std::string> tokens;
+ base::SplitStringDontTrim(specs_str, ',', &tokens);
+ if (tokens.size() != 5) // Force "," for empty fields.
+ return false;
+
+ *size = gfx::kFaviconSize;
+ if (!tokens[0].empty() && !base::StringToInt(tokens[0], size))
+ return false;
+ if (*size <= 0)
+ return false;
+
+ if (!tokens[1].empty() && !ParseColor(tokens[1], &style->background_color))
+ return false;
+
+ if (tokens[2].empty())
+ favicon_base::MatchFallbackIconTextColorAgainstBackgroundColor(style);
+ else if (!ParseColor(tokens[2], &style->text_color))
+ return false;
+
+ if (!tokens[3].empty() &&
+ !base::StringToDouble(tokens[3], &style->font_size_ratio))
+ return false;
+
+ if (!tokens[4].empty() && !base::StringToDouble(tokens[4], &style->roundness))
+ return false;
+
+ return favicon_base::ValidateFallbackIconStyle(*style);
+}
+
+// static
+bool ParsedFallbackIconPath::ParseColor(const std::string& color_str,
+ SkColor* color) {
+ DCHECK(color);
+ // Exclude the empty case. Also disallow the '#' prefix, since we want color
+ // to be part of an URL, but in URL '#' is used for ref fragment.
+ if (color_str.empty() || color_str[0] == '#')
+ return false;
+
+ // If a valid color hex string is given, prepend '#' and parse (always works).
+ // This is unambiguous since named color never only use leters 'a' to 'f'.
+ if (IsHexColorString(color_str)) {
+ // Default alpha to 0xFF since FindColor() preserves unspecified alpha.
+ *color = SK_ColorWHITE;
+ // Need temp variable to avoid use-after-free of returned pointer.
+ std::string color_str_with_hash = "#" + color_str;
+ const char* end = SkParse::FindColor(color_str_with_hash.c_str(), color);
+ DCHECK(end && !*end); // Call should succeed and consume string.
+ return true;
+ }
+
+ // Default alpha to 0xFF.
+ SkColor temp_color = SK_ColorWHITE;
+ const char* end = SkParse::FindColor(color_str.c_str(), &temp_color);
+ if (end && !*end) { // Successful if call succeeds and string is consumed.
+ *color = temp_color;
+ return true;
+ }
+ return false;
+}
+
+} // namespace chrome
diff --git a/components/favicon_base/fallback_icon_url_parser.h b/components/favicon_base/fallback_icon_url_parser.h
new file mode 100644
index 0000000..5d5971e8
--- /dev/null
+++ b/components/favicon_base/fallback_icon_url_parser.h
@@ -0,0 +1,72 @@
+// Copyright 2015 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.
+
+#ifndef COMPONENTS_FAVICON_BASE_FALLBACK_ICON_URL_PARSER_H_
+#define COMPONENTS_FAVICON_BASE_FALLBACK_ICON_URL_PARSER_H_
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "components/favicon_base/fallback_icon_style.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace chrome {
+
+class ParsedFallbackIconPath {
+ public:
+ ParsedFallbackIconPath();
+ ~ParsedFallbackIconPath();
+
+ const std::string& url_string() const { return url_string_; }
+
+ int size_in_pixels() const { return size_in_pixels_; }
+
+ const favicon_base::FallbackIconStyle& style() const { return style_; }
+
+ size_t path_index() const { return path_index_; }
+
+ // Parses |path|, which should be in the format described at the top of the
+ // file "chrome/browser/ui/webui/fallback_icon_source.h".
+ bool Parse(const std::string& path);
+
+ private:
+ // Parses |specs_str|, which should be the comma-separated value portion
+ // in the format described at the top of the file
+ // "chrome/browser/ui/webui/fallback_icon_source.h".
+ static bool ParseSpecs(const std::string& specs_str,
+ int *size,
+ favicon_base::FallbackIconStyle* style);
+
+ // Helper to parse color string (e.g., "red", "#f00", "#aB0137"). On success,
+ // returns true and writes te result to |*color|.
+ static bool ParseColor(const std::string& color_str, SkColor* color);
+
+ friend class FallbackIconUrlParserTest;
+ FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseColorSuccess);
+ FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseColorFailure);
+ FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsEmpty);
+ FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsPartial);
+ FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsFull);
+ FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsFailure);
+
+ // The page URL string the fallback icon is requested for.
+ std::string url_string_;
+
+ // The size of the requested fallback icon in pixels.
+ int size_in_pixels_;
+
+ // Styling specifications of fallback icon.
+ favicon_base::FallbackIconStyle style_;
+
+ // The index of the first character (relative to the path) where the the URL
+ // from which the fallback icon is being requested is located.
+ size_t path_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(ParsedFallbackIconPath);
+};
+
+} // namespace chrome
+
+#endif // COMPONENTS_FAVICON_BASE_FALLBACK_ICON_URL_PARSER_H_
diff --git a/components/favicon_base/fallback_icon_url_parser_unittest.cc b/components/favicon_base/fallback_icon_url_parser_unittest.cc
new file mode 100644
index 0000000..af36304
--- /dev/null
+++ b/components/favicon_base/fallback_icon_url_parser_unittest.cc
@@ -0,0 +1,283 @@
+// Copyright 2015 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.
+
+#include "components/favicon_base/fallback_icon_url_parser.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "components/favicon_base/fallback_icon_style.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/favicon_size.h"
+#include "url/gurl.h"
+
+using chrome::ParsedFallbackIconPath;
+using favicon_base::FallbackIconStyle;
+
+namespace chrome {
+
+namespace {
+
+// Default values for FallbackIconStyle, from
+// /components/favicon_base/fallback_icon_style.h
+const SkColor kDefaultBackgroundColor = SkColorSetRGB(0x78, 0x78, 0x78);
+const SkColor kDefaultTextColorDark = SK_ColorBLACK;
+const SkColor kDefaultTextColorLight = SK_ColorWHITE;
+const double kDefaultFontSizeRatio = 0.44;
+const double kDefaultRoundness = 0;
+
+const char kTestUrlStr[] = "https://www.google.ca/imghp?hl=en&tab=wi";
+
+} // namespace
+
+class FallbackIconUrlParserTest : public testing::Test {
+ public:
+ FallbackIconUrlParserTest() {
+ }
+
+ bool ParseSpecs(const std::string& specs_str,
+ int *size,
+ favicon_base::FallbackIconStyle* style) {
+ return ParsedFallbackIconPath::ParseSpecs(specs_str, size, style);
+ }
+
+ bool ParseColor(const std::string& color_str, SkColor* color) {
+ return ParsedFallbackIconPath::ParseColor(color_str, color);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FallbackIconUrlParserTest);
+};
+
+TEST_F(FallbackIconUrlParserTest, ParseColorSuccess) {
+ SkColor c;
+ EXPECT_TRUE(ParseColor("31aBf0f4", &c));
+ EXPECT_EQ(SkColorSetARGB(0x31, 0xAB, 0xF0, 0xF4), c);
+ EXPECT_TRUE(ParseColor("01aBf0", &c));
+ EXPECT_EQ(SkColorSetRGB(0x01, 0xAB, 0xF0), c);
+ EXPECT_TRUE(ParseColor("501a", &c));
+ EXPECT_EQ(SkColorSetARGB(0x55, 0x00, 0x11, 0xAA), c);
+ EXPECT_TRUE(ParseColor("01a", &c));
+ EXPECT_EQ(SkColorSetRGB(0x00, 0x11, 0xAA), c);
+ EXPECT_TRUE(ParseColor("000000", &c));
+ EXPECT_EQ(SkColorSetARGB(0xFF, 0x00, 0x00, 0x00), c);
+ EXPECT_TRUE(ParseColor("red", &c));
+ EXPECT_EQ(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00), c);
+}
+
+TEST_F(FallbackIconUrlParserTest, ParseColorFailure) {
+ const char* test_cases[] = {
+ "",
+ "00000",
+ "000000000",
+ " 000000",
+ "ABCDEFG",
+ "#000",
+ "#000000",
+ "000000 ",
+ "ABCDEFH",
+ "#ABCDEF",
+ };
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ SkColor c;
+ EXPECT_FALSE(ParseColor(test_cases[i], &c))
+ << "test_cases[" << i << "]";
+ }
+}
+
+TEST_F(FallbackIconUrlParserTest, ParseSpecsEmpty) {
+ int size;
+ FallbackIconStyle style;
+ EXPECT_TRUE(ParseSpecs(",,,,", &size, &style));
+ EXPECT_EQ(16, size);
+ EXPECT_EQ(kDefaultBackgroundColor, style.background_color);
+ EXPECT_EQ(kDefaultTextColorLight, style.text_color);
+ EXPECT_EQ(kDefaultFontSizeRatio, style.font_size_ratio);
+ EXPECT_EQ(kDefaultRoundness, style.roundness);
+}
+
+TEST_F(FallbackIconUrlParserTest, ParseSpecsPartial) {
+ int size;
+ FallbackIconStyle style;
+ EXPECT_TRUE(ParseSpecs(",,aCE,,0.1", &size, &style));
+ EXPECT_EQ(16, size);
+ EXPECT_EQ(kDefaultBackgroundColor, style.background_color);
+ EXPECT_EQ(SkColorSetRGB(0xAA, 0xCC, 0xEE), style.text_color);
+ EXPECT_EQ(kDefaultFontSizeRatio, style.font_size_ratio);
+ EXPECT_EQ(0.1, style.roundness);
+}
+
+TEST_F(FallbackIconUrlParserTest, ParseSpecsFull) {
+ int size;
+
+ {
+ FallbackIconStyle style;
+ EXPECT_TRUE(ParseSpecs("16,000,f01,0.75,0.25", &size, &style));
+ EXPECT_EQ(16, size);
+ EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
+ EXPECT_EQ(SkColorSetRGB(0xff, 0x00, 0x11), style.text_color);
+ EXPECT_EQ(0.75, style.font_size_ratio);
+ EXPECT_EQ(0.25, style.roundness);
+ }
+
+ {
+ FallbackIconStyle style;
+ EXPECT_TRUE(ParseSpecs("48,black,123456,0.5,0.3", &size, &style));
+ EXPECT_EQ(48, size);
+ EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
+ EXPECT_EQ(SkColorSetRGB(0x12, 0x34, 0x56), style.text_color);
+ EXPECT_EQ(0.5, style.font_size_ratio);
+ EXPECT_EQ(0.3, style.roundness);
+ }
+
+ {
+ FallbackIconStyle style;
+ EXPECT_TRUE(ParseSpecs("1,000,red,0,0", &size, &style));
+ EXPECT_EQ(1, size);
+ EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
+ EXPECT_EQ(SkColorSetRGB(0xFF, 0x00, 0x00), style.text_color);
+ EXPECT_EQ(0, style.font_size_ratio);
+ EXPECT_EQ(0, style.roundness);
+ }
+}
+
+TEST_F(FallbackIconUrlParserTest, ParseSpecsDefaultTextColor) {
+ int size;
+
+ {
+ // Dark background -> Light text.
+ FallbackIconStyle style;
+ EXPECT_TRUE(ParseSpecs(",000,,,", &size, &style));
+ EXPECT_EQ(kDefaultTextColorLight, style.text_color);
+ }
+
+ {
+ // Light background -> Dark text.
+ FallbackIconStyle style;
+ EXPECT_TRUE(ParseSpecs(",fff,,,", &size, &style));
+ EXPECT_EQ(kDefaultTextColorDark, style.text_color);
+ }
+
+ {
+ // Light background -> Dark text, more params don't matter.
+ FallbackIconStyle style;
+ EXPECT_TRUE(ParseSpecs("107,fff,,0.3,0.5", &size, &style));
+ EXPECT_EQ(kDefaultTextColorDark, style.text_color);
+ }
+}
+
+TEST_F(FallbackIconUrlParserTest, ParseSpecsFailure) {
+ const char* test_cases[] = {
+ // Need exactly 5 params.
+ "",
+ "16",
+ "16,black",
+ "16,black,fff",
+ "16,black,fff,0.75",
+ ",,,"
+ ",,,,,",
+ "16,black,fff,0.75,0.25,junk",
+ // Don't allow any space.
+ "16,black,fff, 0.75,0.25",
+ "16,black ,fff,0.75,0.25",
+ "16,black,fff,0.75,0.25 ",
+ // Adding junk text.
+ "16,black,fff,0.75,0.25junk",
+ "junk,black,fff,0.75,0.25",
+ "16,#junk,fff,0.75,0.25",
+ "16,black,junk,0.75,0.25",
+ "16,black,fff,junk,0.25",
+ "16,black,fff,0.75,junk",
+ // Out of bound.
+ "0,black,fff,0.75,0.25", // size.
+ "4294967296,black,fff,0.75,0.25", // size.
+ "-1,black,fff,0.75,0.25", // size.
+ "16,black,fff,-0.1,0.25", // font_size_ratio.
+ "16,black,fff,1.1,0.25", // font_size_ratio.
+ "16,black,fff,0.75,-0.1", // roundness.
+ "16,black,fff,0.75,1.1", // roundness.
+ };
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ int size;
+ FallbackIconStyle style;
+ EXPECT_FALSE(ParseSpecs(test_cases[i], &size, &style))
+ << "test_cases[" << i << "]";
+
+ }
+}
+
+TEST_F(FallbackIconUrlParserTest, ParseFallbackIconPathSuccess) {
+ const std::string specs = "31,black,fff,0.75,0.25";
+
+ // Everything populated.
+ {
+ chrome::ParsedFallbackIconPath parsed;
+ EXPECT_TRUE(parsed.Parse(specs + "/" + kTestUrlStr));
+ EXPECT_EQ(31, parsed.size_in_pixels());
+ const favicon_base::FallbackIconStyle& style = parsed.style();
+ EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
+ EXPECT_EQ(SkColorSetRGB(0xFF, 0xFF, 0xFF), style.text_color);
+ EXPECT_EQ(0.75, style.font_size_ratio);
+ EXPECT_EQ(0.25, style.roundness);
+ EXPECT_EQ(GURL(kTestUrlStr), GURL(parsed.url_string()));
+ EXPECT_EQ(specs.length() + 1, parsed.path_index());
+ }
+
+ // Empty URL.
+ {
+ chrome::ParsedFallbackIconPath parsed;
+ EXPECT_TRUE(parsed.Parse(specs + "/"));
+ EXPECT_EQ(31, parsed.size_in_pixels());
+ const favicon_base::FallbackIconStyle& style = parsed.style();
+ EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
+ EXPECT_EQ(SkColorSetRGB(0xFF, 0xFF, 0xFF), style.text_color);
+ EXPECT_EQ(0.75, style.font_size_ratio);
+ EXPECT_EQ(0.25, style.roundness);
+ EXPECT_EQ(GURL(), GURL(parsed.url_string()));
+ EXPECT_EQ(specs.length() + 1, parsed.path_index());
+ }
+
+ // Tolerate invalid URL.
+ {
+ chrome::ParsedFallbackIconPath parsed;
+ EXPECT_TRUE(parsed.Parse(specs + "/NOT A VALID URL"));
+ EXPECT_EQ(31, parsed.size_in_pixels());
+ const favicon_base::FallbackIconStyle& style = parsed.style();
+ EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
+ EXPECT_EQ(SkColorSetRGB(0xFF, 0xFF, 0xFF), style.text_color);
+ EXPECT_EQ(0.75, style.font_size_ratio);
+ EXPECT_EQ(0.25, style.roundness);
+ EXPECT_EQ("NOT A VALID URL", parsed.url_string());
+ EXPECT_EQ(specs.length() + 1, parsed.path_index());
+ }
+
+ // Size and style are default.
+ {
+ std::string specs2 = ",,,,";
+ chrome::ParsedFallbackIconPath parsed;
+ EXPECT_TRUE(parsed.Parse(specs2 + "/" + kTestUrlStr));
+ EXPECT_EQ(gfx::kFaviconSize, parsed.size_in_pixels());
+ const favicon_base::FallbackIconStyle& style = parsed.style();
+ EXPECT_EQ(kDefaultBackgroundColor, style.background_color);
+ EXPECT_EQ(kDefaultTextColorLight, style.text_color);
+ EXPECT_EQ(kDefaultFontSizeRatio, style.font_size_ratio);
+ EXPECT_EQ(kDefaultRoundness, style.roundness);
+ EXPECT_EQ(GURL(kTestUrlStr), GURL(parsed.url_string()));
+ EXPECT_EQ(specs2.length() + 1, parsed.path_index());
+ }
+}
+
+TEST_F(FallbackIconUrlParserTest, ParseFallbackIconPathFailure) {
+ const char* test_cases[] = {
+ // Bad size.
+ "-1,000,fff,0.75,0.25/http://www.google.com/",
+ // Bad specs.
+ "32,#junk,fff,0.75,0.25/http://www.google.com/",
+ };
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ chrome::ParsedFallbackIconPath parsed;
+ EXPECT_FALSE(parsed.Parse(test_cases[i])) << "test_cases[" << i << "]";
+ }
+}
+
+} // namespace chrome
diff --git a/components/favicon_base/favicon_url_parser.cc b/components/favicon_base/favicon_url_parser.cc
new file mode 100644
index 0000000..27ee8e1
--- /dev/null
+++ b/components/favicon_base/favicon_url_parser.cc
@@ -0,0 +1,127 @@
+// Copyright 2013 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.
+
+#include "components/favicon_base/favicon_url_parser.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "components/favicon_base/favicon_types.h"
+#include "net/url_request/url_request.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/favicon_size.h"
+
+namespace {
+
+// Parameters which can be used in chrome://favicon path. See file
+// "chrome/browser/ui/webui/favicon_source.h" for a description of
+// what each does.
+const char kIconURLParameter[] = "iconurl/";
+const char kLargestParameter[] = "largest/";
+const char kOriginParameter[] = "origin/";
+const char kSizeParameter[] = "size/";
+
+// Returns true if |search| is a substring of |path| which starts at
+// |start_index|.
+bool HasSubstringAt(const std::string& path,
+ size_t start_index,
+ const std::string& search) {
+ return path.compare(start_index, search.length(), search) == 0;
+}
+
+} // namespace
+
+namespace chrome {
+
+bool ParseFaviconPath(const std::string& path,
+ int icon_types,
+ ParsedFaviconPath* parsed) {
+ parsed->is_icon_url = false;
+ parsed->url = "";
+ parsed->size_in_dip = gfx::kFaviconSize;
+ parsed->device_scale_factor = 1.0f;
+ parsed->path_index = std::string::npos;
+
+ if (path.empty())
+ return false;
+
+ size_t parsed_index = 0;
+ if (HasSubstringAt(path, parsed_index, kLargestParameter)) {
+ parsed_index += strlen(kLargestParameter);
+ parsed->size_in_dip = 0;
+ } else if (HasSubstringAt(path, parsed_index, kSizeParameter)) {
+ parsed_index += strlen(kSizeParameter);
+
+ size_t slash = path.find("/", parsed_index);
+ if (slash == std::string::npos)
+ return false;
+
+ size_t scale_delimiter = path.find("@", parsed_index);
+ std::string size_str;
+ std::string scale_str;
+ if (scale_delimiter == std::string::npos) {
+ // Support the legacy size format of 'size/aa/' where 'aa' is the desired
+ // size in DIP for the sake of not regressing the extensions which use it.
+ size_str = path.substr(parsed_index, slash - parsed_index);
+ } else {
+ size_str = path.substr(parsed_index, scale_delimiter - parsed_index);
+ scale_str = path.substr(scale_delimiter + 1,
+ slash - scale_delimiter - 1);
+ }
+
+ if (!base::StringToInt(size_str, &parsed->size_in_dip))
+ return false;
+
+ if (parsed->size_in_dip != (gfx::kFaviconSize * 4) &&
+ parsed->size_in_dip != (gfx::kFaviconSize * 2)) {
+ // Only 64x64, 32x32 and 16x16 icons are supported.
+ parsed->size_in_dip = gfx::kFaviconSize;
+ }
+ if (!scale_str.empty())
+ webui::ParseScaleFactor(scale_str, &parsed->device_scale_factor);
+
+ // Return the default favicon (as opposed to a resized favicon) for
+ // favicon sizes which are not cached by the favicon service.
+ // Currently the favicon service caches:
+ // - favicons of sizes "gfx::kFaviconSize * scale factor" px of type FAVICON
+ // where scale factor is one of FaviconUtil::GetFaviconScales().
+ // - the largest TOUCH_ICON / TOUCH_PRECOMPOSED_ICON
+ if (parsed->size_in_dip != gfx::kFaviconSize &&
+ icon_types == favicon_base::FAVICON)
+ return false;
+
+ parsed_index = slash + 1;
+ }
+
+ if (HasSubstringAt(path, parsed_index, kIconURLParameter)) {
+ parsed_index += strlen(kIconURLParameter);
+ parsed->is_icon_url = true;
+ parsed->url = path.substr(parsed_index);
+ } else if (HasSubstringAt(path, parsed_index, kOriginParameter)) {
+ // URL requests prefixed with "origin/" are converted to a form with an
+ // empty path and a valid scheme. (e.g., example.com -->
+ // http://example.com/ or http://example.com/a --> http://example.com/)
+ parsed_index += strlen(kOriginParameter);
+ std::string possibly_invalid_url = path.substr(parsed_index);
+
+ // If the URL does not specify a scheme (e.g., example.com instead of
+ // http://example.com), add "http://" as a default.
+ if (!GURL(possibly_invalid_url).has_scheme())
+ possibly_invalid_url = "http://" + possibly_invalid_url;
+
+ // Strip the path beyond the top-level domain.
+ parsed->url = GURL(possibly_invalid_url).GetOrigin().spec();
+ } else {
+ parsed->url = path.substr(parsed_index);
+ }
+
+ // The parsed index needs to be returned in order to allow Instant Extended
+ // to translate favicon URLs using advanced parameters.
+ // Example:
+ // "chrome-search://favicon/size/16@2x/<renderer-id>/<most-visited-id>"
+ // would be translated to:
+ // "chrome-search://favicon/size/16@2x/<most-visited-item-with-given-id>".
+ parsed->path_index = parsed_index;
+ return true;
+}
+
+} // namespace chrome
diff --git a/components/favicon_base/favicon_url_parser.h b/components/favicon_base/favicon_url_parser.h
new file mode 100644
index 0000000..c1d3f022
--- /dev/null
+++ b/components/favicon_base/favicon_url_parser.h
@@ -0,0 +1,40 @@
+// Copyright 2013 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.
+
+#ifndef COMPONENTS_FAVICON_BASE_FAVICON_URL_PARSER_H_
+#define COMPONENTS_FAVICON_BASE_FAVICON_URL_PARSER_H_
+
+#include <string>
+
+namespace chrome {
+
+struct ParsedFaviconPath {
+ // Whether the URL has the "iconurl" parameter.
+ bool is_icon_url;
+
+ // The URL from which the favicon is being requested.
+ std::string url;
+
+ // The size of the requested favicon in dip.
+ int size_in_dip;
+
+ // The device scale factor of the requested favicon.
+ float device_scale_factor;
+
+ // The index of the first character (relative to the path) where the the URL
+ // from which the favicon is being requested is located.
+ size_t path_index;
+};
+
+// Parses |path|, which should be in the format described at the top of the
+// file "chrome/browser/ui/webui/favicon_source.h". |icon_types| indicates
+// which icon types are supported. Returns true if |path| could be parsed.
+// The result of the parsing will be stored in a ParsedFaviconPath struct.
+bool ParseFaviconPath(const std::string& path,
+ int icon_types,
+ ParsedFaviconPath* parsed);
+
+} // namespace chrome
+
+#endif // COMPONENTS_FAVICON_BASE_FAVICON_URL_PARSER_H_
diff --git a/components/favicon_base/favicon_url_parser_unittest.cc b/components/favicon_base/favicon_url_parser_unittest.cc
new file mode 100644
index 0000000..f460068
--- /dev/null
+++ b/components/favicon_base/favicon_url_parser_unittest.cc
@@ -0,0 +1,165 @@
+// Copyright 2013 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.
+
+#include "components/favicon_base/favicon_url_parser.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "components/favicon_base/favicon_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/layout.h"
+
+class FaviconUrlParserTest : public testing::Test {
+ public:
+ FaviconUrlParserTest() {
+ // Set the supported scale factors because the supported scale factors
+ // affect the result of ParsePathAndScale().
+ std::vector<ui::ScaleFactor> supported_scale_factors;
+ supported_scale_factors.push_back(ui::SCALE_FACTOR_100P);
+ supported_scale_factors.push_back(ui::SCALE_FACTOR_140P);
+ scoped_set_supported_scale_factors_.reset(
+ new ui::test::ScopedSetSupportedScaleFactors(supported_scale_factors));
+ }
+
+ ~FaviconUrlParserTest() override {}
+
+ private:
+ typedef scoped_ptr<ui::test::ScopedSetSupportedScaleFactors>
+ ScopedSetSupportedScaleFactors;
+ ScopedSetSupportedScaleFactors scoped_set_supported_scale_factors_;
+
+ DISALLOW_COPY_AND_ASSIGN(FaviconUrlParserTest);
+};
+
+// Test parsing path with no extra parameters.
+TEST_F(FaviconUrlParserTest, ParsingNoExtraParams) {
+ const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
+ int icon_types = favicon_base::TOUCH_PRECOMPOSED_ICON;
+ chrome::ParsedFaviconPath parsed;
+
+ const std::string path1 = url;
+ EXPECT_TRUE(chrome::ParseFaviconPath(path1, icon_types, &parsed));
+ EXPECT_FALSE(parsed.is_icon_url);
+ EXPECT_EQ(url, parsed.url);
+ EXPECT_EQ(16, parsed.size_in_dip);
+ EXPECT_EQ(1.0f, parsed.device_scale_factor);
+}
+
+// Test parsing path with a 'size' parameter.
+TEST_F(FaviconUrlParserTest, ParsingSizeParam) {
+ const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
+ int icon_types = favicon_base::TOUCH_PRECOMPOSED_ICON;
+ chrome::ParsedFaviconPath parsed;
+
+ // Test that we can still parse the legacy 'size' parameter format.
+ const std::string path2 = "size/32/" + url;
+ EXPECT_TRUE(chrome::ParseFaviconPath(path2, icon_types, &parsed));
+ EXPECT_FALSE(parsed.is_icon_url);
+ EXPECT_EQ(url, parsed.url);
+ EXPECT_EQ(32, parsed.size_in_dip);
+ EXPECT_EQ(1.0f, parsed.device_scale_factor);
+
+ // Test parsing current 'size' parameter format.
+ const std::string path3 = "size/32@1.4x/" + url;
+ EXPECT_TRUE(chrome::ParseFaviconPath(path3, icon_types, &parsed));
+ EXPECT_FALSE(parsed.is_icon_url);
+ EXPECT_EQ(url, parsed.url);
+ EXPECT_EQ(32, parsed.size_in_dip);
+ EXPECT_EQ(1.4f, parsed.device_scale_factor);
+
+ // Test that we pick the ui::ScaleFactor which is closest to the passed in
+ // scale factor.
+ const std::string path4 = "size/16@1.41x/" + url;
+ EXPECT_TRUE(chrome::ParseFaviconPath(path4, icon_types, &parsed));
+ EXPECT_FALSE(parsed.is_icon_url);
+ EXPECT_EQ(url, parsed.url);
+ EXPECT_EQ(16, parsed.size_in_dip);
+ EXPECT_EQ(1.41f, parsed.device_scale_factor);
+
+ // Invalid cases.
+ const std::string path5 = "size/" + url;
+ EXPECT_FALSE(chrome::ParseFaviconPath(path5, icon_types, &parsed));
+ const std::string path6 = "size/@1x/" + url;
+ EXPECT_FALSE(chrome::ParseFaviconPath(path6, icon_types, &parsed));
+ const std::string path7 = "size/abc@1x/" + url;
+ EXPECT_FALSE(chrome::ParseFaviconPath(path7, icon_types, &parsed));
+
+ // Part of url looks like 'size' parameter.
+ const std::string path8 = "http://www.google.com/size/32@1.4x";
+ EXPECT_TRUE(chrome::ParseFaviconPath(path8, icon_types, &parsed));
+ EXPECT_FALSE(parsed.is_icon_url);
+ EXPECT_EQ(path8, parsed.url);
+ EXPECT_EQ(16, parsed.size_in_dip);
+ EXPECT_EQ(1.0f, parsed.device_scale_factor);
+}
+
+// Test parsing path with the 'largest' parameter.
+TEST_F(FaviconUrlParserTest, ParsingLargestParam) {
+ const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
+ int icon_types = favicon_base::TOUCH_PRECOMPOSED_ICON;
+ chrome::ParsedFaviconPath parsed;
+
+ const std::string path9 = "largest/" + url;
+ EXPECT_TRUE(chrome::ParseFaviconPath(path9, icon_types, &parsed));
+ EXPECT_FALSE(parsed.is_icon_url);
+ EXPECT_EQ(url, parsed.url);
+ EXPECT_EQ(0, parsed.size_in_dip);
+ // The scale factor is meaningless when requesting the largest favicon.
+}
+
+// Test parsing path with 'iconurl' parameter.
+TEST_F(FaviconUrlParserTest, ParsingIconUrlParam) {
+ const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
+ int icon_types = favicon_base::TOUCH_PRECOMPOSED_ICON;
+ chrome::ParsedFaviconPath parsed;
+
+ const std::string path10 = "iconurl/http://www.google.com/favicon.ico";
+ EXPECT_TRUE(chrome::ParseFaviconPath(path10, icon_types, &parsed));
+ EXPECT_TRUE(parsed.is_icon_url);
+ EXPECT_EQ("http://www.google.com/favicon.ico", parsed.url);
+ EXPECT_EQ(16, parsed.size_in_dip);
+ EXPECT_EQ(1.0f, parsed.device_scale_factor);
+}
+
+// Test parsing path with 'origin' parameter.
+TEST_F(FaviconUrlParserTest, ParsingOriginParam) {
+ const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
+ int icon_types = favicon_base::TOUCH_PRECOMPOSED_ICON;
+ chrome::ParsedFaviconPath parsed;
+
+ const std::string path11 = "origin/" + url;
+ EXPECT_TRUE(chrome::ParseFaviconPath(path11, icon_types, &parsed));
+ EXPECT_FALSE(parsed.is_icon_url);
+ EXPECT_EQ("https://www.google.ca/", parsed.url);
+ EXPECT_EQ(16, parsed.size_in_dip);
+ EXPECT_EQ(1.0f, parsed.device_scale_factor);
+
+ const std::string path12 = "origin/google.com";
+ EXPECT_TRUE(chrome::ParseFaviconPath(path12, icon_types, &parsed));
+ EXPECT_FALSE(parsed.is_icon_url);
+ EXPECT_EQ("http://google.com/", parsed.url);
+ EXPECT_EQ(16, parsed.size_in_dip);
+ EXPECT_EQ(1.0f, parsed.device_scale_factor);
+}
+
+// Test parsing paths with both a 'size' parameter and a 'url modifier'
+// parameter.
+TEST_F(FaviconUrlParserTest, ParsingSizeParamAndUrlModifier) {
+ const std::string url("https://www.google.ca/imghp?hl=en&tab=wi");
+ int icon_types = favicon_base::TOUCH_PRECOMPOSED_ICON;
+ chrome::ParsedFaviconPath parsed;
+
+ const std::string path13 = "size/32@1.4x/origin/" + url;
+ EXPECT_TRUE(chrome::ParseFaviconPath(path13, icon_types, &parsed));
+ EXPECT_FALSE(parsed.is_icon_url);
+ EXPECT_EQ("https://www.google.ca/", parsed.url);
+ EXPECT_EQ(32, parsed.size_in_dip);
+ EXPECT_EQ(1.4f, parsed.device_scale_factor);
+
+ const std::string path14 =
+ "largest/iconurl/http://www.google.com/favicon.ico";
+ EXPECT_TRUE(chrome::ParseFaviconPath(path14, icon_types, &parsed));
+ EXPECT_TRUE(parsed.is_icon_url);
+ EXPECT_EQ("http://www.google.com/favicon.ico", parsed.url);
+ EXPECT_EQ(0, parsed.size_in_dip);
+}
diff --git a/components/favicon_base/large_icon_url_parser.cc b/components/favicon_base/large_icon_url_parser.cc
new file mode 100644
index 0000000..9f1a9ac
--- /dev/null
+++ b/components/favicon_base/large_icon_url_parser.cc
@@ -0,0 +1,43 @@
+// Copyright 2015 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.
+
+#include "components/favicon_base/large_icon_url_parser.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "third_party/skia/include/utils/SkParse.h"
+#include "ui/gfx/favicon_size.h"
+
+LargeIconUrlParser::LargeIconUrlParser() : size_in_pixels_(48) {
+}
+
+LargeIconUrlParser::~LargeIconUrlParser() {
+}
+
+bool LargeIconUrlParser::Parse(base::StringPiece path) {
+ if (path.empty())
+ return false;
+
+ size_t slash = path.find("/", 0); // |path| does not start with '/'.
+ if (slash == base::StringPiece::npos)
+ return false;
+ base::StringPiece size_str = path.substr(0, slash);
+ // Disallow empty, non-numeric, or non-positive sizes.
+ if (size_str.empty() ||
+ !base::StringToInt(size_str, &size_in_pixels_) ||
+ size_in_pixels_ <= 0)
+ return false;
+
+ // Need to store the index of the URL field, so Instant Extended can translate
+ // large icon URLs using advanced parameters.
+ // Example:
+ // "chrome-search://large-icon/48/<renderer-id>/<most-visited-id>"
+ // would be translated to:
+ // "chrome-search://large-icon/48/<most-visited-item-with-given-id>".
+ path_index_ = slash + 1;
+ url_string_ = path.substr(path_index_).as_string();
+ return true;
+}
diff --git a/components/favicon_base/large_icon_url_parser.h b/components/favicon_base/large_icon_url_parser.h
new file mode 100644
index 0000000..3d60b76
--- /dev/null
+++ b/components/favicon_base/large_icon_url_parser.h
@@ -0,0 +1,46 @@
+// Copyright 2015 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.
+
+#ifndef COMPONENTS_FAVICON_BASE_LARGE_ICON_URL_PARSER_H_
+#define COMPONENTS_FAVICON_BASE_LARGE_ICON_URL_PARSER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+
+// A parser for parameters to the chrome://large-icon/ host.
+class LargeIconUrlParser {
+ public:
+ LargeIconUrlParser();
+ ~LargeIconUrlParser();
+
+ std::string url_string() const { return url_string_; }
+
+ int size_in_pixels() const { return size_in_pixels_; }
+
+ size_t path_index() const { return path_index_; }
+
+ // Parses |path|, which should be in the format described at the top of the
+ // file "chrome/browser/ui/webui/large_icon_source.h". Note that |path| does
+ // not have leading '/'.
+ bool Parse(base::StringPiece path);
+
+ private:
+ friend class LargeIconUrlParserTest;
+
+ // The page URL string of the requested large icon.
+ std::string url_string_;
+
+ // The size of the requested large icon in pixels.
+ int size_in_pixels_;
+
+ // The index of the first character (relative to the path) where the the URL
+ // from which the large icon is being requested is located.
+ size_t path_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(LargeIconUrlParser);
+};
+
+#endif // COMPONENTS_FAVICON_BASE_LARGE_ICON_URL_PARSER_H_
diff --git a/components/favicon_base/large_icon_url_parser_unittest.cc b/components/favicon_base/large_icon_url_parser_unittest.cc
new file mode 100644
index 0000000..e6f52ec
--- /dev/null
+++ b/components/favicon_base/large_icon_url_parser_unittest.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 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.
+
+#include "components/favicon_base/large_icon_url_parser.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+const char kTestUrlStr[] = "https://www.google.ca/imghp?hl=en&tab=wi";
+
+} // namespace
+
+TEST(LargeIconUrlParserTest, ParseLargeIconPathSuccess) {
+ // Everything populated.
+ {
+ LargeIconUrlParser parser;
+ EXPECT_TRUE(parser.Parse(std::string("48/") + kTestUrlStr));
+ EXPECT_EQ(48, parser.size_in_pixels());
+ EXPECT_EQ(GURL(kTestUrlStr), GURL(parser.url_string()));
+ EXPECT_EQ(3U, parser.path_index());
+ }
+
+ // Empty URL.
+ {
+ LargeIconUrlParser parser;
+ EXPECT_TRUE(parser.Parse("48/"));
+ EXPECT_EQ(48, parser.size_in_pixels());
+ EXPECT_EQ(GURL(), GURL(parser.url_string()));
+ EXPECT_EQ(3U, parser.path_index());
+ }
+
+ // Tolerate invalid URL.
+ {
+ LargeIconUrlParser parser;
+ EXPECT_TRUE(parser.Parse("48/NOT A VALID URL"));
+ EXPECT_EQ(48, parser.size_in_pixels());
+ EXPECT_EQ("NOT A VALID URL", parser.url_string());
+ EXPECT_EQ(3U, parser.path_index());
+ }
+}
+
+TEST(LargeIconUrlParserTest, ParseLargeIconPathFailure) {
+ const char* const test_cases[] = {
+ "",
+ "/",
+ "//",
+ "48", // Missing '/'.
+ "/http://www.google.com/", // Missing size.
+ "not_a_number/http://www.google.com/", // Bad size.
+ "0/http://www.google.com/", // Non-positive size.
+ "-1/http://www.google.com/", // Non-positive size.
+ };
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ LargeIconUrlParser parser;
+ EXPECT_FALSE(parser.Parse(test_cases[i])) << "test_cases[" << i << "]";
+ }
+}