Index: chrome/browser/ui/metro_pin_tab_helper_win.cc |
diff --git a/chrome/browser/ui/metro_pin_tab_helper_win.cc b/chrome/browser/ui/metro_pin_tab_helper_win.cc |
index e3fa0faca50c4e6c55fb4dc3e6da586762f18cd9..44ddcc3f13e2a9448805aa12d182226babbdfc7f 100644 |
--- a/chrome/browser/ui/metro_pin_tab_helper_win.cc |
+++ b/chrome/browser/ui/metro_pin_tab_helper_win.cc |
@@ -4,6 +4,8 @@ |
#include "chrome/browser/ui/metro_pin_tab_helper_win.h" |
+#include <set> |
+ |
#include "base/base_paths.h" |
#include "base/bind.h" |
#include "base/file_path.h" |
@@ -16,11 +18,15 @@ |
#include "base/utf_string_conversions.h" |
#include "base/win/metro.h" |
#include "chrome/browser/favicon/favicon_tab_helper.h" |
+#include "chrome/browser/favicon/favicon_util.h" |
#include "chrome/browser/ui/tab_contents/tab_contents.h" |
#include "chrome/common/chrome_paths.h" |
+#include "chrome/common/icon_messages.h" |
#include "content/public/browser/browser_thread.h" |
#include "content/public/browser/web_contents.h" |
#include "crypto/sha2.h" |
+#include "third_party/skia/include/core/SkCanvas.h" |
+#include "third_party/skia/include/core/SkColor.h" |
#include "ui/gfx/canvas.h" |
#include "ui/gfx/codec/png_codec.h" |
#include "ui/gfx/color_analysis.h" |
@@ -68,24 +74,30 @@ bool CreateSiteSpecificLogo(const gfx::ImageSkia& image, |
const int kCaptionHeight = 20; |
const double kBoxFade = 0.75; |
const int kColorMeanDarknessLimit = 100; |
- const int kColorMeanLightnessLimit = 100; |
+ const int kColorMeanLightnessLimit = 650; |
if (image.isNull()) |
return false; |
- *logo_path = logo_dir.Append(tile_id).ReplaceExtension(L".png"); |
- |
- // Use a canvas to paint the tile logo. |
- gfx::Canvas canvas(gfx::Size(kLogoWidth, kLogoHeight), ui::SCALE_FACTOR_100P, |
- true); |
+ // First paint the image onto an opaque background to get rid of transparency. |
+ // White is used as it will be disregarded in the mean calculation because of |
+ // lightness limit. |
+ SkPaint paint; |
+ paint.setColor(SK_ColorWHITE); |
+ gfx::Canvas favicon_canvas(gfx::Size(image.width(), image.height()), |
+ ui::SCALE_FACTOR_100P, true); |
+ favicon_canvas.DrawRect(gfx::Rect(0, 0, image.width(), image.height()), |
+ paint); |
+ favicon_canvas.DrawImageInt(image, 0, 0); |
// Fill the tile logo with the average color from bitmap. To do this we need |
// to work out the 'average color' which is calculated using PNG encoded data |
// of the bitmap. |
- SkPaint paint; |
std::vector<unsigned char> icon_png; |
- if (!gfx::PNGCodec::EncodeBGRASkBitmap(*image.bitmap(), true, &icon_png)) |
+ if (!gfx::PNGCodec::EncodeBGRASkBitmap( |
+ favicon_canvas.ExtractImageRep().sk_bitmap(), false, &icon_png)) { |
return false; |
+ } |
scoped_refptr<base::RefCountedStaticMemory> icon_mem( |
new base::RefCountedStaticMemory(&icon_png.front(), icon_png.size())); |
@@ -93,6 +105,8 @@ bool CreateSiteSpecificLogo(const gfx::ImageSkia& image, |
SkColor mean_color = color_utils::CalculateKMeanColorOfPNG( |
icon_mem, kColorMeanDarknessLimit, kColorMeanLightnessLimit, sampler); |
paint.setColor(mean_color); |
+ gfx::Canvas canvas(gfx::Size(kLogoWidth, kLogoHeight), ui::SCALE_FACTOR_100P, |
+ true); |
canvas.DrawRect(gfx::Rect(0, 0, kLogoWidth, kLogoHeight), paint); |
// Now paint a faded square for the favicon to go in. |
@@ -113,6 +127,7 @@ bool CreateSiteSpecificLogo(const gfx::ImageSkia& image, |
if (!gfx::PNGCodec::EncodeBGRASkBitmap(logo_bitmap, true, &logo_png)) |
return false; |
+ *logo_path = logo_dir.Append(tile_id).ReplaceExtension(L".png"); |
return file_util::WriteFile(*logo_path, |
reinterpret_cast<char*>(&logo_png[0]), |
logo_png.size()) > 0; |
@@ -140,23 +155,178 @@ bool GetPathToBackupLogo(const FilePath& logo_dir, |
} // namespace |
-class MetroPinTabHelper::TaskRunner |
- : public base::RefCountedThreadSafe<TaskRunner> { |
+class MetroPinTabHelper::PagePinner |
sky
2012/10/26 20:01:15
How come this is ref counted?
benwells
2012/10/29 06:38:41
This class is now split into two. One half is not
|
+ : public base::RefCountedThreadSafe<PagePinner> { |
public: |
- TaskRunner() {} |
- |
+ PagePinner() {} |
+ |
+ // Updated when we have a new set of candidate favicon URLs for the current |
+ // page. |
+ void SetFaviconCandidates(const std::vector<FaviconURL>& candidates); |
+ |
+ // This will clear any information we have about the current page's favicon, |
+ // and also cancel any pin requests that are still having favicons downloaded. |
+ void ClearCurrentPageInfo(); |
sky
2012/10/26 20:01:15
Why do we need this?
benwells
2012/10/29 06:38:41
This still exists, kind of, but is now in MetroPin
|
+ |
+ // Start downloading favicons from the candidate URLs, and then proceed on |
+ // to create a secondary tile. |history_image| is the favicon we get from |
+ // the history service. It is too small so we try to get a bigger one |
+ // directly. |
+ void StartDownloadingFavicons(content::RenderViewHost* host, |
+ const string16& title, |
+ const string16& url, |
+ const gfx::ImageSkia& history_image); |
+ |
+ // Callback for when a favicon has been downloaded. The best bitmap so far |
+ // will be stored in best_candidate_. If this is the last URL that was being |
sky
2012/10/26 20:01:15
|s around references to parameters and fields.
benwells
2012/10/29 06:38:41
Done.
|
+ // downloaded, the page is pinned by calling PinPageToStartScreen on the FILE |
+ // thread. |
+ void OnDidDownloadFavicon(int id, |
+ const GURL& image_url, |
+ bool errored, |
+ int requested_size, |
+ const std::vector<SkBitmap>& bitmaps); |
+ |
+ // We now have found the best favicon we can, so create the tile. This is |
+ // executed on the FILE thread. |
void PinPageToStartScreen(const string16& title, |
const string16& url, |
const gfx::ImageSkia& image); |
private: |
- ~TaskRunner() {} |
+ ~PagePinner() {} |
+ |
+ // The best candidate we have so far for the current pin operation. |
+ gfx::ImageSkia best_candidate_; |
+ |
+ // Title and URL of the page being pinned. |
+ string16 title_; |
+ string16 url_; |
- friend class base::RefCountedThreadSafe<TaskRunner>; |
- DISALLOW_COPY_AND_ASSIGN(TaskRunner); |
+ // Candidate Favicon URLs for the current page. |
+ std::vector<FaviconURL> favicon_url_candidates_; |
+ |
+ // Favicon URLs that are being downloaded. While this is not empty there |
+ // is a pin request being processed, if any others are started they will |
+ // be ignored. |
+ std::set<GURL> in_progress_urls_; |
+ |
+ friend class base::RefCountedThreadSafe<PagePinner>; |
+ DISALLOW_COPY_AND_ASSIGN(PagePinner); |
}; |
-void MetroPinTabHelper::TaskRunner::PinPageToStartScreen( |
+void MetroPinTabHelper::PagePinner::SetFaviconCandidates( |
+ const std::vector<FaviconURL>& candidates) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ favicon_url_candidates_ = candidates; |
+} |
+ |
+void MetroPinTabHelper::PagePinner::ClearCurrentPageInfo() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ favicon_url_candidates_.clear(); |
+ in_progress_urls_.clear(); |
+} |
+ |
+void MetroPinTabHelper::PagePinner::StartDownloadingFavicons( |
+ content::RenderViewHost* host, |
+ const string16& title, |
+ const string16& url, |
+ const gfx::ImageSkia& history_image) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ // If the PagePinner is already pinning the current page, ignore any more |
+ // requests. |
+ if (!in_progress_urls_.empty()) |
sky
2012/10/26 20:01:15
Should we create a pinner per url? Seems like it w
benwells
2012/10/29 06:38:41
I've separated out the logic into a MetroPinTabHel
|
+ return; |
+ |
+ // If there are no candidate URLs, progress straight to pinning. |
+ if (favicon_url_candidates_.empty()) { |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind(&PagePinner::PinPageToStartScreen, |
+ this, |
+ title, |
+ url, |
+ history_image)); |
+ return; |
+ } |
+ |
+ best_candidate_ = history_image; |
+ title_ = title; |
+ url_ = url; |
+ |
+ // Request all the candidates. |
+ int image_size = 0; // Request the full sized image. |
+ for (std::vector<FaviconURL>::const_iterator iter = |
+ favicon_url_candidates_.begin(); |
+ iter != favicon_url_candidates_.end(); |
+ ++iter) { |
+ // Ignore any duplicate URLs in the candidates. |
+ if (in_progress_urls_.find(iter->icon_url) != in_progress_urls_.end()) |
+ continue; |
+ |
+ in_progress_urls_.insert(iter->icon_url); |
+ FaviconUtil::DownloadFavicon(host, iter->icon_url, image_size); |
sky
2012/10/26 20:01:15
You need to cache the return value and compare tha
benwells
2012/10/29 06:38:41
Done. Yeah I was checking the URLs when they come
|
+ } |
+} |
+ |
+void MetroPinTabHelper::PagePinner::OnDidDownloadFavicon( |
+ int id, |
+ const GURL& image_url, |
+ bool errored, |
+ int requested_size, |
+ const std::vector<SkBitmap>& bitmaps) { |
+ const int kMaxIconSize = 32; |
+ |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ std::set<GURL>::iterator iter = in_progress_urls_.find(image_url); |
+ // If this URL isn't in the in progress list, the page must have been |
+ // navigated since the request started, cancelling this request. |
+ if (iter == in_progress_urls_.end()) |
+ return; |
+ |
+ in_progress_urls_.erase(iter); |
+ |
+ // Process the bitmaps, keeping the one that is best so far. |
+ if (!errored) { |
+ for (std::vector<SkBitmap>::const_iterator iter = bitmaps.begin(); |
+ iter != bitmaps.end(); |
+ ++iter) { |
+ |
+ // If we don't have a best candidate yet, this is better so just grab it. |
+ if (best_candidate_.isNull()) { |
+ best_candidate_ = gfx::ImageSkia(*iter).DeepCopy(); |
+ continue; |
+ } |
+ // If the new bitmap is bigger than the best candidate, and not greater |
+ // than the max size, grab it. |
+ if (iter->height() > kMaxIconSize || iter->width() > kMaxIconSize) |
+ continue; |
+ |
+ if (iter->height() <= best_candidate_.height() || |
+ iter->width() <= best_candidate_.width()) { |
+ continue; |
+ } |
+ |
+ best_candidate_ = gfx::ImageSkia(*iter).DeepCopy(); |
+ } |
+ } |
+ |
+ // If there are no more URLs to download, pin the page on the FILE thread. |
+ if (in_progress_urls_.empty()) { |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind(&PagePinner::PinPageToStartScreen, |
+ this, |
+ title_, |
+ url_, |
+ best_candidate_)); |
+ return; |
+ } |
+} |
+ |
+void MetroPinTabHelper::PagePinner::PinPageToStartScreen( |
const string16& title, |
const string16& url, |
const gfx::ImageSkia& image) { |
@@ -198,7 +368,7 @@ void MetroPinTabHelper::TaskRunner::PinPageToStartScreen( |
MetroPinTabHelper::MetroPinTabHelper(content::WebContents* web_contents) |
: content::WebContentsObserver(web_contents), |
is_pinned_(false), |
- task_runner_(new TaskRunner) {} |
+ page_pinner_(new PagePinner) {} |
MetroPinTabHelper::~MetroPinTabHelper() {} |
@@ -216,42 +386,58 @@ void MetroPinTabHelper::TogglePinnedToStartScreen() { |
return; |
} |
- // TODO(benwells): Handle downloading a larger favicon if there is one. |
GURL url = web_contents()->GetURL(); |
string16 url_str = UTF8ToUTF16(url.spec()); |
string16 title = web_contents()->GetTitle(); |
- TabContents* tab_contents = TabContents::FromWebContents(web_contents()); |
- DCHECK(tab_contents); |
FaviconTabHelper* favicon_tab_helper = FaviconTabHelper::FromWebContents( |
- tab_contents->web_contents()); |
+ web_contents()); |
if (favicon_tab_helper->FaviconIsValid()) { |
gfx::Image favicon = favicon_tab_helper->GetFavicon(); |
gfx::ImageSkia favicon_skia = favicon.AsImageSkia().DeepCopy(); |
- content::BrowserThread::PostTask( |
- content::BrowserThread::FILE, |
- FROM_HERE, |
- base::Bind(&TaskRunner::PinPageToStartScreen, |
- task_runner_, |
- title, |
- url_str, |
- favicon_skia)); |
+ page_pinner_->StartDownloadingFavicons(web_contents()->GetRenderViewHost(), |
+ title, |
+ url_str, |
+ favicon_skia); |
return; |
} |
- content::BrowserThread::PostTask( |
- content::BrowserThread::FILE, |
- FROM_HERE, |
- base::Bind(&TaskRunner::PinPageToStartScreen, |
- task_runner_, |
- title, |
- url_str, |
- gfx::ImageSkia())); |
+ page_pinner_->StartDownloadingFavicons(web_contents()->GetRenderViewHost(), |
+ title, |
+ url_str, |
+ gfx::ImageSkia()); |
} |
void MetroPinTabHelper::DidNavigateMainFrame( |
const content::LoadCommittedDetails& /*details*/, |
const content::FrameNavigateParams& /*params*/) { |
UpdatePinnedStateForCurrentURL(); |
+ page_pinner_->ClearCurrentPageInfo(); |
+} |
+ |
+bool MetroPinTabHelper::OnMessageReceived(const IPC::Message& message) { |
+ bool message_handled = false; // Allow other handlers to receive these. |
+ IPC_BEGIN_MESSAGE_MAP(MetroPinTabHelper, message) |
+ IPC_MESSAGE_HANDLER(IconHostMsg_UpdateFaviconURL, OnUpdateFaviconURL) |
+ IPC_MESSAGE_HANDLER(IconHostMsg_DidDownloadFavicon, OnDidDownloadFavicon) |
+ IPC_MESSAGE_UNHANDLED(message_handled = false) |
+ IPC_END_MESSAGE_MAP() |
+ return message_handled; |
+} |
+ |
+void MetroPinTabHelper::OnUpdateFaviconURL( |
+ int32 page_id, |
+ const std::vector<FaviconURL>& candidates) { |
+ page_pinner_->SetFaviconCandidates(candidates); |
+} |
+ |
+void MetroPinTabHelper::OnDidDownloadFavicon( |
+ int id, |
+ const GURL& image_url, |
+ bool errored, |
+ int requested_size, |
+ const std::vector<SkBitmap>& bitmaps) { |
+ page_pinner_->OnDidDownloadFavicon(id, image_url, errored, requested_size, |
+ bitmaps); |
} |
void MetroPinTabHelper::UpdatePinnedStateForCurrentURL() { |