| Index: chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
|
| diff --git a/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc b/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
|
| index f2a30904843e2b96f4b4c1fe0af5c19a4f9fc5a2..a72265740d40882db3c68dbef3b491d1dd538fff 100644
|
| --- a/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
|
| +++ b/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
|
| @@ -18,7 +18,10 @@
|
| #include "base/threading/sequenced_worker_pool.h"
|
| #include "chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h"
|
| #include "content/public/browser/browser_thread.h"
|
| +#include "third_party/skia/include/core/SkBitmap.h"
|
| +#include "third_party/skia/include/core/SkCanvas.h"
|
| #include "ui/base/models/menu_model.h"
|
| +#include "ui/gfx/codec/png_codec.h"
|
| #include "ui/gfx/image/image.h"
|
| #include "ui/gfx/image/image_skia.h"
|
|
|
| @@ -134,92 +137,14 @@ void EnsureMethodsLoaded() {
|
| dlsym(indicator_lib, "app_indicator_set_icon_theme_path"));
|
| }
|
|
|
| -// Returns whether a temporary directory should be created for each app
|
| -// indicator image.
|
| -bool ShouldCreateTempDirectoryPerImage(bool using_kde4) {
|
| - // Create a new temporary directory for each image on Unity since using a
|
| - // single temporary directory seems to have issues when changing icons in
|
| - // quick succession.
|
| - return !using_kde4;
|
| -}
|
| -
|
| -// Returns the subdirectory of |temp_dir| in which the app indicator image
|
| -// should be saved.
|
| -base::FilePath GetImageDirectoryPath(bool using_kde4,
|
| - const base::FilePath& temp_dir) {
|
| - // On KDE4, an image located in a directory ending with
|
| - // "icons/hicolor/16x16/apps" can be used as the app indicator image because
|
| - // "/usr/share/icons/hicolor/16x16/apps" exists.
|
| - return using_kde4 ?
|
| - temp_dir.AppendASCII("icons").AppendASCII("hicolor").AppendASCII("16x16").
|
| - AppendASCII("apps") :
|
| - temp_dir;
|
| -}
|
| -
|
| -std::string GetImageFileNameForKDE4(
|
| - const scoped_refptr<base::RefCountedMemory>& png_data) {
|
| - // On KDE4, the name of the image file for each different looking bitmap must
|
| - // be unique. It must also be unique across runs of Chrome.
|
| - base::MD5Digest digest;
|
| - base::MD5Sum(png_data->front_as<char>(), png_data->size(), &digest);
|
| - return base::StringPrintf("chrome_app_indicator_%s.png",
|
| - base::MD5DigestToBase16(digest).c_str());
|
| -}
|
| -
|
| -std::string GetImageFileNameForNonKDE4(int icon_change_count,
|
| - const std::string& id) {
|
| - return base::StringPrintf("%s_%d.png", id.c_str(), icon_change_count);
|
| -}
|
| -
|
| -// Returns the "icon theme path" given the file path of the app indicator image.
|
| -std::string GetIconThemePath(bool using_kde4,
|
| - const base::FilePath& image_path) {
|
| - return using_kde4 ?
|
| - image_path.DirName().DirName().DirName().DirName().value() :
|
| - image_path.DirName().value();
|
| -}
|
| -
|
| -base::FilePath CreateTempImageFile(bool using_kde4,
|
| - gfx::ImageSkia* image_ptr,
|
| - int icon_change_count,
|
| - std::string id,
|
| - const base::FilePath& previous_file_path) {
|
| - scoped_ptr<gfx::ImageSkia> image(image_ptr);
|
| -
|
| - scoped_refptr<base::RefCountedMemory> png_data =
|
| - gfx::Image(*image.get()).As1xPNGBytes();
|
| - if (png_data->size() == 0) {
|
| - // If the bitmap could not be encoded to PNG format, skip it.
|
| - LOG(WARNING) << "Could not encode icon";
|
| - return base::FilePath();
|
| - }
|
| -
|
| - base::FilePath new_file_path;
|
| - if (previous_file_path.empty() ||
|
| - ShouldCreateTempDirectoryPerImage(using_kde4)) {
|
| - base::FilePath tmp_dir;
|
| - if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &tmp_dir))
|
| - return base::FilePath();
|
| - new_file_path = GetImageDirectoryPath(using_kde4, tmp_dir);
|
| - if (new_file_path != tmp_dir) {
|
| - if (!base::CreateDirectory(new_file_path))
|
| - return base::FilePath();
|
| - }
|
| - } else {
|
| - new_file_path = previous_file_path.DirName();
|
| - }
|
| -
|
| - new_file_path = new_file_path.Append(using_kde4 ?
|
| - GetImageFileNameForKDE4(png_data) :
|
| - GetImageFileNameForNonKDE4(icon_change_count, id));
|
| -
|
| - int bytes_written =
|
| - base::WriteFile(new_file_path,
|
| - png_data->front_as<char>(), png_data->size());
|
| -
|
| - if (bytes_written != static_cast<int>(png_data->size()))
|
| - return base::FilePath();
|
| - return new_file_path;
|
| +// Writes |bitmap| to a file at |path|. Returns true if successful.
|
| +bool WriteFile(const base::FilePath& path, const SkBitmap& bitmap) {
|
| + std::vector<unsigned char> png_data;
|
| + if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data))
|
| + return false;
|
| + int bytes_written = base::WriteFile(
|
| + path, reinterpret_cast<char*>(&png_data[0]), png_data.size());
|
| + return (bytes_written == static_cast<int>(png_data.size()));
|
| }
|
|
|
| void DeleteTempDirectory(const base::FilePath& dir_path) {
|
| @@ -255,7 +180,7 @@ AppIndicatorIcon::~AppIndicatorIcon() {
|
| g_object_unref(icon_);
|
| content::BrowserThread::GetBlockingPool()->PostTask(
|
| FROM_HERE,
|
| - base::Bind(&DeleteTempDirectory, icon_file_path_.DirName()));
|
| + base::Bind(&DeleteTempDirectory, temp_dir_.DirName()));
|
| }
|
| }
|
|
|
| @@ -271,22 +196,29 @@ void AppIndicatorIcon::SetImage(const gfx::ImageSkia& image) {
|
|
|
| ++icon_change_count_;
|
|
|
| - // We create a deep copy of the image since it may have been freed by the time
|
| - // it's accessed in the other thread.
|
| - scoped_ptr<gfx::ImageSkia> safe_image(image.DeepCopy());
|
| - base::PostTaskAndReplyWithResult(
|
| + // Copy the bitmap because it may be freed by the time it's accessed in
|
| + // another thread.
|
| + SkBitmap safe_bitmap = *image.bitmap();
|
| +
|
| + scoped_refptr<base::TaskRunner> task_runner =
|
| content::BrowserThread::GetBlockingPool()
|
| ->GetTaskRunnerWithShutdownBehavior(
|
| - base::SequencedWorkerPool::SKIP_ON_SHUTDOWN).get(),
|
| - FROM_HERE,
|
| - base::Bind(&CreateTempImageFile,
|
| - using_kde4_,
|
| - safe_image.release(),
|
| - icon_change_count_,
|
| - id_,
|
| - icon_file_path_),
|
| - base::Bind(&AppIndicatorIcon::SetImageFromFile,
|
| - weak_factory_.GetWeakPtr()));
|
| + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
|
| + if (using_kde4_) {
|
| + base::PostTaskAndReplyWithResult(
|
| + task_runner.get(), FROM_HERE,
|
| + base::Bind(AppIndicatorIcon::WriteKDE4TempImageOnWorkerThread,
|
| + safe_bitmap, temp_dir_),
|
| + base::Bind(&AppIndicatorIcon::SetImageFromFile,
|
| + weak_factory_.GetWeakPtr()));
|
| + } else {
|
| + base::PostTaskAndReplyWithResult(
|
| + task_runner.get(), FROM_HERE,
|
| + base::Bind(AppIndicatorIcon::WriteUnityTempImageOnWorkerThread,
|
| + safe_bitmap, icon_change_count_, id_),
|
| + base::Bind(&AppIndicatorIcon::SetImageFromFile,
|
| + weak_factory_.GetWeakPtr()));
|
| + }
|
| }
|
|
|
| void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) {
|
| @@ -311,37 +243,113 @@ void AppIndicatorIcon::RefreshPlatformContextMenu() {
|
| menu_->Refresh();
|
| }
|
|
|
| -void AppIndicatorIcon::SetImageFromFile(const base::FilePath& icon_file_path) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| - if (icon_file_path.empty())
|
| - return;
|
| +// static
|
| +AppIndicatorIcon::SetImageFromFileParams
|
| +AppIndicatorIcon::WriteKDE4TempImageOnWorkerThread(
|
| + const SkBitmap& bitmap,
|
| + const base::FilePath& existing_temp_dir) {
|
| + base::FilePath temp_dir = existing_temp_dir;
|
| + if (temp_dir.empty() &&
|
| + !base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_dir)) {
|
| + LOG(WARNING) << "Could not create temporary directory";
|
| + return SetImageFromFileParams();
|
| + }
|
| +
|
| + base::FilePath icon_theme_path = temp_dir.AppendASCII("icons");
|
|
|
| - base::FilePath old_path = icon_file_path_;
|
| - icon_file_path_ = icon_file_path;
|
| + // On KDE4, an image located in a directory ending with
|
| + // "icons/hicolor/24x24/apps" can be used as the app indicator image because
|
| + // "/usr/share/icons/hicolor/24x24/apps" exists.
|
| + base::FilePath image_dir = icon_theme_path.AppendASCII("hicolor")
|
| + .AppendASCII("24x24")
|
| + .AppendASCII("apps");
|
| +
|
| + if (!base::CreateDirectory(image_dir))
|
| + return SetImageFromFileParams();
|
| +
|
| + // On KDE4, the name of the image file for each different looking bitmap must
|
| + // be unique. It must also be unique across runs of Chrome.
|
| + std::vector<unsigned char> bitmap_png_data;
|
| + if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_png_data)) {
|
| + LOG(WARNING) << "Could not encode icon";
|
| + return SetImageFromFileParams();
|
| + }
|
| + base::MD5Digest digest;
|
| + base::MD5Sum(reinterpret_cast<char*>(&bitmap_png_data[0]),
|
| + bitmap_png_data.size(), &digest);
|
| + std::string icon_name = base::StringPrintf(
|
| + "chrome_app_indicator2_%s", base::MD5DigestToBase16(digest).c_str());
|
| +
|
| + // If |bitmap| is not 24x24, KDE does some really ugly resizing. Pad |bitmap|
|
| + // with transparent pixels to make it 24x24.
|
| + const int kDesiredSize = 24;
|
| + SkBitmap scaled_bitmap;
|
| + scaled_bitmap.allocN32Pixels(kDesiredSize, kDesiredSize);
|
| + scaled_bitmap.eraseARGB(0, 0, 0, 0);
|
| + SkCanvas canvas(scaled_bitmap);
|
| + canvas.drawBitmap(bitmap, (kDesiredSize - bitmap.width()) / 2,
|
| + (kDesiredSize - bitmap.height()) / 2);
|
| +
|
| + base::FilePath image_path = image_dir.Append(icon_name + ".png");
|
| + if (!WriteFile(image_path, scaled_bitmap))
|
| + return SetImageFromFileParams();
|
| +
|
| + SetImageFromFileParams params;
|
| + params.parent_temp_dir = temp_dir;
|
| + params.icon_theme_path = icon_theme_path.value();
|
| + params.icon_name = icon_name;
|
| + return params;
|
| +}
|
| +
|
| +// static
|
| +AppIndicatorIcon::SetImageFromFileParams
|
| +AppIndicatorIcon::WriteUnityTempImageOnWorkerThread(const SkBitmap& bitmap,
|
| + int icon_change_count,
|
| + const std::string& id) {
|
| + // Create a new temporary directory for each image on Unity since using a
|
| + // single temporary directory seems to have issues when changing icons in
|
| + // quick succession.
|
| + base::FilePath temp_dir;
|
| + if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_dir)) {
|
| + LOG(WARNING) << "Could not create temporary directory";
|
| + return SetImageFromFileParams();
|
| + }
|
|
|
| std::string icon_name =
|
| - icon_file_path_.BaseName().RemoveExtension().value();
|
| - std::string icon_dir = GetIconThemePath(using_kde4_, icon_file_path);
|
| + base::StringPrintf("%s_%d", id.c_str(), icon_change_count);
|
| + base::FilePath image_path = temp_dir.Append(icon_name + ".png");
|
| + SetImageFromFileParams params;
|
| + if (WriteFile(image_path, bitmap)) {
|
| + params.parent_temp_dir = temp_dir;
|
| + params.icon_theme_path = temp_dir.value();
|
| + params.icon_name = icon_name;
|
| + }
|
| + return params;
|
| +}
|
| +
|
| +void AppIndicatorIcon::SetImageFromFile(const SetImageFromFileParams& params) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + if (params.icon_theme_path.empty())
|
| + return;
|
| +
|
| if (!icon_) {
|
| icon_ =
|
| app_indicator_new_with_path(id_.c_str(),
|
| - icon_name.c_str(),
|
| + params.icon_name.c_str(),
|
| APP_INDICATOR_CATEGORY_APPLICATION_STATUS,
|
| - icon_dir.c_str());
|
| + params.icon_theme_path.c_str());
|
| app_indicator_set_status(icon_, APP_INDICATOR_STATUS_ACTIVE);
|
| SetMenu();
|
| } else {
|
| - // Currently we are creating a new temp directory every time the icon is
|
| - // set. So we need to set the directory each time.
|
| - app_indicator_set_icon_theme_path(icon_, icon_dir.c_str());
|
| - app_indicator_set_icon_full(icon_, icon_name.c_str(), "icon");
|
| -
|
| - if (ShouldCreateTempDirectoryPerImage(using_kde4_)) {
|
| - // Delete previous icon directory.
|
| - content::BrowserThread::GetBlockingPool()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&DeleteTempDirectory, old_path.DirName()));
|
| - }
|
| + app_indicator_set_icon_theme_path(icon_, params.icon_theme_path.c_str());
|
| + app_indicator_set_icon_full(icon_, params.icon_name.c_str(), "icon");
|
| + }
|
| +
|
| + if (temp_dir_ != params.parent_temp_dir) {
|
| + content::BrowserThread::GetBlockingPool()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&DeleteTempDirectory, temp_dir_));
|
| + temp_dir_ = params.parent_temp_dir;
|
| }
|
| }
|
|
|
|
|