| 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
|
| old mode 100644
|
| new mode 100755
|
| index 489bdd6320783e1899e75eb799cfd5c7f2470f1b..37a6b013e951fe1044515b1297c739c272279ed2
|
| --- 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,54 @@ 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(const string16& title,
|
| + const string16& url,
|
| + const 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.
|
| + const string16 title_;
|
| + const 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(const string16& title,
|
| + const string16& url,
|
| + const gfx::ImageSkia& favicon)
|
| + : title_(title),
|
| + url_(url),
|
| + favicon_(favicon) {}
|
| +
|
| +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 +212,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 +232,139 @@ 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.
|
| + const string16 title_;
|
| + const 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 the new bitmap is too big, ignore it.
|
| + if (iter->height() > kMaxIconSize || iter->width() > kMaxIconSize)
|
| + continue;
|
| +
|
| + // 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 it is smaller than our best one so far, ignore it.
|
| + if (iter->height() <= best_candidate_.height() ||
|
| + iter->width() <= best_candidate_.width()) {
|
| + continue;
|
| + }
|
| +
|
| + // Othewise it is our new best candidate.
|
| + 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 +382,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();
|
| + // 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 +453,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 +470,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();
|
| +}
|
|
|