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 489bdd6320783e1899e75eb799cfd5c7f2470f1b..687ef4fa86b2f62e55995113d2f0502600fd979d 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" |
@@ -75,19 +81,25 @@ bool CreateSiteSpecificLogo(const gfx::ImageSkia& image, |
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())); |
@@ -95,6 +107,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. |
@@ -115,6 +129,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; |
@@ -142,31 +157,50 @@ bool GetPathToBackupLogo(const FilePath& logo_dir, |
return file_util::CopyFile(default_logo_path, *logo_path); |
} |
-} // namespace |
- |
-class MetroPinTabHelper::TaskRunner |
- : public base::RefCountedThreadSafe<TaskRunner> { |
+// The PinPageTaskRunner class performs the necessary FILE thread actions to |
+// pin a page, such as generating or copying the tile image file. When it |
+// has performed these actions it will send the tile creation request to the |
+// metro driver. |
+class PinPageTaskRunner : public base::RefCountedThreadSafe<PinPageTaskRunner> { |
public: |
- TaskRunner() {} |
+ // Creates a task runner for the pinning operation with the given details. |
+ // |favicon| can be a null image (i.e. favicon.isNull() can be true), in |
+ // which case the backup tile image will be used. |
+ PinPageTaskRunner(string16& title, string16& url, gfx::ImageSkia& favicon); |
- void PinPageToStartScreen(const string16& title, |
- const string16& url, |
- const gfx::ImageSkia& image); |
+ void Run(); |
+ void RunOnFileThread(); |
private: |
- ~TaskRunner() {} |
+ ~PinPageTaskRunner() {} |
+ |
+ // Details of the page being pinned. |
+ string16 title_; |
+ string16 url_; |
+ gfx::ImageSkia favicon_; |
- friend class base::RefCountedThreadSafe<TaskRunner>; |
- DISALLOW_COPY_AND_ASSIGN(TaskRunner); |
+ friend class base::RefCountedThreadSafe<PinPageTaskRunner>; |
+ DISALLOW_COPY_AND_ASSIGN(PinPageTaskRunner); |
}; |
-void MetroPinTabHelper::TaskRunner::PinPageToStartScreen( |
- const string16& title, |
- const string16& url, |
- const gfx::ImageSkia& image) { |
+PinPageTaskRunner::PinPageTaskRunner(string16& title, |
+ string16& url, |
+ gfx::ImageSkia& favicon) |
+ : title_(title), url_(url), favicon_(favicon) {} |
sky
2012/10/29 15:06:41
Since you wrapped the constructor each param on it
benwells
2012/10/29 23:20:44
Done.
|
+ |
+void PinPageTaskRunner::Run() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind(&PinPageTaskRunner::RunOnFileThread, this)); |
+} |
+ |
+void PinPageTaskRunner::RunOnFileThread() { |
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
- string16 tile_id = GenerateTileId(url); |
+ string16 tile_id = GenerateTileId(url_); |
FilePath logo_dir = GetTileImagesDir(); |
if (logo_dir.empty()) { |
LOG(ERROR) << "Could not create directory to store tile image."; |
@@ -174,7 +208,7 @@ void MetroPinTabHelper::TaskRunner::PinPageToStartScreen( |
} |
FilePath logo_path; |
- if (!CreateSiteSpecificLogo(image, tile_id, logo_dir, &logo_path) && |
+ if (!CreateSiteSpecificLogo(favicon_, tile_id, logo_dir, &logo_path) && |
!GetPathToBackupLogo(logo_dir, &logo_path)) { |
LOG(ERROR) << "Count not get path to logo tile."; |
return; |
@@ -194,15 +228,137 @@ void MetroPinTabHelper::TaskRunner::PinPageToStartScreen( |
return; |
} |
- VLOG(1) << __FUNCTION__ << " calling pin with title: " << title |
- << " and url: " << url; |
- metro_pin_to_start_screen(tile_id, title, url, logo_path); |
+ metro_pin_to_start_screen(tile_id, title_, url_, logo_path); |
+} |
+ |
+} // namespace |
+ |
+class MetroPinTabHelper::FaviconDownloader { |
+ public: |
+ FaviconDownloader(MetroPinTabHelper* helper, |
+ const string16& title, |
+ const string16& url, |
+ const gfx::ImageSkia& history_image); |
+ ~FaviconDownloader() {} |
+ |
+ void Start(content::RenderViewHost* host, |
+ const std::vector<FaviconURL>& candidates); |
+ |
+ // 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 |
+ // 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); |
+ |
+ private: |
+ // The tab helper that this downloader is operating for. |
+ MetroPinTabHelper* helper_; |
+ |
+ // Title and URL of the page being pinned. |
+ string16 title_; |
sky
2012/10/29 15:06:41
const title_ and url_
benwells
2012/10/29 23:20:44
Done.
|
+ string16 url_; |
+ |
+ // The best candidate we have so far for the current pin operation. |
+ gfx::ImageSkia best_candidate_; |
+ |
+ // Outstanding favicon download requests. |
+ std::set<int> in_progress_requests_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FaviconDownloader); |
+}; |
+ |
+MetroPinTabHelper::FaviconDownloader::FaviconDownloader( |
+ MetroPinTabHelper* helper, |
+ const string16& title, |
+ const string16& url, |
+ const gfx::ImageSkia& history_image) |
+ : helper_(helper), |
+ title_(title), |
+ url_(url), |
+ best_candidate_(history_image) {} |
+ |
+void MetroPinTabHelper::FaviconDownloader::Start( |
+ content::RenderViewHost* host, |
+ const std::vector<FaviconURL>& candidates) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ |
+ // If there are no candidate URLs, progress straight to pinning. |
+ if (candidates.empty()) { |
+ scoped_refptr<PinPageTaskRunner> runner( |
+ new PinPageTaskRunner(title_, url_, best_candidate_)); |
+ runner->Run(); |
+ helper_->FaviconDownloaderFinished(); |
+ return; |
+ } |
+ |
+ // Request all the candidates. |
+ int image_size = 0; // Request the full sized image. |
+ for (std::vector<FaviconURL>::const_iterator iter = candidates.begin(); |
+ iter != candidates.end(); |
+ ++iter) { |
+ in_progress_requests_.insert( |
+ FaviconUtil::DownloadFavicon(host, iter->icon_url, image_size)); |
+ } |
+} |
+ |
+void MetroPinTabHelper::FaviconDownloader::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<int>::iterator iter = in_progress_requests_.find(id); |
+ // Check that this request is one of ours. |
+ if (iter == in_progress_requests_.end()) |
+ return; |
+ |
+ in_progress_requests_.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()) { |
sky
2012/10/29 15:06:41
Might this result in an image bigger than kMaxIcon
benwells
2012/10/29 23:20:44
Oh yes, good catch.
|
+ 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 outstanding requests, pin the page on the FILE thread. |
+ // Once this happens this downloader has done its job, so delete it. |
+ if (in_progress_requests_.empty()) { |
+ scoped_refptr<PinPageTaskRunner> runner( |
+ new PinPageTaskRunner(title_, url_, best_candidate_)); |
+ runner->Run(); |
+ helper_->FaviconDownloaderFinished(); |
+ } |
} |
MetroPinTabHelper::MetroPinTabHelper(content::WebContents* web_contents) |
: content::WebContentsObserver(web_contents), |
- is_pinned_(false), |
- task_runner_(new TaskRunner) {} |
+ is_pinned_(false) {} |
MetroPinTabHelper::~MetroPinTabHelper() {} |
@@ -220,42 +376,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); |
+ gfx::ImageSkia favicon; |
FaviconTabHelper* favicon_tab_helper = FaviconTabHelper::FromWebContents( |
- tab_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)); |
- return; |
- } |
- |
- content::BrowserThread::PostTask( |
- content::BrowserThread::FILE, |
- FROM_HERE, |
- base::Bind(&TaskRunner::PinPageToStartScreen, |
- task_runner_, |
- title, |
- url_str, |
- gfx::ImageSkia())); |
+ web_contents()); |
+ if (favicon_tab_helper->FaviconIsValid()) |
+ favicon = favicon_tab_helper->GetFavicon().AsImageSkia().DeepCopy(); |
+ |
+ favicon_downloader_.reset(new FaviconDownloader(this, title, url_str, |
+ favicon)); |
+ favicon_downloader_->Start(web_contents()->GetRenderViewHost(), |
+ favicon_url_candidates_); |
} |
void MetroPinTabHelper::DidNavigateMainFrame( |
const content::LoadCommittedDetails& /*details*/, |
const content::FrameNavigateParams& /*params*/) { |
UpdatePinnedStateForCurrentURL(); |
+ // Cancel any outstanding pin operations once the user navigates away from |
+ // the page. |
+ if (favicon_downloader_.get()) |
+ favicon_downloader_.reset(); |
sky
2012/10/29 15:06:41
Does this mean if the user pins something and the
benwells
2012/10/29 23:20:44
I think it makes the most sense. The pin action co
|
+ // Any candidate favicons we have are now out of date so clear them. |
+ favicon_url_candidates_.clear(); |
+} |
+ |
+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) { |
+ favicon_url_candidates_ = candidates; |
+} |
+ |
+void MetroPinTabHelper::OnDidDownloadFavicon( |
+ int id, |
+ const GURL& image_url, |
+ bool errored, |
+ int requested_size, |
+ const std::vector<SkBitmap>& bitmaps) { |
+ if (favicon_downloader_.get()) |
+ favicon_downloader_->OnDidDownloadFavicon(id, image_url, errored, |
+ requested_size, bitmaps); |
} |
void MetroPinTabHelper::UpdatePinnedStateForCurrentURL() { |
@@ -275,8 +447,6 @@ void MetroPinTabHelper::UpdatePinnedStateForCurrentURL() { |
GURL url = web_contents()->GetURL(); |
string16 tile_id = GenerateTileId(UTF8ToUTF16(url.spec())); |
is_pinned_ = metro_is_pinned_to_start_screen(tile_id) != 0; |
- VLOG(1) << __FUNCTION__ << " with url " << UTF8ToUTF16(url.spec()) |
- << " result: " << is_pinned_; |
} |
void MetroPinTabHelper::UnPinPageFromStartScreen() { |
@@ -294,8 +464,10 @@ void MetroPinTabHelper::UnPinPageFromStartScreen() { |
} |
GURL url = web_contents()->GetURL(); |
- VLOG(1) << __FUNCTION__ << " calling unpin with url: " |
- << UTF8ToUTF16(url.spec()); |
string16 tile_id = GenerateTileId(UTF8ToUTF16(url.spec())); |
metro_un_pin_from_start_screen(tile_id); |
} |
+ |
+void MetroPinTabHelper::FaviconDownloaderFinished() { |
+ favicon_downloader_.reset(); |
+} |