| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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. | |
| 4 | |
| 5 #include "chrome/browser/manifest/manifest_icon_downloader.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <limits> | |
| 10 | |
| 11 #include "chrome/browser/manifest/manifest_icon_selector.h" | |
| 12 #include "content/public/browser/browser_thread.h" | |
| 13 #include "content/public/browser/render_frame_host.h" | |
| 14 #include "content/public/browser/web_contents.h" | |
| 15 #include "content/public/browser/web_contents_observer.h" | |
| 16 #include "content/public/common/console_message_level.h" | |
| 17 #include "skia/ext/image_operations.h" | |
| 18 | |
| 19 // DevToolsConsoleHelper is a class that holds a WebContents in order to be able | |
| 20 // to send a message to the WebContents' main frame. It is used so | |
| 21 // ManifestIconDownloader and the callers do not have to worry about | |
| 22 // |web_contents| lifetime. If the |web_contents| is invalidated before the | |
| 23 // message can be sent, the message will simply be ignored. | |
| 24 class ManifestIconDownloader::DevToolsConsoleHelper | |
| 25 : public content::WebContentsObserver { | |
| 26 public: | |
| 27 explicit DevToolsConsoleHelper(content::WebContents* web_contents); | |
| 28 ~DevToolsConsoleHelper() override = default; | |
| 29 | |
| 30 void AddMessage(content::ConsoleMessageLevel level, | |
| 31 const std::string& message); | |
| 32 }; | |
| 33 | |
| 34 ManifestIconDownloader::DevToolsConsoleHelper::DevToolsConsoleHelper( | |
| 35 content::WebContents* web_contents) | |
| 36 : WebContentsObserver(web_contents) { | |
| 37 } | |
| 38 | |
| 39 void ManifestIconDownloader::DevToolsConsoleHelper::AddMessage( | |
| 40 content::ConsoleMessageLevel level, | |
| 41 const std::string& message) { | |
| 42 if (!web_contents()) | |
| 43 return; | |
| 44 web_contents()->GetMainFrame()->AddMessageToConsole(level, message); | |
| 45 } | |
| 46 | |
| 47 bool ManifestIconDownloader::Download( | |
| 48 content::WebContents* web_contents, | |
| 49 const GURL& icon_url, | |
| 50 int ideal_icon_size_in_px, | |
| 51 int minimum_icon_size_in_px, | |
| 52 const ManifestIconDownloader::IconFetchCallback& callback) { | |
| 53 DCHECK(minimum_icon_size_in_px <= ideal_icon_size_in_px); | |
| 54 if (!web_contents || !icon_url.is_valid()) | |
| 55 return false; | |
| 56 | |
| 57 web_contents->DownloadImage( | |
| 58 icon_url, | |
| 59 false, // is_favicon | |
| 60 0, // max_bitmap_size - 0 means no maximum size. | |
| 61 false, // bypass_cache | |
| 62 base::Bind(&ManifestIconDownloader::OnIconFetched, | |
| 63 ideal_icon_size_in_px, | |
| 64 minimum_icon_size_in_px, | |
| 65 base::Owned(new DevToolsConsoleHelper(web_contents)), | |
| 66 callback)); | |
| 67 return true; | |
| 68 } | |
| 69 | |
| 70 void ManifestIconDownloader::OnIconFetched( | |
| 71 int ideal_icon_size_in_px, | |
| 72 int minimum_icon_size_in_px, | |
| 73 DevToolsConsoleHelper* console_helper, | |
| 74 const ManifestIconDownloader::IconFetchCallback& callback, | |
| 75 int id, | |
| 76 int http_status_code, | |
| 77 const GURL& url, | |
| 78 const std::vector<SkBitmap>& bitmaps, | |
| 79 const std::vector<gfx::Size>& sizes) { | |
| 80 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 81 | |
| 82 if (bitmaps.empty()) { | |
| 83 console_helper->AddMessage( | |
| 84 content::CONSOLE_MESSAGE_LEVEL_ERROR, | |
| 85 "Error while trying to use the following icon from the Manifest: " | |
| 86 + url.spec() + " (Download error or resource isn't a valid image)"); | |
| 87 | |
| 88 callback.Run(SkBitmap()); | |
| 89 return; | |
| 90 } | |
| 91 | |
| 92 const int closest_index = FindClosestBitmapIndex( | |
| 93 ideal_icon_size_in_px, minimum_icon_size_in_px, bitmaps); | |
| 94 | |
| 95 if (closest_index == -1) { | |
| 96 console_helper->AddMessage( | |
| 97 content::CONSOLE_MESSAGE_LEVEL_ERROR, | |
| 98 "Error while trying to use the following icon from the Manifest: " | |
| 99 + url.spec() | |
| 100 + " (Resource size is not correct - typo in the Manifest?)"); | |
| 101 | |
| 102 callback.Run(SkBitmap()); | |
| 103 return; | |
| 104 } | |
| 105 | |
| 106 const SkBitmap& chosen = bitmaps[closest_index]; | |
| 107 | |
| 108 // Only scale if we need to scale down. For scaling up we will let the system | |
| 109 // handle that when it is required to display it. This saves space in the | |
| 110 // webapp storage system as well. | |
| 111 if (chosen.height() > ideal_icon_size_in_px || | |
| 112 chosen.width() > ideal_icon_size_in_px) { | |
| 113 content::BrowserThread::PostTask( | |
| 114 content::BrowserThread::IO, FROM_HERE, | |
| 115 base::BindOnce(&ManifestIconDownloader::ScaleIcon, | |
| 116 ideal_icon_size_in_px, chosen, callback)); | |
| 117 return; | |
| 118 } | |
| 119 | |
| 120 callback.Run(chosen); | |
| 121 } | |
| 122 | |
| 123 void ManifestIconDownloader::ScaleIcon( | |
| 124 int ideal_icon_size_in_px, | |
| 125 const SkBitmap& bitmap, | |
| 126 const ManifestIconDownloader::IconFetchCallback& callback) { | |
| 127 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 128 | |
| 129 const SkBitmap& scaled = skia::ImageOperations::Resize( | |
| 130 bitmap, | |
| 131 skia::ImageOperations::RESIZE_BEST, | |
| 132 ideal_icon_size_in_px, | |
| 133 ideal_icon_size_in_px); | |
| 134 | |
| 135 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | |
| 136 base::BindOnce(callback, scaled)); | |
| 137 } | |
| 138 | |
| 139 int ManifestIconDownloader::FindClosestBitmapIndex( | |
| 140 int ideal_icon_size_in_px, | |
| 141 int minimum_icon_size_in_px, | |
| 142 const std::vector<SkBitmap>& bitmaps) { | |
| 143 int best_index = -1; | |
| 144 int best_delta = std::numeric_limits<int>::min(); | |
| 145 const int max_negative_delta = | |
| 146 minimum_icon_size_in_px - ideal_icon_size_in_px; | |
| 147 | |
| 148 for (size_t i = 0; i < bitmaps.size(); ++i) { | |
| 149 if (bitmaps[i].height() != bitmaps[i].width()) | |
| 150 continue; | |
| 151 | |
| 152 int delta = bitmaps[i].width() - ideal_icon_size_in_px; | |
| 153 if (delta == 0) | |
| 154 return i; | |
| 155 | |
| 156 if (best_delta > 0 && delta < 0) | |
| 157 continue; | |
| 158 | |
| 159 if ((best_delta > 0 && delta < best_delta) || | |
| 160 (best_delta < 0 && delta > best_delta && delta >= max_negative_delta)) { | |
| 161 best_index = i; | |
| 162 best_delta = delta; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 if (best_index != -1) | |
| 167 return best_index; | |
| 168 | |
| 169 // There was no square icon of a correct size found. Try to find the most | |
| 170 // square-like icon which has both dimensions greater than the minimum size. | |
| 171 float best_ratio_difference = std::numeric_limits<float>::infinity(); | |
| 172 for (size_t i = 0; i < bitmaps.size(); ++i) { | |
| 173 if (bitmaps[i].height() < minimum_icon_size_in_px || | |
| 174 bitmaps[i].width() < minimum_icon_size_in_px) { | |
| 175 continue; | |
| 176 } | |
| 177 | |
| 178 float height = static_cast<float>(bitmaps[i].height()); | |
| 179 float width = static_cast<float>(bitmaps[i].width()); | |
| 180 float ratio = height / width; | |
| 181 float ratio_difference = fabs(ratio - 1); | |
| 182 if (ratio_difference < best_ratio_difference) { | |
| 183 best_index = i; | |
| 184 best_ratio_difference = ratio_difference; | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 return best_index; | |
| 189 } | |
| OLD | NEW |