Chromium Code Reviews| 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() { |