Home | History | Annotate | Download | only in browser
      1 // Copyright 2014 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 #include "chrome/browser/font_family_cache.h"
      6 
      7 #include <map>
      8 
      9 #include "base/prefs/pref_service.h"
     10 #include "base/strings/stringprintf.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/chrome_notification_types.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/common/pref_font_webkit_names.h"
     15 #include "chrome/common/pref_names.h"
     16 #include "content/public/browser/notification_source.h"
     17 
     18 // Identifies the user data on the profile.
     19 const char kFontFamilyCacheKey[] = "FontFamilyCacheKey";
     20 
     21 FontFamilyCache::FontFamilyCache(Profile* profile)
     22     : prefs_(profile->GetPrefs()) {
     23   profile_pref_registrar_.Init(profile->GetPrefs());
     24   notification_registrar_.Add(this,
     25                               chrome::NOTIFICATION_PROFILE_DESTROYED,
     26                               content::Source<Profile>(profile));
     27 }
     28 
     29 FontFamilyCache::~FontFamilyCache() {
     30 }
     31 
     32 void FontFamilyCache::FillFontFamilyMap(Profile* profile,
     33                                         const char* map_name,
     34                                         content::ScriptFontFamilyMap* map) {
     35   FontFamilyCache* cache =
     36       static_cast<FontFamilyCache*>(profile->GetUserData(&kFontFamilyCacheKey));
     37   if (!cache) {
     38     cache = new FontFamilyCache(profile);
     39     // The profile takes ownership of |cache|.
     40     profile->SetUserData(&kFontFamilyCacheKey, cache);
     41   }
     42 
     43   cache->FillFontFamilyMap(map_name, map);
     44 }
     45 
     46 void FontFamilyCache::FillFontFamilyMap(const char* map_name,
     47                                         content::ScriptFontFamilyMap* map) {
     48   // TODO(falken): Get rid of the brute-force scan over possible
     49   // (font family / script) combinations - see http://crbug.com/308095.
     50   for (size_t i = 0; i < prefs::kWebKitScriptsForFontFamilyMapsLength; ++i) {
     51     const char* script = prefs::kWebKitScriptsForFontFamilyMaps[i];
     52     base::string16 result = FetchAndCacheFont(script, map_name);
     53     if (!result.empty())
     54       (*map)[script] = result;
     55   }
     56 }
     57 
     58 base::string16 FontFamilyCache::FetchFont(const char* script,
     59                                           const char* map_name) {
     60   std::string pref_name = base::StringPrintf("%s.%s", map_name, script);
     61   std::string font = prefs_->GetString(pref_name.c_str());
     62   base::string16 font16 = base::UTF8ToUTF16(font);
     63 
     64   // Lazily constructs the map if it doesn't already exist.
     65   ScriptFontMap& map = font_family_map_[map_name];
     66   map[script] = font16;
     67 
     68   // Register for profile preference changes.
     69   profile_pref_registrar_.Add(
     70       pref_name.c_str(),
     71       base::Bind(&FontFamilyCache::OnPrefsChanged, base::Unretained(this)));
     72   return font16;
     73 }
     74 
     75 base::string16 FontFamilyCache::FetchAndCacheFont(const char* script,
     76                                                   const char* map_name) {
     77   FontFamilyMap::const_iterator it = font_family_map_.find(map_name);
     78   if (it != font_family_map_.end()) {
     79     ScriptFontMap::const_iterator it2 = it->second.find(script);
     80     if (it2 != it->second.end())
     81       return it2->second;
     82   }
     83 
     84   return FetchFont(script, map_name);
     85 }
     86 
     87 // There are ~1000 entries in the cache. Avoid unnecessary object construction,
     88 // including std::string.
     89 void FontFamilyCache::OnPrefsChanged(const std::string& pref_name) {
     90   const size_t delimiter_length = 1;
     91   const char delimiter = '.';
     92   for (FontFamilyMap::iterator it = font_family_map_.begin();
     93        it != font_family_map_.end();
     94        ++it) {
     95     const char* map_name = it->first;
     96     size_t map_name_length = strlen(map_name);
     97 
     98     // If the map name doesn't match, move on.
     99     if (pref_name.compare(0, map_name_length, map_name) != 0)
    100       continue;
    101 
    102     ScriptFontMap& map = it->second;
    103     for (ScriptFontMap::iterator it2 = map.begin(); it2 != map.end(); ++it2) {
    104       const char* script = it2->first;
    105       size_t script_length = strlen(script);
    106 
    107       // If the length doesn't match, move on.
    108       if (pref_name.size() !=
    109           map_name_length + script_length + delimiter_length)
    110         continue;
    111 
    112       // If the script doesn't match, move on.
    113       if (pref_name.compare(
    114               map_name_length + delimiter_length, script_length, script) != 0)
    115         continue;
    116 
    117       // If the delimiter doesn't match, move on.
    118       if (pref_name[map_name_length] != delimiter)
    119         continue;
    120 
    121       // Clear the cache and the observer.
    122       map.erase(it2);
    123       profile_pref_registrar_.Remove(pref_name.c_str());
    124       break;
    125     }
    126   }
    127 }
    128 
    129 void FontFamilyCache::Observe(int type,
    130                               const content::NotificationSource& source,
    131                               const content::NotificationDetails& details) {
    132   DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
    133   profile_pref_registrar_.RemoveAll();
    134 }
    135