// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_THEMES_BROWSER_THEME_PACK_H_
#define CHROME_BROWSER_THEMES_BROWSER_THEME_PACK_H_
#pragma once
#include <map>
#include <string>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/common/extensions/extension.h"
#include "content/browser/browser_thread.h"
#include "ui/gfx/color_utils.h"
class DictionaryValue;
class FilePath;
class RefCountedMemory;
namespace ui {
class DataPack;
}
// An optimized representation of a theme, backed by a mmapped DataPack.
//
// The idea is to pre-process all images (tinting, compositing, etc) at theme
// install time, save all the PNG-ified data into an mmappable file so we don't
// suffer multiple file system access times, therefore solving two of the
// problems with the previous implementation.
//
// A note on const-ness. All public, non-static methods are const. We do this
// because once we've constructed a BrowserThemePack through the
// BuildFromExtension() interface, we WriteToDisk() on a thread other than the
// UI thread that consumes a BrowserThemePack. There is no locking; thread
// safety between the writing thread and the UI thread is ensured by having the
// data be immutable.
//
// BrowserThemePacks are always deleted on the file thread because in the
// common case, they are backed by mmapped data and the unmmapping operation
// will trip our IO on the UI thread detector.
class BrowserThemePack : public base::RefCountedThreadSafe<
BrowserThemePack, BrowserThread::DeleteOnFileThread> {
public:
// Builds the theme pack from all data from |extension|. This is often done
// on a separate thread as it takes so long. This can fail and return NULL in
// the case where the theme has invalid data.
static BrowserThemePack* BuildFromExtension(const Extension* extension);
// Builds the theme pack from a previously performed WriteToDisk(). This
// operation should be relatively fast, as it should be an mmap() and some
// pointer swizzling. Returns NULL on any error attempting to read |path|.
static scoped_refptr<BrowserThemePack> BuildFromDataPack(
FilePath path, const std::string& expected_id);
// Builds a data pack on disk at |path| for future quick loading by
// BuildFromDataPack(). Often (but not always) called from the file thread;
// implementation should be threadsafe because neither thread will write to
// |image_memory_| and the worker thread will keep a reference to prevent
// destruction.
bool WriteToDisk(FilePath path) const;
// If this theme specifies data for the corresponding |id|, return true and
// write the corresponding value to the output parameter. These functions
// don't return the default data. These methods should only be called from
// the UI thread. (But this isn't enforced because of unit tests).
bool GetTint(int id, color_utils::HSL* hsl) const;
bool GetColor(int id, SkColor* color) const;
bool GetDisplayProperty(int id, int* result) const;
// Returns a bitmap if we have a custom image for |id|, otherwise NULL. Note
// that this is separate from HasCustomImage() which returns whether a custom
// image |id| was included in the unprocessed theme and is used as a proxy
// for making layout decisions in the interface.
SkBitmap* GetBitmapNamed(int id) const;
// Returns the raw PNG encoded data for IDR_THEME_NTP_*. This method is only
// supposed to work for the NTP attribution and background resources.
RefCountedMemory* GetRawData(int id) const;
// Whether this theme provides an image for |id|.
bool HasCustomImage(int id) const;
private:
friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>;
friend class DeleteTask<BrowserThemePack>;
friend class BrowserThemePackTest;
// Cached images. We cache all retrieved and generated bitmaps and keep
// track of the pointers. We own these and will delete them when we're done
// using them.
typedef std::map<int, SkBitmap*> ImageCache;
// The raw PNG memory associated with a certain id.
typedef std::map<int, scoped_refptr<RefCountedMemory> > RawImages;
// The type passed to base::DataPack::WritePack.
typedef std::map<uint32, base::StringPiece> RawDataForWriting;
// An association between an id and the FilePath that has the image data.
typedef std::map<int, FilePath> FilePathMap;
// Default. Everything is empty.
BrowserThemePack();
virtual ~BrowserThemePack();
// Builds a header ready to write to disk.
void BuildHeader(const Extension* extension);
// Transforms the JSON tint values into their final versions in the |tints_|
// array.
void BuildTintsFromJSON(DictionaryValue* tints_value);
// Transforms the JSON color values into their final versions in the
// |colors_| array and also fills in unspecified colors based on tint values.
void BuildColorsFromJSON(DictionaryValue* color_value);
// Implementation details of BuildColorsFromJSON().
void ReadColorsFromJSON(DictionaryValue* colors_value,
std::map<int, SkColor>* temp_colors);
void GenerateMissingColors(std::map<int, SkColor>* temp_colors);
// Transforms the JSON display properties into |display_properties_|.
void BuildDisplayPropertiesFromJSON(DictionaryValue* display_value);
// Parses the image names out of an extension.
void ParseImageNamesFromJSON(DictionaryValue* images_value,
const FilePath& images_path,
FilePathMap* file_paths) const;
// Creates the data for |source_images_| from |file_paths|.
void BuildSourceImagesArray(const FilePathMap& file_paths);
// Loads the unmodified bitmaps packed in the extension to SkBitmaps. Returns
// true if all images loaded.
bool LoadRawBitmapsTo(const FilePathMap& file_paths,
ImageCache* raw_bitmaps);
// Creates tinted and composited frame images. Source and destination is
// |bitmaps|.
void GenerateFrameImages(ImageCache* bitmaps) const;
// Generates button images tinted with |button_tint| and places them in
// processed_bitmaps.
void GenerateTintedButtons(const color_utils::HSL& button_tint,
ImageCache* processed_bitmaps) const;
// Generates the semi-transparent tab background images, putting the results
// in |bitmaps|. Must be called after GenerateFrameImages().
void GenerateTabBackgroundImages(ImageCache* bitmaps) const;
// Takes all the SkBitmaps in |images|, encodes them as PNGs and places
// them in |reencoded_images|.
void RepackImages(const ImageCache& images,
RawImages* reencoded_images) const;
// Takes all images in |source| and puts them in |destination|, freeing any
// image already in |destination| that |source| would overwrite.
void MergeImageCaches(const ImageCache& source,
ImageCache* destination) const;
// Changes the RefCountedMemory based |images| into StringPiece data in |out|.
void AddRawImagesTo(const RawImages& images, RawDataForWriting* out) const;
// Retrieves the tint OR the default tint. Unlike the public interface, we
// always need to return a reasonable tint here, instead of partially
// querying if the tint exists.
color_utils::HSL GetTintInternal(int id) const;
// Data pack, if we have one.
scoped_ptr<ui::DataPack> data_pack_;
// All structs written to disk need to be packed; no alignment tricks here,
// please.
#pragma pack(push,1)
// Header that is written to disk.
struct BrowserThemePackHeader {
// Numeric version to make sure we're compatible in the future.
int32 version;
// 1 if little_endian. 0 if big_endian. On mismatch, abort load.
int32 little_endian;
// theme_id without NULL terminator.
uint8 theme_id[16];
} *header_;
// The remaining structs represent individual entries in an array. For the
// following three structs, BrowserThemePack will either allocate an array or
// will point directly to mmapped data.
struct TintEntry {
int32 id;
double h;
double s;
double l;
} *tints_;
struct ColorPair {
int32 id;
SkColor color;
} *colors_;
struct DisplayPropertyPair {
int32 id;
int32 property;
} *display_properties_;
// A list of included source images. A pointer to a -1 terminated array of
// our persistent IDs.
int* source_images_;
#pragma pack(pop)
// References to raw PNG data. This map isn't touched when |data_pack_| is
// non-NULL; |image_memory_| is only filled during BuildFromExtension(). Any
// image data that needs to be written to the DataPack during WriteToDisk()
// needs to be in |image_memory_|.
RawImages image_memory_;
// An immutable cache of images generated in BuildFromExtension(). When this
// BrowserThemePack is generated from BuildFromDataPack(), this cache is
// empty. We separate the images from the images loaded from disk so that
// WriteToDisk()'s implementation doesn't need locks. There should be no IDs
// in |image_memory_| that are in |prepared_images_| or vice versa.
ImageCache prepared_images_;
// Loaded images. These are loaded from |image_memory_| or the |data_pack_|.
mutable ImageCache loaded_images_;
DISALLOW_COPY_AND_ASSIGN(BrowserThemePack);
};
#endif // CHROME_BROWSER_THEMES_BROWSER_THEME_PACK_H_