// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/prefs/prefs_tab_helper.h"

#include <stddef.h>
#include <stdint.h>

#include <set>
#include <string>

#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_preferences_util.h"
#include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
#include "chrome/common/pref_font_webkit_names.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_names_util.h"
#include "chrome/grit/locale_settings.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/overlay_user_pref_store.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/renderer_preferences.h"
#include "content/public/common/web_preferences.h"
#include "grit/platform_locale_settings.h"
#include "third_party/icu/source/common/unicode/uchar.h"
#include "third_party/icu/source/common/unicode/uscript.h"
#include "ui/base/l10n/l10n_util.h"

#if defined(OS_POSIX) && !defined(OS_MACOSX) && defined(ENABLE_THEMES)
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#endif

#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif

using content::WebContents;
using content::WebPreferences;

DEFINE_WEB_CONTENTS_USER_DATA_KEY(PrefsTabHelper);

namespace {

// The list of prefs we want to observe.
const char* kPrefsToObserve[] = {
#if defined(ENABLE_EXTENSIONS)
  prefs::kAnimationPolicy,
#endif
  prefs::kDataSaverEnabled,
  prefs::kDefaultCharset,
  prefs::kDisable3DAPIs,
  prefs::kEnableHyperlinkAuditing,
  prefs::kWebKitAllowDisplayingInsecureContent,
  prefs::kWebKitAllowRunningInsecureContent,
  prefs::kWebKitDefaultFixedFontSize,
  prefs::kWebKitDefaultFontSize,
  prefs::kWebKitDomPasteEnabled,
#if defined(OS_ANDROID)
  prefs::kWebKitFontScaleFactor,
  prefs::kWebKitForceEnableZoom,
  prefs::kWebKitPasswordEchoEnabled,
#endif
  prefs::kWebKitJavascriptCanOpenWindowsAutomatically,
  prefs::kWebKitJavascriptEnabled,
  prefs::kWebKitLoadsImagesAutomatically,
  prefs::kWebKitMinimumFontSize,
  prefs::kWebKitMinimumLogicalFontSize,
  prefs::kWebKitPluginsEnabled,
  prefs::kWebkitTabsToLinks,
  prefs::kWebKitTextAreasAreResizable,
  prefs::kWebKitUsesUniversalDetector,
  prefs::kWebKitWebSecurityEnabled,
};

const int kPrefsToObserveLength = arraysize(kPrefsToObserve);

#if !defined(OS_ANDROID)
// Registers a preference under the path |pref_name| for each script used for
// per-script font prefs.
// For example, for WEBKIT_WEBPREFS_FONTS_SERIF ("fonts.serif"):
// "fonts.serif.Arab", "fonts.serif.Hang", etc. are registered.
// |fonts_with_defaults| contains all |pref_names| already registered since they
// have a specified default value.
// On Android there are no default values for these properties and there is no
// way to set them (because extensions are not supported so the Font Settings
// API cannot be used), so we can avoid registering them altogether.
void RegisterFontFamilyPrefs(user_prefs::PrefRegistrySyncable* registry,
                             const std::set<std::string>& fonts_with_defaults) {
  // Expand the font concatenated with script name so this stays at RO memory
  // rather than allocated in heap.
  static const char* const kFontFamilyMap[] = {
#define EXPAND_SCRIPT_FONT(map_name, script_name) map_name "." script_name,

#include "chrome/common/pref_font_script_names-inl.h"
ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_CURSIVE)
ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_FANTASY)
ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_FIXED)
ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_PICTOGRAPH)
ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_SANSERIF)
ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_SERIF)
ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_STANDARD)

#undef EXPAND_SCRIPT_FONT
  };

  for (size_t i = 0; i < arraysize(kFontFamilyMap); ++i) {
    const char* pref_name = kFontFamilyMap[i];
    if (fonts_with_defaults.find(pref_name) == fonts_with_defaults.end()) {
      // We haven't already set a default value for this font preference, so set
      // an empty string as the default.
      registry->RegisterStringPref(pref_name, std::string());
    }
  }
}
#endif  // !defined(OS_ANDROID)

// Registers |obs| to observe per-script font prefs under the path |map_name|.
// On android, there's no exposed way to change these prefs, so we can save
// ~715KB of heap and some startup cycles by avoiding observing these prefs
// since they will never change.
void RegisterFontFamilyMapObserver(
    PrefChangeRegistrar* registrar,
    const char* map_name,
    const PrefChangeRegistrar::NamedChangeCallback& obs) {
  DCHECK(base::StartsWith(map_name, "webkit.webprefs.",
                          base::CompareCase::SENSITIVE));

  for (size_t i = 0; i < prefs::kWebKitScriptsForFontFamilyMapsLength; ++i) {
    const char* script = prefs::kWebKitScriptsForFontFamilyMaps[i];
    registrar->Add(base::StringPrintf("%s.%s", map_name, script), obs);
  }
}

#if defined(OS_WIN)
// On Windows with antialising we want to use an alternate fixed font like
// Consolas, which looks much better than Courier New.
bool ShouldUseAlternateDefaultFixedFont(const std::string& script) {
  if (!base::StartsWith(script, "courier",
                        base::CompareCase::INSENSITIVE_ASCII))
    return false;
  UINT smooth_type = 0;
  SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smooth_type, 0);
  return (base::win::GetVersion() >= base::win::VERSION_WIN7) &&
         (smooth_type == FE_FONTSMOOTHINGCLEARTYPE);
}
#endif

struct FontDefault {
  const char* pref_name;
  int resource_id;
};

// Font pref defaults.  The prefs that have defaults vary by platform, since not
// all platforms have fonts for all scripts for all generic families.
// TODO(falken): add proper defaults when possible for all
// platforms/scripts/generic families.
const FontDefault kFontDefaults[] = {
  { prefs::kWebKitStandardFontFamily, IDS_STANDARD_FONT_FAMILY },
  { prefs::kWebKitFixedFontFamily, IDS_FIXED_FONT_FAMILY },
  { prefs::kWebKitSerifFontFamily, IDS_SERIF_FONT_FAMILY },
  { prefs::kWebKitSansSerifFontFamily, IDS_SANS_SERIF_FONT_FAMILY },
  { prefs::kWebKitCursiveFontFamily, IDS_CURSIVE_FONT_FAMILY },
  { prefs::kWebKitFantasyFontFamily, IDS_FANTASY_FONT_FAMILY },
  { prefs::kWebKitPictographFontFamily, IDS_PICTOGRAPH_FONT_FAMILY },
#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined(OS_WIN)
  { prefs::kWebKitStandardFontFamilyJapanese,
    IDS_STANDARD_FONT_FAMILY_JAPANESE },
  { prefs::kWebKitFixedFontFamilyJapanese, IDS_FIXED_FONT_FAMILY_JAPANESE },
  { prefs::kWebKitSerifFontFamilyJapanese, IDS_SERIF_FONT_FAMILY_JAPANESE },
  { prefs::kWebKitSansSerifFontFamilyJapanese,
    IDS_SANS_SERIF_FONT_FAMILY_JAPANESE },
  { prefs::kWebKitStandardFontFamilyKorean, IDS_STANDARD_FONT_FAMILY_KOREAN },
  { prefs::kWebKitSerifFontFamilyKorean, IDS_SERIF_FONT_FAMILY_KOREAN },
  { prefs::kWebKitSansSerifFontFamilyKorean,
    IDS_SANS_SERIF_FONT_FAMILY_KOREAN },
  { prefs::kWebKitStandardFontFamilySimplifiedHan,
    IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN },
  { prefs::kWebKitSerifFontFamilySimplifiedHan,
    IDS_SERIF_FONT_FAMILY_SIMPLIFIED_HAN },
  { prefs::kWebKitSansSerifFontFamilySimplifiedHan,
    IDS_SANS_SERIF_FONT_FAMILY_SIMPLIFIED_HAN },
  { prefs::kWebKitStandardFontFamilyTraditionalHan,
    IDS_STANDARD_FONT_FAMILY_TRADITIONAL_HAN },
  { prefs::kWebKitSerifFontFamilyTraditionalHan,
    IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN },
  { prefs::kWebKitSansSerifFontFamilyTraditionalHan,
    IDS_SANS_SERIF_FONT_FAMILY_TRADITIONAL_HAN },
#endif
#if defined(OS_CHROMEOS)
  { prefs::kWebKitStandardFontFamilyArabic, IDS_STANDARD_FONT_FAMILY_ARABIC },
  { prefs::kWebKitSerifFontFamilyArabic, IDS_SERIF_FONT_FAMILY_ARABIC },
  { prefs::kWebKitSansSerifFontFamilyArabic,
    IDS_SANS_SERIF_FONT_FAMILY_ARABIC },
  { prefs::kWebKitFixedFontFamilyKorean, IDS_FIXED_FONT_FAMILY_KOREAN },
  { prefs::kWebKitFixedFontFamilySimplifiedHan,
    IDS_FIXED_FONT_FAMILY_SIMPLIFIED_HAN },
  { prefs::kWebKitFixedFontFamilyTraditionalHan,
    IDS_FIXED_FONT_FAMILY_TRADITIONAL_HAN },
#elif defined(OS_WIN)
  { prefs::kWebKitFixedFontFamilyArabic, IDS_FIXED_FONT_FAMILY_ARABIC },
  { prefs::kWebKitSansSerifFontFamilyArabic,
    IDS_SANS_SERIF_FONT_FAMILY_ARABIC },
  { prefs::kWebKitStandardFontFamilyCyrillic,
    IDS_STANDARD_FONT_FAMILY_CYRILLIC },
  { prefs::kWebKitFixedFontFamilyCyrillic, IDS_FIXED_FONT_FAMILY_CYRILLIC },
  { prefs::kWebKitSerifFontFamilyCyrillic, IDS_SERIF_FONT_FAMILY_CYRILLIC },
  { prefs::kWebKitSansSerifFontFamilyCyrillic,
    IDS_SANS_SERIF_FONT_FAMILY_CYRILLIC },
  { prefs::kWebKitStandardFontFamilyGreek, IDS_STANDARD_FONT_FAMILY_GREEK },
  { prefs::kWebKitFixedFontFamilyGreek, IDS_FIXED_FONT_FAMILY_GREEK },
  { prefs::kWebKitSerifFontFamilyGreek, IDS_SERIF_FONT_FAMILY_GREEK },
  { prefs::kWebKitSansSerifFontFamilyGreek, IDS_SANS_SERIF_FONT_FAMILY_GREEK },
  { prefs::kWebKitFixedFontFamilyKorean, IDS_FIXED_FONT_FAMILY_KOREAN },
  { prefs::kWebKitCursiveFontFamilyKorean, IDS_CURSIVE_FONT_FAMILY_KOREAN },
  { prefs::kWebKitFixedFontFamilySimplifiedHan,
    IDS_FIXED_FONT_FAMILY_SIMPLIFIED_HAN },
  { prefs::kWebKitFixedFontFamilyTraditionalHan,
    IDS_FIXED_FONT_FAMILY_TRADITIONAL_HAN },
#endif
};

const size_t kFontDefaultsLength = arraysize(kFontDefaults);

// Returns the script of the font pref |pref_name|.  For example, suppose
// |pref_name| is "webkit.webprefs.fonts.serif.Hant".  Since the script code for
// the script name "Hant" is USCRIPT_TRADITIONAL_HAN, the function returns
// USCRIPT_TRADITIONAL_HAN.  |pref_name| must be a valid font pref name.
UScriptCode GetScriptOfFontPref(const char* pref_name) {
  // ICU script names are four letters.
  static const size_t kScriptNameLength = 4;

  size_t len = strlen(pref_name);
  DCHECK_GT(len, kScriptNameLength);
  const char* scriptName = &pref_name[len - kScriptNameLength];
  int32_t code = u_getPropertyValueEnum(UCHAR_SCRIPT, scriptName);
  DCHECK(code >= 0 && code < USCRIPT_CODE_LIMIT);
  return static_cast<UScriptCode>(code);
}

// Returns the primary script used by the browser's UI locale.  For example, if
// the locale is "ru", the function returns USCRIPT_CYRILLIC, and if the locale
// is "en", the function returns USCRIPT_LATIN.
UScriptCode GetScriptOfBrowserLocale() {
  std::string locale = g_browser_process->GetApplicationLocale();

  // For Chinese locales, uscript_getCode() just returns USCRIPT_HAN but our
  // per-script fonts are for USCRIPT_SIMPLIFIED_HAN and
  // USCRIPT_TRADITIONAL_HAN.
  if (locale == "zh-CN")
    return USCRIPT_SIMPLIFIED_HAN;
  if (locale == "zh-TW")
    return USCRIPT_TRADITIONAL_HAN;
  // For Korean and Japanese, multiple scripts are returned by
  // |uscript_getCode|, but we're passing a one entry buffer leading
  // the buffer to be filled by USCRIPT_INVALID_CODE. We need to
  // hard-code the results for them.
  if (locale == "ko")
    return USCRIPT_HANGUL;
  if (locale == "ja")
    return USCRIPT_JAPANESE;

  UScriptCode code = USCRIPT_INVALID_CODE;
  UErrorCode err = U_ZERO_ERROR;
  uscript_getCode(locale.c_str(), &code, 1, &err);

  if (U_FAILURE(err))
    code = USCRIPT_INVALID_CODE;
  return code;
}

// Sets a font family pref in |prefs| to |pref_value|.
void OverrideFontFamily(WebPreferences* prefs,
                        const std::string& generic_family,
                        const std::string& script,
                        const std::string& pref_value) {
  content::ScriptFontFamilyMap* map = NULL;
  if (generic_family == "standard")
    map = &prefs->standard_font_family_map;
  else if (generic_family == "fixed")
    map = &prefs->fixed_font_family_map;
  else if (generic_family == "serif")
    map = &prefs->serif_font_family_map;
  else if (generic_family == "sansserif")
    map = &prefs->sans_serif_font_family_map;
  else if (generic_family == "cursive")
    map = &prefs->cursive_font_family_map;
  else if (generic_family == "fantasy")
    map = &prefs->fantasy_font_family_map;
  else if (generic_family == "pictograph")
    map = &prefs->pictograph_font_family_map;
  else
    NOTREACHED() << "Unknown generic font family: " << generic_family;
  (*map)[script] = base::UTF8ToUTF16(pref_value);
}

void RegisterLocalizedFontPref(user_prefs::PrefRegistrySyncable* registry,
                               const char* path,
                               int default_message_id) {
  int val = 0;
  bool success = base::StringToInt(l10n_util::GetStringUTF8(
      default_message_id), &val);
  DCHECK(success);
  registry->RegisterIntegerPref(path, val);
}

bool IsAutodetectEncodingEnabledByDefault() {
  const std::string group_name = base::FieldTrialList::FindFullName(
      "AutodetectEncoding");
  return base::StartsWith(group_name,
                          "Enabled",
                          base::CompareCase::INSENSITIVE_ASCII);
}

}  // namespace

// Watching all these settings per tab is slow when a user has a lot of tabs and
// and they use session restore. So watch them once per profile.
// http://crbug.com/452693
class PrefWatcher : public KeyedService {
 public:
  explicit PrefWatcher(Profile* profile) : profile_(profile) {
    pref_change_registrar_.Init(profile_->GetPrefs());

    base::Closure renderer_callback = base::Bind(
        &PrefWatcher::UpdateRendererPreferences, base::Unretained(this));
    pref_change_registrar_.Add(prefs::kAcceptLanguages, renderer_callback);
    pref_change_registrar_.Add(prefs::kEnableDoNotTrack, renderer_callback);
    pref_change_registrar_.Add(prefs::kEnableReferrers, renderer_callback);

#if defined(ENABLE_WEBRTC)
    pref_change_registrar_.Add(prefs::kWebRTCMultipleRoutesEnabled,
                               renderer_callback);
    pref_change_registrar_.Add(prefs::kWebRTCNonProxiedUdpEnabled,
                               renderer_callback);
    pref_change_registrar_.Add(prefs::kWebRTCIPHandlingPolicy,
                               renderer_callback);
#endif

#if !defined(OS_MACOSX)
    pref_change_registrar_.Add(prefs::kFullscreenAllowed, renderer_callback);
#endif

    PrefChangeRegistrar::NamedChangeCallback webkit_callback = base::Bind(
        &PrefWatcher::OnWebPrefChanged, base::Unretained(this));
    for (int i = 0; i < kPrefsToObserveLength; ++i) {
      const char* pref_name = kPrefsToObserve[i];
      pref_change_registrar_.Add(pref_name, webkit_callback);
    }

    RegisterFontFamilyMapObserver(&pref_change_registrar_,
                                  prefs::kWebKitStandardFontFamilyMap,
                                  webkit_callback);
    RegisterFontFamilyMapObserver(&pref_change_registrar_,
                                  prefs::kWebKitFixedFontFamilyMap,
                                  webkit_callback);
    RegisterFontFamilyMapObserver(&pref_change_registrar_,
                                  prefs::kWebKitSerifFontFamilyMap,
                                  webkit_callback);
    RegisterFontFamilyMapObserver(&pref_change_registrar_,
                                  prefs::kWebKitSansSerifFontFamilyMap,
                                  webkit_callback);
    RegisterFontFamilyMapObserver(&pref_change_registrar_,
                                  prefs::kWebKitCursiveFontFamilyMap,
                                  webkit_callback);
    RegisterFontFamilyMapObserver(&pref_change_registrar_,
                                  prefs::kWebKitFantasyFontFamilyMap,
                                  webkit_callback);
    RegisterFontFamilyMapObserver(&pref_change_registrar_,
                                  prefs::kWebKitPictographFontFamilyMap,
                                  webkit_callback);
  }

  static PrefWatcher* Get(Profile* profile);

  void RegisterHelper(PrefsTabHelper* helper) {
    helpers_.insert(helper);
  }

  void UnregisterHelper(PrefsTabHelper* helper) {
    helpers_.erase(helper);
  }

 private:
  // KeyedService overrides:
  void Shutdown() override {
    pref_change_registrar_.RemoveAll();
  }

  void UpdateRendererPreferences() {
    for (const auto& helper : helpers_)
      helper->UpdateRendererPreferences();
  }

  void OnWebPrefChanged(const std::string& pref_name) {
    for (const auto& helper : helpers_)
      helper->OnWebPrefChanged(pref_name);
  }

  Profile* profile_;
  PrefChangeRegistrar pref_change_registrar_;
  std::set<PrefsTabHelper*> helpers_;
};

class PrefWatcherFactory : public BrowserContextKeyedServiceFactory {
 public:
  static PrefWatcher* GetForProfile(Profile* profile) {
    return static_cast<PrefWatcher*>(
        GetInstance()->GetServiceForBrowserContext(profile, true));
  }

  static PrefWatcherFactory* GetInstance() {
    return base::Singleton<PrefWatcherFactory>::get();
  }

 private:
  friend struct base::DefaultSingletonTraits<PrefWatcherFactory>;

  PrefWatcherFactory() : BrowserContextKeyedServiceFactory(
      "PrefWatcher",
      BrowserContextDependencyManager::GetInstance()) {
  }

  ~PrefWatcherFactory() override {}

  // BrowserContextKeyedServiceFactory:
  KeyedService* BuildServiceInstanceFor(
      content::BrowserContext* browser_context) const override {
    return new PrefWatcher(Profile::FromBrowserContext(browser_context));
  }

  content::BrowserContext* GetBrowserContextToUse(
      content::BrowserContext* context) const override {
    return chrome::GetBrowserContextOwnInstanceInIncognito(context);
  }
};

// static
PrefWatcher* PrefWatcher::Get(Profile* profile) {
  return PrefWatcherFactory::GetForProfile(profile);
}

PrefsTabHelper::PrefsTabHelper(WebContents* contents)
    : web_contents_(contents),
      profile_(Profile::FromBrowserContext(web_contents_->GetBrowserContext())),
      weak_ptr_factory_(this) {
  PrefService* prefs = profile_->GetPrefs();
  if (prefs) {
    // If the tab is in an incognito profile, we track changes in the default
    // zoom level of the parent profile instead.
    Profile* profile_to_track = profile_->GetOriginalProfile();
    ChromeZoomLevelPrefs* zoom_level_prefs =
        profile_to_track->GetZoomLevelPrefs();

    base::Closure renderer_callback = base::Bind(
        &PrefsTabHelper::UpdateRendererPreferences, base::Unretained(this));
    // Tests should not need to create a ZoomLevelPrefs.
    if (zoom_level_prefs) {
      default_zoom_level_subscription_ =
          zoom_level_prefs->RegisterDefaultZoomLevelCallback(renderer_callback);
    }

    PrefWatcher::Get(profile_)->RegisterHelper(this);
  }

  content::RendererPreferences* render_prefs =
      web_contents_->GetMutableRendererPrefs();
  renderer_preferences_util::UpdateFromSystemSettings(render_prefs,
                                                      profile_,
                                                      web_contents_);

#if defined(OS_POSIX) && !defined(OS_MACOSX) && defined(ENABLE_THEMES)
  registrar_.Add(this,
                 chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
                 content::Source<ThemeService>(
                     ThemeServiceFactory::GetForProfile(profile_)));
#endif
#if defined(USE_AURA)
  registrar_.Add(this,
                 chrome::NOTIFICATION_BROWSER_FLING_CURVE_PARAMETERS_CHANGED,
                 content::NotificationService::AllSources());
#endif
}

PrefsTabHelper::~PrefsTabHelper() {
  PrefWatcher::Get(profile_)->UnregisterHelper(this);
}

// static
void PrefsTabHelper::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  WebPreferences pref_defaults;
  registry->RegisterBooleanPref(prefs::kWebKitJavascriptEnabled,
                                pref_defaults.javascript_enabled);
  registry->RegisterBooleanPref(prefs::kWebKitWebSecurityEnabled,
                                pref_defaults.web_security_enabled);
  registry->RegisterBooleanPref(
      prefs::kWebKitJavascriptCanOpenWindowsAutomatically, true);
  registry->RegisterBooleanPref(prefs::kWebKitLoadsImagesAutomatically,
                                pref_defaults.loads_images_automatically);
  registry->RegisterBooleanPref(prefs::kWebKitPluginsEnabled,
                                pref_defaults.plugins_enabled);
  registry->RegisterBooleanPref(prefs::kWebKitDomPasteEnabled,
                                pref_defaults.dom_paste_enabled);
  registry->RegisterBooleanPref(prefs::kWebKitTextAreasAreResizable,
                                pref_defaults.text_areas_are_resizable);
  registry->RegisterBooleanPref(prefs::kWebkitTabsToLinks,
                                pref_defaults.tabs_to_links);
  registry->RegisterBooleanPref(prefs::kWebKitAllowRunningInsecureContent,
                                false);
  registry->RegisterBooleanPref(prefs::kWebKitAllowDisplayingInsecureContent,
                                true);
  registry->RegisterBooleanPref(prefs::kEnableReferrers, true);
#if defined(OS_ANDROID)
  registry->RegisterDoublePref(prefs::kWebKitFontScaleFactor, 1.0);
  registry->RegisterBooleanPref(prefs::kWebKitForceEnableZoom,
                                pref_defaults.force_enable_zoom);
  registry->RegisterBooleanPref(prefs::kWebKitPasswordEchoEnabled,
                                pref_defaults.password_echo_enabled);
#endif
  registry->RegisterStringPref(
      prefs::kAcceptLanguages,
      l10n_util::GetStringUTF8(IDS_ACCEPT_LANGUAGES),
      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  registry->RegisterStringPref(
      prefs::kDefaultCharset,
      l10n_util::GetStringUTF8(IDS_DEFAULT_ENCODING),
      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);

  // Register font prefs that have defaults.
  std::set<std::string> fonts_with_defaults;
  UScriptCode browser_script = GetScriptOfBrowserLocale();
  for (size_t i = 0; i < kFontDefaultsLength; ++i) {
    FontDefault pref = kFontDefaults[i];

#if defined(OS_WIN)
    if (pref.pref_name == prefs::kWebKitFixedFontFamily) {
      if (ShouldUseAlternateDefaultFixedFont(
              l10n_util::GetStringUTF8(pref.resource_id)))
        pref.resource_id = IDS_FIXED_FONT_FAMILY_ALT_WIN;
    }

    // The standard font (Meiryo) isn't installed by default as of Win 10.
    if (base::win::GetVersion() >= base::win::VERSION_WIN10) {
      if (pref.pref_name == prefs::kWebKitStandardFontFamilyJapanese) {
        pref.resource_id = IDS_STANDARD_FONT_FAMILY_JAPANESE_ALT_WIN;
      } else if (pref.pref_name == prefs::kWebKitSansSerifFontFamilyJapanese) {
        pref.resource_id = IDS_SANS_SERIF_FONT_FAMILY_JAPANESE_ALT_WIN;
      }
    }
#endif

    UScriptCode pref_script = GetScriptOfFontPref(pref.pref_name);

    // Suppress this default font pref value if it is for the primary script of
    // the browser's UI locale.  For example, if the pref is for the sans-serif
    // font for the Cyrillic script, and the browser locale is "ru" (Russian),
    // the default is suppressed.  Otherwise, the default would override the
    // user's font preferences when viewing pages in their native language.
    // This is because users have no way yet of customizing their per-script
    // font preferences.  The font prefs accessible in the options UI are for
    // the default, unknown script; these prefs have less priority than the
    // per-script font prefs when the script of the content is known.  This code
    // can possibly be removed later if users can easily access per-script font
    // prefs (e.g., via the extensions workflow), or the problem turns out to
    // not be really critical after all.
    if (browser_script != pref_script) {
      registry->RegisterStringPref(pref.pref_name,
                                   l10n_util::GetStringUTF8(pref.resource_id));
      fonts_with_defaults.insert(pref.pref_name);
    }
  }

  // Register per-script font prefs that don't have defaults.
#if !defined(OS_ANDROID)
  RegisterFontFamilyPrefs(registry, fonts_with_defaults);
#endif

  RegisterLocalizedFontPref(registry, prefs::kWebKitDefaultFontSize,
                            IDS_DEFAULT_FONT_SIZE);
  RegisterLocalizedFontPref(registry, prefs::kWebKitDefaultFixedFontSize,
                            IDS_DEFAULT_FIXED_FONT_SIZE);
  RegisterLocalizedFontPref(registry, prefs::kWebKitMinimumFontSize,
                            IDS_MINIMUM_FONT_SIZE);
  RegisterLocalizedFontPref(registry, prefs::kWebKitMinimumLogicalFontSize,
                            IDS_MINIMUM_LOGICAL_FONT_SIZE);
  bool uses_universal_detector = IsAutodetectEncodingEnabledByDefault() ||
      l10n_util::GetStringUTF8(IDS_USES_UNIVERSAL_DETECTOR) == "true";
  registry->RegisterBooleanPref(
      prefs::kWebKitUsesUniversalDetector,
      uses_universal_detector,
      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  registry->RegisterStringPref(
      prefs::kStaticEncodings,
      l10n_util::GetStringUTF8(IDS_STATIC_ENCODING_LIST));
  registry->RegisterStringPref(prefs::kRecentlySelectedEncoding, std::string());
}

// static
void PrefsTabHelper::GetServiceInstance() {
  PrefWatcherFactory::GetInstance();
}

void PrefsTabHelper::Observe(int type,
                             const content::NotificationSource& source,
                             const content::NotificationDetails& details) {
#if defined(OS_POSIX) && !defined(OS_MACOSX) && defined(ENABLE_THEMES)
  if (type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED) {
    UpdateRendererPreferences();
    return;
  }
#endif

#if defined(USE_AURA)
  if (type == chrome::NOTIFICATION_BROWSER_FLING_CURVE_PARAMETERS_CHANGED) {
    UpdateRendererPreferences();
    return;
  }
#endif  // defined(USE_AURA)

  NOTREACHED();
}

void PrefsTabHelper::UpdateWebPreferences() {
  web_contents_->GetRenderViewHost()->UpdateWebkitPreferences(
      web_contents_->GetRenderViewHost()->GetWebkitPreferences());
}

void PrefsTabHelper::UpdateRendererPreferences() {
  content::RendererPreferences* prefs =
      web_contents_->GetMutableRendererPrefs();
  renderer_preferences_util::UpdateFromSystemSettings(
      prefs, profile_, web_contents_);
  web_contents_->GetRenderViewHost()->SyncRendererPrefs();
}
void PrefsTabHelper::OnFontFamilyPrefChanged(const std::string& pref_name) {
  // When a font family pref's value goes from non-empty to the empty string, we
  // must add it to the usual WebPreferences struct passed to the renderer.
  //
  // The empty string means to fall back to the pref for the Common script
  // ("Zyyy").  For example, if chrome.fonts.serif.Cyrl is the empty string, it
  // means to use chrome.fonts.serif.Zyyy for Cyrillic script. Prefs that are
  // the empty string are normally not passed to WebKit, since there are so many
  // of them that it would cause a performance regression. Not passing the pref
  // is normally okay since WebKit does the desired fallback behavior regardless
  // of whether the empty string is passed or the pref is not passed at all. But
  // if the pref has changed from non-empty to the empty string, we must let
  // WebKit know.
  std::string generic_family;
  std::string script;
  if (pref_names_util::ParseFontNamePrefPath(pref_name,
                                             &generic_family,
                                             &script)) {
    PrefService* prefs = profile_->GetPrefs();
    std::string pref_value = prefs->GetString(pref_name);
    if (pref_value.empty()) {
      WebPreferences web_prefs =
          web_contents_->GetRenderViewHost()->GetWebkitPreferences();
      OverrideFontFamily(&web_prefs, generic_family, script, std::string());
      web_contents_->GetRenderViewHost()->UpdateWebkitPreferences(web_prefs);
      return;
    }
  }
}

void PrefsTabHelper::OnWebPrefChanged(const std::string& pref_name) {
#if !defined(OS_ANDROID)
  OnFontFamilyPrefChanged(pref_name);
#endif

  web_contents_->GetRenderViewHost()->OnWebkitPreferencesChanged();
}