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