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. 4 5 #include "chrome/browser/ui/ash/launcher/launcher_favicon_loader.h" 6 7 #include "base/logging.h" 8 #include "base/memory/weak_ptr.h" 9 #include "chrome/browser/ui/ash/launcher/browser_launcher_item_controller.h" 10 #include "content/public/browser/render_view_host.h" 11 #include "content/public/browser/web_contents.h" 12 #include "content/public/browser/web_contents_observer.h" 13 #include "third_party/skia/include/core/SkBitmap.h" 14 #include "url/gurl.h" 15 16 namespace internal { 17 18 const int kMaxBitmapSize = 256; 19 20 //////////////////////////////////////////////////////////////////////////////// 21 // FaviconBitmapHandler fetchs all bitmaps with the 'icon' (or 'shortcut icon') 22 // link tag, storing the one that best matches ash::kLauncherPreferredSize. 23 // These icon bitmaps are not resized and are not cached beyond the lifetime 24 // of the class. Bitmaps larger than kMaxBitmapSize are ignored. 25 26 class FaviconBitmapHandler : public content::WebContentsObserver { 27 public: 28 FaviconBitmapHandler(content::WebContents* web_contents, 29 LauncherFaviconLoader::Delegate* delegate) 30 : content::WebContentsObserver(web_contents), 31 delegate_(delegate), 32 web_contents_(web_contents), 33 weak_ptr_factory_(this) { 34 } 35 36 virtual ~FaviconBitmapHandler() {} 37 38 const SkBitmap& bitmap() const { return bitmap_; } 39 40 bool HasPendingDownloads() const; 41 42 // content::WebContentObserver implementation. 43 virtual void DidUpdateFaviconURL( 44 int32 page_id, 45 const std::vector<content::FaviconURL>& candidates) OVERRIDE; 46 47 private: 48 void DidDownloadFavicon( 49 int id, 50 int http_status_code, 51 const GURL& image_url, 52 int requested_size, 53 const std::vector<SkBitmap>& bitmaps); 54 55 void AddFavicon(const GURL& image_url, const SkBitmap& new_bitmap); 56 57 LauncherFaviconLoader::Delegate* delegate_; 58 59 content::WebContents* web_contents_; 60 61 typedef std::set<GURL> UrlSet; 62 // Map of pending download urls. 63 UrlSet pending_requests_; 64 // Map of processed urls. 65 UrlSet processed_requests_; 66 // Current bitmap and source url. 67 SkBitmap bitmap_; 68 GURL bitmap_url_; 69 70 base::WeakPtrFactory<FaviconBitmapHandler> weak_ptr_factory_; 71 72 DISALLOW_COPY_AND_ASSIGN(FaviconBitmapHandler); 73 }; 74 75 void FaviconBitmapHandler::DidUpdateFaviconURL( 76 int32 page_id, 77 const std::vector<content::FaviconURL>& candidates) { 78 // This function receives a complete list of faviocn urls for the page. 79 // It may get called multiple times with the same list, and will also get 80 // called any time an item is added or removed. As such, we track processed 81 // and pending urls, but only until they are removed from the list. 82 UrlSet new_pending, new_processed; 83 // Create a map of valid favicon urls. 84 std::set<GURL> urls; 85 std::vector<content::FaviconURL>::const_iterator iter; 86 for (iter = candidates.begin(); iter != candidates.end(); ++iter) { 87 if (iter->icon_type != content::FaviconURL::FAVICON) 88 continue; 89 const GURL& url = iter->icon_url; 90 if (url.is_valid()) 91 urls.insert(url); 92 // Preserve matching pending requests amd processed requests. 93 if (pending_requests_.find(url) != pending_requests_.end()) 94 new_pending.insert(url); 95 if (processed_requests_.find(url) != processed_requests_.end()) 96 new_processed.insert(url); 97 } 98 pending_requests_ = new_pending; 99 processed_requests_ = new_processed; 100 // Reset bitmap_ if no longer valid (i.e. not in the list of urls). 101 if (urls.find(bitmap_url_) == urls.end()) { 102 bitmap_url_ = GURL(); 103 bitmap_.reset(); 104 } 105 // Request any new urls. 106 for (std::set<GURL>::iterator iter = urls.begin(); 107 iter != urls.end(); ++iter) { 108 if (processed_requests_.find(*iter) != processed_requests_.end()) 109 continue; // Skip already processed downloads. 110 if (pending_requests_.find(*iter) != pending_requests_.end()) 111 continue; // Skip already pending downloads. 112 pending_requests_.insert(*iter); 113 web_contents_->DownloadImage( 114 *iter, 115 true, // is a favicon 116 0, // no preferred size 117 0, // no maximum size 118 base::Bind(&FaviconBitmapHandler::DidDownloadFavicon, 119 weak_ptr_factory_.GetWeakPtr())); 120 } 121 } 122 123 bool FaviconBitmapHandler::HasPendingDownloads() const { 124 return !pending_requests_.empty(); 125 } 126 127 void FaviconBitmapHandler::DidDownloadFavicon( 128 int id, 129 int http_status_code, 130 const GURL& image_url, 131 int requested_size, 132 const std::vector<SkBitmap>& bitmaps) { 133 UrlSet::iterator iter = pending_requests_.find(image_url); 134 if (iter == pending_requests_.end()) { 135 // Updates are received for all downloads; ignore unrequested urls. 136 return; 137 } 138 pending_requests_.erase(iter); 139 140 // Favicon bitmaps are ordered by decreasing width. 141 if (!bitmaps.empty()) 142 AddFavicon(image_url, bitmaps[0]); 143 } 144 145 void FaviconBitmapHandler::AddFavicon(const GURL& image_url, 146 const SkBitmap& new_bitmap) { 147 processed_requests_.insert(image_url); 148 if (new_bitmap.height() > kMaxBitmapSize || 149 new_bitmap.width() > kMaxBitmapSize) 150 return; 151 if (new_bitmap.height() < ash::kLauncherPreferredSize) 152 return; 153 if (!bitmap_.isNull()) { 154 // We want the smallest icon that is large enough. 155 if (new_bitmap.height() > bitmap_.height()) 156 return; 157 } 158 bitmap_url_ = image_url; 159 bitmap_ = new_bitmap; 160 delegate_->FaviconUpdated(); 161 } 162 163 } // namespace internal 164 165 //////////////////////////////////////////////////////////////////////////////// 166 167 LauncherFaviconLoader::LauncherFaviconLoader(Delegate* delegate, 168 content::WebContents* web_contents) 169 : web_contents_(web_contents) { 170 favicon_handler_.reset( 171 new internal::FaviconBitmapHandler(web_contents, delegate)); 172 } 173 174 LauncherFaviconLoader::~LauncherFaviconLoader() { 175 } 176 177 SkBitmap LauncherFaviconLoader::GetFavicon() const { 178 return favicon_handler_->bitmap(); 179 } 180 181 bool LauncherFaviconLoader::HasPendingDownloads() const { 182 return favicon_handler_->HasPendingDownloads(); 183 } 184