15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/fileicon_source.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/ref_counted_memory.h"
129ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h"
184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/base/webui/web_ui_util.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/codec/png_codec.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_skia.h"
227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/gurl.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)typedef std::map<std::string, IconLoader::IconSize> QueryIconSizeMap;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The path used in internal URLs to file icon data.
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kFileIconPath[] = "fileicon";
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// URL parameter specifying icon size.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kIconSize[] = "iconsize";
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// URL parameter specifying scale factor.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kScaleFactor[] = "scale";
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Assuming the url is of the form '/path?query', convert the path portion into
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// a FilePath and return the resulting |file_path| and |query|.  The path
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// portion may have been encoded using encodeURIComponent().
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GetFilePathAndQuery(const std::string& url,
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         base::FilePath* file_path,
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         std::string* query) {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We receive the url with chrome://fileicon/ stripped but GURL expects it.
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const GURL gurl("chrome://fileicon/" + url);
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string path = net::UnescapeURLComponent(
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gurl.path().substr(1), (net::UnescapeRule::URL_SPECIAL_CHARS |
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              net::UnescapeRule::SPACES));
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  *file_path = base::FilePath::FromUTF8Unsafe(path);
50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  *file_path = file_path->NormalizePathSeparators();
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  query->assign(gurl.query());
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IconLoader::IconSize SizeStringToIconSize(const std::string& size_string) {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (size_string == "small") return IconLoader::SMALL;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (size_string == "large") return IconLoader::LARGE;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We default to NORMAL if we don't recognize the size_string. Including
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // size_string=="normal".
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return IconLoader::NORMAL;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Simple parser for data on the query.
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ParseQueryParams(const std::string& query,
64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                      float* scale_factor,
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      IconLoader::IconSize* icon_size) {
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::pair<std::string, std::string> KVPair;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<KVPair> parameters;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (icon_size)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *icon_size = IconLoader::NORMAL;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (scale_factor)
71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    *scale_factor = 1.0f;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SplitStringIntoKeyValuePairs(query, '=', '&', &parameters);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::vector<KVPair>::const_iterator iter = parameters.begin();
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != parameters.end(); ++iter) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (icon_size && iter->first == kIconSize)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *icon_size = SizeStringToIconSize(iter->second);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (scale_factor && iter->first == kScaleFactor)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      webui::ParseScaleFactor(iter->second, scale_factor);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)FileIconSource::IconRequestDetails::IconRequestDetails() : scale_factor(1.0f) {
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)FileIconSource::IconRequestDetails::~IconRequestDetails() {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)FileIconSource::FileIconSource() {}
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)FileIconSource::~FileIconSource() {}
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FileIconSource::FetchFileIcon(
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& path,
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    float scale_factor,
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IconLoader::IconSize icon_size,
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const content::URLDataSource::GotDataCallback& callback) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IconManager* im = g_browser_process->icon_manager();
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Image* icon = im->LookupIconFromFilepath(path, icon_size);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (icon) {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_refptr<base::RefCountedBytes> icon_data(new base::RefCountedBytes);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::PNGCodec::EncodeBGRASkBitmap(
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        icon->ToImageSkia()->GetRepresentation(scale_factor).sk_bitmap(),
106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        false,
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        &icon_data->data());
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
109868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    callback.Run(icon_data.get());
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Attach the ChromeURLDataManager request ID to the history request.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IconRequestDetails details;
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    details.callback = callback;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    details.scale_factor = scale_factor;
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Icon was not in cache, go fetch it slowly.
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    im->LoadIcon(path,
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 icon_size,
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Bind(&FileIconSource::OnFileIconDataAvailable,
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            base::Unretained(this), details),
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 &cancelable_task_tracker_);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)std::string FileIconSource::GetSource() const {
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return kFileIconPath;
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FileIconSource::StartDataRequest(
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& url_path,
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int render_process_id,
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int render_frame_id,
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const content::URLDataSource::GotDataCallback& callback) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string query;
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath file_path;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IconLoader::IconSize icon_size;
137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  float scale_factor = 1.0f;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetFilePathAndQuery(url_path, &file_path, &query);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ParseQueryParams(query, &scale_factor, &icon_size);
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FetchFileIcon(file_path, scale_factor, icon_size, callback);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string FileIconSource::GetMimeType(const std::string&) const {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Rely on image decoder inferring the correct type.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return std::string();
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FileIconSource::OnFileIconDataAvailable(const IconRequestDetails& details,
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             gfx::Image* icon) {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (icon) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_refptr<base::RefCountedBytes> icon_data(new base::RefCountedBytes);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::PNGCodec::EncodeBGRASkBitmap(
15368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        icon->ToImageSkia()->GetRepresentation(
154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            details.scale_factor).sk_bitmap(),
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        false,
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        &icon_data->data());
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
158868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    details.callback.Run(icon_data.get());
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(glen): send a dummy icon.
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    details.callback.Run(NULL);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
164