15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// found in the LICENSE file.
45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "chrome/browser/font_family_cache.h"
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include <map>
85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/prefs/pref_service.h"
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/strings/stringprintf.h"
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/strings/utf_string_conversions.h"
125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "chrome/browser/chrome_notification_types.h"
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "chrome/common/pref_font_webkit_names.h"
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "chrome/common/pref_names.h"
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "content/public/browser/notification_source.h"
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Identifies the user data on the profile.
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kFontFamilyCacheKey[] = "FontFamilyCacheKey";
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)FontFamilyCache::FontFamilyCache(Profile* profile)
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    : prefs_(profile->GetPrefs()) {
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  profile_pref_registrar_.Init(profile->GetPrefs());
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  notification_registrar_.Add(this,
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              chrome::NOTIFICATION_PROFILE_DESTROYED,
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              content::Source<Profile>(profile));
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)FontFamilyCache::~FontFamilyCache() {
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void FontFamilyCache::FillFontFamilyMap(Profile* profile,
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                        const char* map_name,
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                        content::ScriptFontFamilyMap* map) {
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  FontFamilyCache* cache =
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      static_cast<FontFamilyCache*>(profile->GetUserData(&kFontFamilyCacheKey));
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (!cache) {
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cache = new FontFamilyCache(profile);
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // The profile takes ownership of |cache|.
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    profile->SetUserData(&kFontFamilyCacheKey, cache);
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  cache->FillFontFamilyMap(map_name, map);
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void FontFamilyCache::FillFontFamilyMap(const char* map_name,
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                        content::ScriptFontFamilyMap* map) {
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // TODO(falken): Get rid of the brute-force scan over possible
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // (font family / script) combinations - see http://crbug.com/308095.
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for (size_t i = 0; i < prefs::kWebKitScriptsForFontFamilyMapsLength; ++i) {
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const char* script = prefs::kWebKitScriptsForFontFamilyMaps[i];
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    base::string16 result = FetchAndCacheFont(script, map_name);
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (!result.empty())
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      (*map)[script] = result;
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)base::string16 FontFamilyCache::FetchFont(const char* script,
595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                          const char* map_name) {
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  std::string pref_name = base::StringPrintf("%s.%s", map_name, script);
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  std::string font = prefs_->GetString(pref_name.c_str());
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  base::string16 font16 = base::UTF8ToUTF16(font);
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Lazily constructs the map if it doesn't already exist.
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ScriptFontMap& map = font_family_map_[map_name];
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  map[script] = font16;
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Register for profile preference changes.
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  profile_pref_registrar_.Add(
705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      pref_name.c_str(),
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      base::Bind(&FontFamilyCache::OnPrefsChanged, base::Unretained(this)));
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return font16;
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)base::string16 FontFamilyCache::FetchAndCacheFont(const char* script,
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                                  const char* map_name) {
775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  FontFamilyMap::const_iterator it = font_family_map_.find(map_name);
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (it != font_family_map_.end()) {
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ScriptFontMap::const_iterator it2 = it->second.find(script);
805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (it2 != it->second.end())
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return it2->second;
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return FetchFont(script, map_name);
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// There are ~1000 entries in the cache. Avoid unnecessary object construction,
885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// including std::string.
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void FontFamilyCache::OnPrefsChanged(const std::string& pref_name) {
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  const size_t delimiter_length = 1;
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  const char delimiter = '.';
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for (FontFamilyMap::iterator it = font_family_map_.begin();
935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)       it != font_family_map_.end();
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)       ++it) {
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const char* map_name = it->first;
965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    size_t map_name_length = strlen(map_name);
975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // If the map name doesn't match, move on.
995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (pref_name.compare(0, map_name_length, map_name) != 0)
1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      continue;
1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ScriptFontMap& map = it->second;
1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for (ScriptFontMap::iterator it2 = map.begin(); it2 != map.end(); ++it2) {
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      const char* script = it2->first;
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      size_t script_length = strlen(script);
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // If the length doesn't match, move on.
1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if (pref_name.size() !=
1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          map_name_length + script_length + delimiter_length)
1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        continue;
1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // If the script doesn't match, move on.
1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if (pref_name.compare(
1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              map_name_length + delimiter_length, script_length, script) != 0)
1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        continue;
1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // If the delimiter doesn't match, move on.
1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if (pref_name[map_name_length] != delimiter)
1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        continue;
1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // Clear the cache and the observer.
1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      map.erase(it2);
1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      profile_pref_registrar_.Remove(pref_name.c_str());
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      break;
1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void FontFamilyCache::Observe(int type,
1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              const content::NotificationSource& source,
1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              const content::NotificationDetails& details) {
1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  profile_pref_registrar_.RemoveAll();
1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
135