1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_THEMES_BROWSER_THEME_PACK_H_
6#define CHROME_BROWSER_THEMES_BROWSER_THEME_PACK_H_
7
8#include <map>
9#include <string>
10#include <vector>
11
12#include "base/basictypes.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/sequenced_task_runner_helpers.h"
15#include "chrome/browser/themes/custom_theme_supplier.h"
16#include "extensions/common/extension.h"
17#include "third_party/skia/include/core/SkColor.h"
18#include "ui/base/layout.h"
19#include "ui/gfx/color_utils.h"
20
21namespace base {
22class DictionaryValue;
23class FilePath;
24class RefCountedMemory;
25}
26
27namespace extensions {
28class Extensions;
29}
30
31namespace gfx {
32class Image;
33}
34
35namespace ui {
36class DataPack;
37}
38
39// An optimized representation of a theme, backed by a mmapped DataPack.
40//
41// The idea is to pre-process all images (tinting, compositing, etc) at theme
42// install time, save all the PNG-ified data into an mmappable file so we don't
43// suffer multiple file system access times, therefore solving two of the
44// problems with the previous implementation.
45//
46// A note on const-ness. All public, non-static methods are const.  We do this
47// because once we've constructed a BrowserThemePack through the
48// BuildFromExtension() interface, we WriteToDisk() on a thread other than the
49// UI thread that consumes a BrowserThemePack. There is no locking; thread
50// safety between the writing thread and the UI thread is ensured by having the
51// data be immutable.
52//
53// BrowserThemePacks are always deleted on the file thread because in the
54// common case, they are backed by mmapped data and the unmmapping operation
55// will trip our IO on the UI thread detector.
56class BrowserThemePack : public CustomThemeSupplier {
57 public:
58  // Builds the theme pack from all data from |extension|. This is often done
59  // on a separate thread as it takes so long. This can fail and return NULL in
60  // the case where the theme has invalid data.
61  static scoped_refptr<BrowserThemePack> BuildFromExtension(
62      const extensions::Extension* extension);
63
64  // Builds the theme pack from a previously performed WriteToDisk(). This
65  // operation should be relatively fast, as it should be an mmap() and some
66  // pointer swizzling. Returns NULL on any error attempting to read |path|.
67  static scoped_refptr<BrowserThemePack> BuildFromDataPack(
68      const base::FilePath& path, const std::string& expected_id);
69
70  // Returns the set of image IDRs which can be overwritten by a user provided
71  // theme.
72  static void GetThemeableImageIDRs(std::set<int>* result);
73
74  // Builds a data pack on disk at |path| for future quick loading by
75  // BuildFromDataPack(). Often (but not always) called from the file thread;
76  // implementation should be threadsafe because neither thread will write to
77  // |image_memory_| and the worker thread will keep a reference to prevent
78  // destruction.
79  bool WriteToDisk(const base::FilePath& path) const;
80
81  // Overridden from CustomThemeSupplier:
82  virtual bool GetTint(int id, color_utils::HSL* hsl) const OVERRIDE;
83  virtual bool GetColor(int id, SkColor* color) const OVERRIDE;
84  virtual bool GetDisplayProperty(int id, int* result) const OVERRIDE;
85  virtual gfx::Image GetImageNamed(int id) OVERRIDE;
86  virtual base::RefCountedMemory* GetRawData(
87      int id, ui::ScaleFactor scale_factor) const OVERRIDE;
88  virtual bool HasCustomImage(int id) const OVERRIDE;
89
90 private:
91  friend class BrowserThemePackTest;
92
93  // Cached images.
94  typedef std::map<int, gfx::Image> ImageCache;
95
96  // The raw PNG memory associated with a certain id.
97  typedef std::map<int, scoped_refptr<base::RefCountedMemory> > RawImages;
98
99  // The type passed to ui::DataPack::WritePack.
100  typedef std::map<uint16, base::StringPiece> RawDataForWriting;
101
102  // Maps scale factors (enum values) to file paths.
103  typedef std::map<ui::ScaleFactor, base::FilePath> ScaleFactorToFileMap;
104
105  // Maps image ids to maps of scale factors to file paths.
106  typedef std::map<int, ScaleFactorToFileMap> FilePathMap;
107
108  // Default. Everything is empty.
109  BrowserThemePack();
110
111  virtual ~BrowserThemePack();
112
113  // Builds a header ready to write to disk.
114  void BuildHeader(const extensions::Extension* extension);
115
116  // Transforms the JSON tint values into their final versions in the |tints_|
117  // array.
118  void BuildTintsFromJSON(const base::DictionaryValue* tints_value);
119
120  // Transforms the JSON color values into their final versions in the
121  // |colors_| array and also fills in unspecified colors based on tint values.
122  void BuildColorsFromJSON(const base::DictionaryValue* color_value);
123
124  // Implementation details of BuildColorsFromJSON().
125  void ReadColorsFromJSON(const base::DictionaryValue* colors_value,
126                          std::map<int, SkColor>* temp_colors);
127  void GenerateMissingColors(std::map<int, SkColor>* temp_colors);
128
129  // Transforms the JSON display properties into |display_properties_|.
130  void BuildDisplayPropertiesFromJSON(
131      const base::DictionaryValue* display_value);
132
133  // Parses the image names out of an extension.
134  void ParseImageNamesFromJSON(const base::DictionaryValue* images_value,
135                               const base::FilePath& images_path,
136                               FilePathMap* file_paths) const;
137
138  // Helper function to populate the FilePathMap.
139  void AddFileAtScaleToMap(const std::string& image_name,
140                           ui::ScaleFactor scale_factor,
141                           const base::FilePath& image_path,
142                           FilePathMap* file_paths) const;
143
144  // Creates the data for |source_images_| from |file_paths|.
145  void BuildSourceImagesArray(const FilePathMap& file_paths);
146
147  // Loads the unmodified images packed in the extension to SkBitmaps. Returns
148  // true if all images loaded.
149  bool LoadRawBitmapsTo(const FilePathMap& file_paths,
150                        ImageCache* image_cache);
151
152  // Populate |images| cache with empty gfx::Images. Image reps are lazily
153  // generated when an image rep is requested via ImageSkia::GetRepresentation.
154  // Source and destination is |images|.
155  void CreateImages(ImageCache* images) const;
156
157  // Crops images down to a size such that most of the cropped image will be
158  // displayed in the UI. Cropping is useful because images from custom themes
159  // can be of any size. Source and destination is |images|.
160  void CropImages(ImageCache* images) const;
161
162  // Creates tinted and composited frame images. Source and destination is
163  // |images|.
164  void CreateFrameImages(ImageCache* images) const;
165
166  // Creates button images tinted with |button_tint| and places them in
167  // processed_images.
168  void CreateTintedButtons(const color_utils::HSL& button_tint,
169                           ImageCache* processed_images) const;
170
171  // Creates the semi-transparent tab background images, putting the results
172  // in |images|. Must be called after GenerateFrameImages().
173  void CreateTabBackgroundImages(ImageCache* images) const;
174
175  // Takes all the SkBitmaps in |images|, encodes them as PNGs and places
176  // them in |reencoded_images|.
177  void RepackImages(const ImageCache& images,
178                    RawImages* reencoded_images) const;
179
180  // Takes all images in |source| and puts them in |destination|, freeing any
181  // image already in |destination| that |source| would overwrite.
182  void MergeImageCaches(const ImageCache& source,
183                        ImageCache* destination) const;
184
185  // Copies images from |source| to |destination| such that the lifetimes of
186  // the images in |destination| are not affected by the lifetimes of the
187  // images in |source|.
188  void CopyImagesTo(const ImageCache& source, ImageCache* destination) const;
189
190  // Changes the RefCountedMemory based |images| into StringPiece data in |out|.
191  void AddRawImagesTo(const RawImages& images, RawDataForWriting* out) const;
192
193  // Retrieves the tint OR the default tint. Unlike the public interface, we
194  // always need to return a reasonable tint here, instead of partially
195  // querying if the tint exists.
196  color_utils::HSL GetTintInternal(int id) const;
197
198  // Returns a unique id to use to store the raw bitmap for |prs_id| at
199  // |scale_factor| in memory.
200  int GetRawIDByPersistentID(int prs_id, ui::ScaleFactor scale_factor) const;
201
202  // Returns true if the |key| specifies a valid scale (e.g. "100") and
203  // the corresponding scale factor is currently in use. If true, returns
204  // the scale factor in |scale_factor|.
205  bool GetScaleFactorFromManifestKey(const std::string& key,
206                                     ui::ScaleFactor* scale_factor) const;
207
208  // Generates raw images for any missing scale from an available scale.
209  void GenerateRawImageForAllSupportedScales(int prs_id);
210
211  // Data pack, if we have one.
212  scoped_ptr<ui::DataPack> data_pack_;
213
214  // All structs written to disk need to be packed; no alignment tricks here,
215  // please.
216#pragma pack(push,1)
217  // Header that is written to disk.
218  struct BrowserThemePackHeader {
219    // Numeric version to make sure we're compatible in the future.
220    int32 version;
221
222    // 1 if little_endian. 0 if big_endian. On mismatch, abort load.
223    int32 little_endian;
224
225    // theme_id without NULL terminator.
226    uint8 theme_id[16];
227  } *header_;
228
229  // The remaining structs represent individual entries in an array. For the
230  // following three structs, BrowserThemePack will either allocate an array or
231  // will point directly to mmapped data.
232  struct TintEntry {
233    int32 id;
234    double h;
235    double s;
236    double l;
237  } *tints_;
238
239  struct ColorPair {
240    int32 id;
241    SkColor color;
242  } *colors_;
243
244  struct DisplayPropertyPair {
245    int32 id;
246    int32 property;
247  } *display_properties_;
248
249  // A list of included source images. A pointer to a -1 terminated array of
250  // our persistent IDs.
251  int* source_images_;
252#pragma pack(pop)
253
254  // The scale factors represented by the images in the theme pack.
255  std::vector<ui::ScaleFactor> scale_factors_;
256
257  // References to raw PNG data. This map isn't touched when |data_pack_| is
258  // non-NULL; |image_memory_| is only filled during BuildFromExtension(). Any
259  // image data that needs to be written to the DataPack during WriteToDisk()
260  // needs to be in |image_memory_|.
261  RawImages image_memory_;
262
263  // Loaded images. These are loaded from |image_memory_|, from |data_pack_|,
264  // and by BuildFromExtension(). These images should only be accessed on the UI
265  // thread.
266  ImageCache images_on_ui_thread_;
267
268  // Cache of images created in BuildFromExtension(). Once the theme pack is
269  // created, this cache should only be accessed on the file thread. There
270  // should be no IDs in |image_memory_| that are in |images_on_file_thread_|
271  // or vice versa.
272  ImageCache images_on_file_thread_;
273
274  DISALLOW_COPY_AND_ASSIGN(BrowserThemePack);
275};
276
277#endif  // CHROME_BROWSER_THEMES_BROWSER_THEME_PACK_H_
278