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 <stdint.h>
9 
10 #include <map>
11 #include <memory>
12 #include <string>
13 #include <vector>
14 
15 #include "base/containers/flat_map.h"
16 #include "base/macros.h"
17 #include "base/sequenced_task_runner_helpers.h"
18 #include "chrome/browser/themes/custom_theme_supplier.h"
19 #include "chrome/common/themes/autogenerated_theme_util.h"
20 #include "extensions/common/extension.h"
21 #include "third_party/skia/include/core/SkColor.h"
22 #include "ui/base/layout.h"
23 #include "ui/gfx/color_utils.h"
24 
25 namespace base {
26 class DictionaryValue;
27 class FilePath;
28 class RefCountedMemory;
29 }
30 
31 namespace gfx {
32 class Image;
33 }
34 
35 namespace ui {
36 class ColorProvider;
37 class DataPack;
38 }
39 
40 // An optimized representation of a theme, backed by a mmapped DataPack.
41 //
42 // The idea is to pre-process all images (tinting, compositing, etc) at theme
43 // install time, save all the PNG-ified data into an mmappable file so we don't
44 // suffer multiple file system access times, therefore solving two of the
45 // problems with the previous implementation.
46 //
47 // A note on const-ness. All public, non-static methods are const.  We do this
48 // because once we've constructed a BrowserThemePack through the
49 // BuildFromExtension() interface, we WriteToDisk() on a thread other than the
50 // UI thread that consumes a BrowserThemePack. There is no locking; thread
51 // safety between the writing thread and the UI thread is ensured by having the
52 // data be immutable.
53 //
54 // BrowserThemePacks are always deleted on a sequence with I/O allowed because
55 // in the common case, they are backed by mmapped data and the unmmapping
56 // operation will trip our IO on the UI thread detector.
57 // See CustomThemeSupplier constructor more more details.
58 class BrowserThemePack : public CustomThemeSupplier {
59  public:
60   // Public because these IDs are used both in private members of this class and
61   // free functions in browser_theme_pack.cc.
62   enum PersistentID : int;
63 
64   // Builds the theme from |extension| into |pack|. This may be done on a
65   // separate thread as it takes so long. This can fail in the case where the
66   // theme has invalid data, in which case |pack->is_valid()| will be false.
67   static void BuildFromExtension(const extensions::Extension* extension,
68                                  BrowserThemePack* pack);
69 
70   // Builds the theme pack from a previously performed WriteToDisk(). This
71   // operation should be relatively fast, as it should be an mmap() and some
72   // pointer swizzling. Returns NULL on any error attempting to read |path|.
73   static scoped_refptr<BrowserThemePack> BuildFromDataPack(
74       const base::FilePath& path, const std::string& expected_id);
75 
76   // Returns whether the specified identifier is one of the images we persist
77   // in the data pack.
78   static bool IsPersistentImageID(int id);
79 
80   // Builds the theme from given |color| into |pack|.
81   static void BuildFromColor(SkColor color, BrowserThemePack* pack);
82 
83   // Builds the theme from the given |colors| into |pack|.
84   static void BuildFromColors(AutogeneratedThemeColors colors,
85                               BrowserThemePack* pack);
86 
87   // Default. Everything is empty.
88   explicit BrowserThemePack(ThemeType theme_type);
89 
is_valid()90   bool is_valid() const { return is_valid_; }
91 
92   // Builds a data pack on disk at |path| for future quick loading by
93   // BuildFromDataPack(). Often (but not always) called from the file thread;
94   // implementation should be threadsafe because neither thread will write to
95   // |image_memory_| and the worker thread will keep a reference to prevent
96   // destruction.
97   bool WriteToDisk(const base::FilePath& path) const;
98 
99   // Overridden from CustomThemeSupplier:
100   bool GetTint(int id, color_utils::HSL* hsl) const override;
101   bool GetColor(int id, SkColor* color) const override;
102   bool GetDisplayProperty(int id, int* result) const override;
103   gfx::Image GetImageNamed(int id) const override;
104   base::RefCountedMemory* GetRawData(int id, ui::ScaleFactor scale_factor)
105       const override;
106   bool HasCustomImage(int id) const override;
107 
108   // Builds the color mixers that represent the state of the current browser
109   // theme instance.
110   void AddCustomThemeColorMixers(ui::ColorProvider* provider) const;
111 
112  private:
113   friend class BrowserThemePackTest;
114 
115   // Cached images.
116   typedef base::flat_map<PersistentID, gfx::Image> ImageCache;
117 
118   // The raw PNG memory associated with a certain id.
119   typedef std::map<int, scoped_refptr<base::RefCountedMemory> > RawImages;
120 
121   // The type passed to ui::DataPack::WritePack.
122   typedef std::map<uint16_t, base::StringPiece> RawDataForWriting;
123 
124   // Maps scale factors (enum values) to file paths.
125   typedef base::flat_map<ui::ScaleFactor, base::FilePath> ScaleFactorToFileMap;
126 
127   // Maps image ids to maps of scale factors to file paths.
128   typedef base::flat_map<PersistentID, ScaleFactorToFileMap> FilePathMap;
129 
130   ~BrowserThemePack() override;
131 
132   // Modifies |colors_| to set the entry with identifier |id| to |color|.  Only
133   // valid to call after InitColors(), which creates |colors_|.
134   void SetColor(int id, SkColor color);
135 
136   // If |colors_| does not already contain an entry with identifier |id|,
137   // modifies |colors_| to set the entry with identifier |id| to |color|.  If an
138   // entry for |id| already exists, does nothing.
139   // Only valid to call after BuildColorsFromJSON(), which creates |colors_|.
140   void SetColorIfUnspecified(int id, SkColor color);
141 
142   // Sets the value for |id| in |tints_|. Only valid to call after InitTints(),
143   // which creates |tints_|.
144   void SetTint(int id, color_utils::HSL tint);
145 
146   // Sets the value for |id| in |display_properties_|. Only valid to call after
147   // InitDisplayProperties(), which creates |display_properties_|.
148   void SetDisplayProperty(int id, int value);
149 
150   // Calculates the dominant color of the top |height| rows of |image|.
151   SkColor ComputeImageColor(const gfx::Image& image, int height);
152 
153   // Adjusts/sets theme properties.
154   void AdjustThemePack();
155 
156   // Initializes necessary fields.
157   void InitEmptyPack();
158 
159   // Initializes the |header_| with default values.
160   void InitHeader();
161 
162   // Initializes the |tints_| with default values.
163   void InitTints();
164 
165   // Initializes the |colors_| with default values.
166   void InitColors();
167 
168   // Initializes the |display_properties_| with default values.
169   void InitDisplayProperties();
170 
171   // Initializes the |source_images_| with default values.
172   void InitSourceImages();
173 
174   // Sets the ID from |extension|.
175   void SetHeaderId(const extensions::Extension* extension);
176 
177   // Transforms the JSON tint values into their final versions in the |tints_|
178   // array.
179   void SetTintsFromJSON(const base::DictionaryValue* tints_value);
180 
181   // Transforms the JSON color values into their final versions in the
182   // |colors_| array and also fills in unspecified colors based on tint values.
183   void SetColorsFromJSON(const base::DictionaryValue* color_value);
184 
185   // Implementation details of BuildColorsFromJSON().
186   void ReadColorsFromJSON(const base::DictionaryValue* colors_value,
187                           std::map<int, SkColor>* temp_colors);
188 
189   // Transforms the JSON display properties into |display_properties_|.
190   void SetDisplayPropertiesFromJSON(const base::DictionaryValue* display_value);
191 
192   // Parses the image names out of an extension.
193   void ParseImageNamesFromJSON(const base::DictionaryValue* images_value,
194                                const base::FilePath& images_path,
195                                FilePathMap* file_paths) const;
196 
197   // Helper function to populate the FilePathMap.
198   void AddFileAtScaleToMap(const std::string& image_name,
199                            ui::ScaleFactor scale_factor,
200                            const base::FilePath& image_path,
201                            FilePathMap* file_paths) const;
202 
203   // Creates the data for |source_images_| from |file_paths|.
204   void BuildSourceImagesArray(const FilePathMap& file_paths);
205 
206   // Loads the unmodified images packed in the extension to SkBitmaps. Returns
207   // true if all images loaded.
208   bool LoadRawBitmapsTo(const FilePathMap& file_paths,
209                         ImageCache* image_cache);
210 
211   // Crops images down to a size such that most of the cropped image will be
212   // displayed in the UI. Cropping is useful because images from custom themes
213   // can be of any size. Source and destination is |images|.
214   void CropImages(ImageCache* images) const;
215 
216   // Set frame and toolbar related elements' colors (e.g. status bubble,
217   // info bar, download shelf) to frame or toolbar colors.
218   void SetFrameAndToolbarRelatedColors();
219 
220   // Creates a composited toolbar image. Source and destination is |images|.
221   // Also sets toolbar color corresponding to this image.
222   void CreateToolbarImageAndColors(ImageCache* images);
223 
224   // Creates tinted and composited frame images. Source and destination is
225   // |images|.  Also sets frame colors corresponding to these images if no
226   // explicit color has been specified for these colors.
227   void CreateFrameImagesAndColors(ImageCache* images);
228 
229   // Generates any frame colors which have not already been set from tints.
230   void GenerateFrameColorsFromTints();
231 
232   // Generates background color information for the background of window control
233   // buttons.  This can be used when drawing the window control/caption buttons
234   // to determine what color to draw the symbol, ensuring that it contrasts
235   // sufficiently with the background of the button.
236   void GenerateWindowControlButtonColor(ImageCache* images);
237 
238   // Creates the semi-transparent tab background images, putting the results
239   // in |images|.  Also sets colors corresponding to these images if no explicit
240   // color has been specified.  Must be called after GenerateFrameImages().
241   void CreateTabBackgroundImagesAndColors(ImageCache* images);
242 
243   // Generates missing NTP related colors.
244   void GenerateMissingNtpColors();
245 
246   // Takes all the SkBitmaps in |images|, encodes them as PNGs and places
247   // them in |reencoded_images|.
248   void RepackImages(const ImageCache& images,
249                     RawImages* reencoded_images) const;
250 
251   // Takes all images in |source| and puts them in |destination|, freeing any
252   // image already in |destination| that |source| would overwrite.
253   void MergeImageCaches(const ImageCache& source,
254                         ImageCache* destination) const;
255 
256   // Copies images from |source| to |destination| such that the lifetimes of
257   // the images in |destination| are not affected by the lifetimes of the
258   // images in |source|.
259   void CopyImagesTo(const ImageCache& source, ImageCache* destination) const;
260 
261   // Changes the RefCountedMemory based |images| into StringPiece data in |out|.
262   void AddRawImagesTo(const RawImages& images, RawDataForWriting* out) const;
263 
264   // Retrieves the tint OR the default tint. Unlike the public interface, we
265   // always need to return a reasonable tint here, instead of partially
266   // querying if the tint exists.
267   color_utils::HSL GetTintInternal(int id) const;
268 
269   // Returns a unique id to use to store the raw bitmap for |prs_id| at
270   // |scale_factor| in memory.
271   int GetRawIDByPersistentID(PersistentID prs_id,
272                              ui::ScaleFactor scale_factor) const;
273 
274   // Returns true if the |key| specifies a valid scale (e.g. "100") and
275   // the corresponding scale factor is currently in use. If true, returns
276   // the scale factor in |scale_factor|.
277   bool GetScaleFactorFromManifestKey(const std::string& key,
278                                      ui::ScaleFactor* scale_factor) const;
279 
280   // Generates raw images for any missing scale from an available scale.
281   void GenerateRawImageForAllSupportedScales(PersistentID prs_id);
282 
283   // Data pack, if we have one.
284   std::unique_ptr<ui::DataPack> data_pack_;
285 
286   // All structs written to disk need to be packed; no alignment tricks here,
287   // please.
288   // NOTE: This structs can only contain primary data types to be reliably
289   // seralized and de-seralized. Not even nested structs will work across
290   // different machines, see crbug.com/988055.
291 #pragma pack(push,1)
292   // Header that is written to disk.
293   struct BrowserThemePackHeader {
294     // Numeric version to make sure we're compatible in the future.
295     int32_t version;
296 
297     // 1 if little_endian. 0 if big_endian. On mismatch, abort load.
298     int32_t little_endian;
299 
300     // theme_id without NULL terminator.
301     uint8_t theme_id[16];
302   };
303   BrowserThemePackHeader* header_ = nullptr;
304 
305   // The remaining structs represent individual entries in an array. For the
306   // following three structs, BrowserThemePack will either allocate an array or
307   // will point directly to mmapped data.
308   struct TintEntry {
309     int32_t id;
310     double h;
311     double s;
312     double l;
313   };
314   TintEntry* tints_ = nullptr;
315 
316   struct ColorPair {
317     int32_t id;
318     SkColor color;
319   };
320   ColorPair* colors_ = nullptr;
321 
322   struct DisplayPropertyPair {
323     int32_t id;
324     int32_t property;
325   };
326   DisplayPropertyPair* display_properties_ = nullptr;
327 
328   // A list of included source images. A pointer to a -1 terminated array of
329   // our persistent IDs.
330   int* source_images_ = nullptr;
331 #pragma pack(pop)
332 
333   // The scale factors represented by the images in the theme pack.
334   std::vector<ui::ScaleFactor> scale_factors_;
335 
336   // References to raw PNG data. This map isn't touched when |data_pack_| is
337   // non-NULL; |image_memory_| is only filled during BuildFromExtension(). Any
338   // image data that needs to be written to the DataPack during WriteToDisk()
339   // needs to be in |image_memory_|.
340   RawImages image_memory_;
341 
342   // Cached loaded images. These are loaded from |image_memory_|, from
343   // |data_pack_|, and by BuildFromExtension().
344   mutable ImageCache images_;
345 
346   // Cache of images created in BuildFromExtension(). Once the theme pack is
347   // created, this cache should only be accessed on the file thread. There
348   // should be no IDs in |image_memory_| that are in |images_on_file_thread_|
349   // or vice versa.
350   ImageCache images_on_file_thread_;
351 
352   // Whether the theme pack has been succesfully initialized and is ready to
353   // use.
354   bool is_valid_ = false;
355 
356   DISALLOW_COPY_AND_ASSIGN(BrowserThemePack);
357 };
358 
359 #endif  // CHROME_BROWSER_THEMES_BROWSER_THEME_PACK_H_
360