| 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;
 | 
|    }
 | 
|  }
 | 
|  
 | 
| 
 |