dom-ui settings: Add setting for detault zoom level.

BUG=11321
TEST=Exercise the default zoom level setting in the 'Under the hood' panel.
Review URL: http://codereview.chromium.org/3807001

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63702 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 32f7f97..187cd0f 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -2054,6 +2054,7 @@
   prefs->RegisterBooleanPref(prefs::kRemotingHasSetupCompleted, false);
   prefs->RegisterStringPref(prefs::kCloudPrintEmail, std::string());
   prefs->RegisterBooleanPref(prefs::kDevToolsDisabled, false);
+  prefs->RegisterRealPref(prefs::kDefaultZoomLevel, 0.0);
 }
 
 // static
diff --git a/chrome/browser/dom_ui/options/advanced_options_handler.cc b/chrome/browser/dom_ui/options/advanced_options_handler.cc
index 15a297c0..aadfaf8 100644
--- a/chrome/browser/dom_ui/options/advanced_options_handler.cc
+++ b/chrome/browser/dom_ui/options/advanced_options_handler.cc
@@ -112,6 +112,8 @@
       l10n_util::GetStringUTF16(IDS_OPTIONS_TABS_TO_LINKS_PREF));
   localized_strings->SetString("fontSettingsInfo",
       l10n_util::GetStringUTF16(IDS_OPTIONS_FONTSETTINGS_INFO));
+  localized_strings->SetString("defaultZoomLevelLabel",
+      l10n_util::GetStringUTF16(IDS_OPTIONS_DEFAULT_ZOOM_LEVEL_LABEL));
   localized_strings->SetString("fontSettingsConfigureFontsOnlyButton",
       l10n_util::GetStringUTF16(
           IDS_OPTIONS_FONTSETTINGS_CONFIGUREFONTSONLY_BUTTON));
@@ -183,6 +185,7 @@
 void AdvancedOptionsHandler::Initialize() {
   DCHECK(dom_ui_);
   SetupMetricsReportingCheckbox(false);
+  SetupDefaultZoomLevel();
   SetupDownloadLocationPath();
   SetupAutoOpenFileTypesDisabledAttribute();
   SetupProxySettingsSection();
@@ -215,6 +218,7 @@
   default_download_location_.Init(prefs::kDownloadDefaultDirectory,
                                   prefs, this);
   auto_open_files_.Init(prefs::kDownloadExtensionsToOpen, prefs, this);
+  default_zoom_level_.Init(prefs::kDefaultZoomLevel, prefs, this);
   proxy_prefs_.reset(
       PrefSetObserver::CreateProxyPrefSetObserver(prefs, this));
 
@@ -234,6 +238,8 @@
   dom_ui_->RegisterMessageCallback("resetToDefaults",
       NewCallback(this,
                   &AdvancedOptionsHandler::HandleResetToDefaults));
+  dom_ui_->RegisterMessageCallback("defaultZoomLevelAction",
+      NewCallback(this, &AdvancedOptionsHandler::HandleDefaultZoomLevel));
 #if !defined(OS_CHROMEOS)
   dom_ui_->RegisterMessageCallback("metricsReportingCheckboxAction",
       NewCallback(this,
@@ -351,6 +357,14 @@
 #endif
 }
 
+void AdvancedOptionsHandler::HandleDefaultZoomLevel(const ListValue* args) {
+  UserMetricsRecordAction(UserMetricsAction("Options_ChangeDefaultZoomLevel"));
+  int zoom_level;
+  if (ExtractIntegerValue(args, &zoom_level)) {
+    default_zoom_level_.SetValue(static_cast<double>(zoom_level));
+  }
+}
+
 #if defined(OS_WIN)
 void AdvancedOptionsHandler::HandleCheckRevocationCheckbox(
     const ListValue* args) {
@@ -473,6 +487,13 @@
 #endif
 }
 
+void AdvancedOptionsHandler::SetupDefaultZoomLevel() {
+  // We're only interested in integer values, so convert to int.
+  FundamentalValue value(static_cast<int>(default_zoom_level_.GetValue()));
+  dom_ui_->CallJavascriptFunction(
+      L"options.AdvancedOptions.SetDefaultZoomLevel", value);
+}
+
 void AdvancedOptionsHandler::SetupDownloadLocationPath() {
   StringValue value(default_download_location_.GetValue().value());
   dom_ui_->CallJavascriptFunction(
diff --git a/chrome/browser/dom_ui/options/advanced_options_handler.h b/chrome/browser/dom_ui/options/advanced_options_handler.h
index 64f1094..7b9425e2f 100644
--- a/chrome/browser/dom_ui/options/advanced_options_handler.h
+++ b/chrome/browser/dom_ui/options/advanced_options_handler.h
@@ -58,6 +58,12 @@
   // Callback for the "metricsReportingCheckboxAction" message.  This is called
   // if the user toggles the metrics reporting checkbox.
   void HandleMetricsReportingCheckbox(const ListValue* args);
+
+  // Callback for the "defaultZoomLevelAction" message.  This is called if the
+  // user changes the default zoom level.  |args| is an array that contains
+  // one item, the zoom level as a numeric value.
+  void HandleDefaultZoomLevel(const ListValue* args);
+
 #if defined(OS_WIN)
   // Callback for the "Check SSL Revocation" checkbox.  This is needed so we
   // can support manual handling on Windows.
@@ -115,6 +121,8 @@
   // Setup the checked state for the metrics reporting checkbox.
   void SetupMetricsReportingCheckbox(bool user_changed);
 
+  void SetupDefaultZoomLevel();
+
   // Setup the download path based on user preferences.
   void SetupDownloadLocationPath();
 
@@ -138,6 +146,7 @@
 
   FilePathPrefMember default_download_location_;
   StringPrefMember auto_open_files_;
+  RealPrefMember default_zoom_level_;
   scoped_ptr<PrefSetObserver> proxy_prefs_;
   scoped_ptr<OptionsManagedBannerHandler> banner_handler_;
 
@@ -145,3 +154,4 @@
 };
 
 #endif  // CHROME_BROWSER_DOM_UI_OPTIONS_ADVANCED_OPTIONS_HANDLER_H_
+
diff --git a/chrome/browser/host_zoom_map.cc b/chrome/browser/host_zoom_map.cc
index b9d8d24..19e55bb1 100644
--- a/chrome/browser/host_zoom_map.cc
+++ b/chrome/browser/host_zoom_map.cc
@@ -29,6 +29,8 @@
     : profile_(profile),
       updating_preferences_(false) {
   Load();
+  default_zoom_level_ =
+      profile_->GetPrefs()->GetReal(prefs::kDefaultZoomLevel);
   registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
                  Source<Profile>(profile));
   // Don't observe pref changes (e.g. from sync) in Incognito; once we create
@@ -37,6 +39,7 @@
   if (!profile_->IsOffTheRecord()) {
     pref_change_registrar_.Init(profile_->GetPrefs());
     pref_change_registrar_.Add(prefs::kPerHostZoomLevels, this);
+    pref_change_registrar_.Add(prefs::kDefaultZoomLevel, this);
   }
 
   registrar_.Add(
@@ -94,7 +97,7 @@
   std::string host(net::GetHostOrSpecFromURL(url));
   AutoLock auto_lock(lock_);
   HostZoomLevels::const_iterator i(host_zoom_levels_.find(host));
-  return (i == host_zoom_levels_.end()) ? 0 : i->second;
+  return (i == host_zoom_levels_.end()) ? default_zoom_level_ : i->second;
 }
 
 void HostZoomMap::SetZoomLevel(const GURL& url, double level) {
@@ -106,7 +109,7 @@
 
   {
     AutoLock auto_lock(lock_);
-    if (level == 0)
+    if (level == default_zoom_level_)
       host_zoom_levels_.erase(host);
     else
       host_zoom_levels_[host] = level;
@@ -126,7 +129,7 @@
     ScopedPrefUpdate update(profile_->GetPrefs(), prefs::kPerHostZoomLevels);
     DictionaryValue* host_zoom_dictionary =
         profile_->GetPrefs()->GetMutableDictionary(prefs::kPerHostZoomLevels);
-    if (level == 0) {
+    if (level == default_zoom_level_) {
       host_zoom_dictionary->RemoveWithoutPathExpansion(host, NULL);
     } else {
       host_zoom_dictionary->SetWithoutPathExpansion(
@@ -240,6 +243,10 @@
         std::string* name = Details<std::string>(details).ptr();
         if (prefs::kPerHostZoomLevels == *name)
           Load();
+        else if (prefs::kDefaultZoomLevel == *name) {
+          default_zoom_level_ =
+              profile_->GetPrefs()->GetReal(prefs::kDefaultZoomLevel);
+        }
       }
       break;
     }
diff --git a/chrome/browser/host_zoom_map.h b/chrome/browser/host_zoom_map.h
index b75dcea..10f512e1 100644
--- a/chrome/browser/host_zoom_map.h
+++ b/chrome/browser/host_zoom_map.h
@@ -34,22 +34,22 @@
   // Returns the zoom level for a given url. The zoom level is determined by
   // the host portion of the URL, or (in the absence of a host) the complete
   // spec of the URL. In most cases, there is no custom zoom level, and this
-  // returns 0.  Otherwise, returns the saved zoom level, which may be positive
-  // (to zoom in) or negative (to zoom out).
+  // returns the user's default zoom level.  Otherwise, returns the saved zoom
+  // level, which may be positive (to zoom in) or negative (to zoom out).
   //
   // This may be called on any thread.
   double GetZoomLevel(const GURL& url) const;
 
-  // Sets the zoom level for a given url to |level|.  If the level is 0,
-  // the host is erased from the saved preferences; otherwise the new value is
-  // written out.
+  // Sets the zoom level for a given url to |level|.  If the level matches the
+  // current default zoom level, the host is erased from the saved preferences;
+  // otherwise the new value is written out.
   //
   // This should only be called on the UI thread.
   void SetZoomLevel(const GURL& url, double level);
 
   // Returns the temporary zoom level that's only valid for the lifetime of
   // the given tab (i.e. isn't saved and doesn't affect other tabs) if it
-  // exists, 0 otherwise.
+  // exists, the default zoom level otherwise.
   //
   // This may be called on any thread.
   double GetTemporaryZoomLevel(int render_process_id,
@@ -92,6 +92,7 @@
 
   // Copy of the pref data, so that we can read it on the IO thread.
   HostZoomLevels host_zoom_levels_;
+  double default_zoom_level_;
 
   struct TemporaryZoomLevel {
     int render_process_id;
@@ -103,8 +104,8 @@
   // level, so vector is fine for now.
   std::vector<TemporaryZoomLevel> temporary_zoom_levels_;
 
-  // Used around accesses to |host_zoom_levels_| and |temporary_zoom_levels_| to
-  // guarantee thread safety.
+  // Used around accesses to |host_zoom_levels_|, |default_zoom_level_| and
+  // |temporary_zoom_levels_| to guarantee thread safety.
   mutable Lock lock_;
 
   // Whether we are currently updating preferences, this is used to ignore
diff --git a/chrome/browser/host_zoom_map_unittest.cc b/chrome/browser/host_zoom_map_unittest.cc
index ea23b33..f77f96bb 100644
--- a/chrome/browser/host_zoom_map_unittest.cc
+++ b/chrome/browser/host_zoom_map_unittest.cc
@@ -27,6 +27,7 @@
 class HostZoomMapTest : public testing::Test {
  public:
   static const double kZoomLevel;
+  static const double kDefaultZoomLevel;
   HostZoomMapTest()
       : ui_thread_(BrowserThread::UI, &message_loop_),
         prefs_(profile_.GetPrefs()),
@@ -54,6 +55,7 @@
   NotificationObserverMock pref_observer_;
 };
 const double HostZoomMapTest::kZoomLevel = 4;
+const double HostZoomMapTest::kDefaultZoomLevel = -2;
 
 TEST_F(HostZoomMapTest, LoadNoPrefs) {
   scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_));
@@ -120,3 +122,11 @@
   EXPECT_EQ(kZoomLevel, map->GetZoomLevel(file_url1_));
   EXPECT_EQ(0, map->GetZoomLevel(file_url2_));
 }
+
+TEST_F(HostZoomMapTest, ChangeDefaultZoomLevel) {
+  FundamentalValue zoom_level(kDefaultZoomLevel);
+  prefs_->Set(prefs::kDefaultZoomLevel, zoom_level);
+  scoped_refptr<HostZoomMap> map(new HostZoomMap(&profile_));
+  EXPECT_EQ(kDefaultZoomLevel, map->GetZoomLevel(url_));
+}
+
diff --git a/chrome/browser/options_util.cc b/chrome/browser/options_util.cc
index caf0ee0..56d0c07b 100644
--- a/chrome/browser/options_util.cc
+++ b/chrome/browser/options_util.cc
@@ -30,6 +30,7 @@
     prefs::kClearSiteDataOnExit,
     prefs::kCookieBehavior,
     prefs::kDefaultCharset,
+    prefs::kDefaultZoomLevel,
     prefs::kDnsPrefetchingEnabled,
 #if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_OPENBSD)
     prefs::kCertRevocationCheckingEnabled,
diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc
index b8508dc0..82b09c1 100644
--- a/chrome/browser/renderer_host/render_view_host.cc
+++ b/chrome/browser/renderer_host/render_view_host.cc
@@ -433,6 +433,10 @@
   Send(new ViewMsg_Zoom(routing_id(), function));
 }
 
+void RenderViewHost::SetZoomLevel(double zoom_level) {
+  Send(new ViewMsg_SetZoomLevel(routing_id(), zoom_level));
+}
+
 void RenderViewHost::SetPageEncoding(const std::string& encoding_name) {
   Send(new ViewMsg_SetPageEncoding(routing_id(), encoding_name));
 }
diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h
index 76f1f01..338e7a4 100644
--- a/chrome/browser/renderer_host/render_view_host.h
+++ b/chrome/browser/renderer_host/render_view_host.h
@@ -216,9 +216,12 @@
   // Cancel a pending find operation.
   void StopFinding(FindBarController::SelectionAction selection_action);
 
-  // Change the zoom level of a page.
+  // Increment, decrement, or reset the zoom level of a page.
   void Zoom(PageZoom::Function function);
 
+  // Change the zoom level of a page to a specific value.
+  void SetZoomLevel(double zoom_level);
+
   // Change the encoding of the page.
   void SetPageEncoding(const std::string& encoding);
 
diff --git a/chrome/browser/resources/options/advanced_options.html b/chrome/browser/resources/options/advanced_options.html
index f6b94ea..2469a79 100644
--- a/chrome/browser/resources/options/advanced_options.html
+++ b/chrome/browser/resources/options/advanced_options.html
@@ -92,6 +92,23 @@
 </if>
       <div><button id="fontSettingsConfigureFontsOnlyButton"
           i18n-content="fontSettingsConfigureFontsOnlyButton"></button></div>
+      <div>
+        <label style="display:inline;">
+          <span i18n-content="defaultZoomLevelLabel"></span>
+          <select id="defaultZoomLevel">
+            <option value="-3">57%</option>
+            <option value="-2">69%</option>
+            <option value="-1">83%</option>
+            <option value="0">100%</option>
+            <option value="1">120%</option>
+            <option value="2">144%</option>
+            <option value="3">172%</option>
+            <option value="4">207%</option>
+            <option value="5">248%</option>
+            <option value="6">298%</option>
+          </select>
+        </label>
+      </div>
 <if expr="os == 'win32'">
       <div>
         <label style="display:inline;">
diff --git a/chrome/browser/resources/options/advanced_options.js b/chrome/browser/resources/options/advanced_options.js
index 0cd785d..2e3405a9 100644
--- a/chrome/browser/resources/options/advanced_options.js
+++ b/chrome/browser/resources/options/advanced_options.js
@@ -56,6 +56,10 @@
         OptionsPage.showPageByName('fontSettings');
         chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
       };
+      $('defaultZoomLevel').onchange = function(event) {
+        chrome.send('defaultZoomLevelAction',
+            [String(event.target.options[event.target.selectedIndex].value)]);
+      }
       $('optionsReset').onclick = function(event) {
         AlertOverlay.show(undefined,
             localStrings.getString('optionsResetMessage'),
@@ -167,6 +171,18 @@
       AdvancedOptions.getInstance().showRestartRequiredAlert_();
   }
 
+  // Set the default zoom level selected item.
+  AdvancedOptions.SetDefaultZoomLevel = function(value) {
+    var selectCtl = $('defaultZoomLevel');
+    for (var i = 0; i < selectCtl.options.length; i++) {
+      if (selectCtl.options[i].value == value) {
+        selectCtl.selectedIndex = i;
+        return;
+      }
+    }
+    selectCtl.selectedIndex = 4;  // 100%
+  };
+
   // Set the download path.
   AdvancedOptions.SetDownloadLocationPath = function(path) {
     if (!cr.isChromeOS)
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index c7e68ea..2624094 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -167,6 +167,7 @@
 // The list of prefs we want to observe.
 const char* kPrefsToObserve[] = {
   prefs::kAlternateErrorPagesEnabled,
+  prefs::kDefaultZoomLevel,
   prefs::kWebKitJavaEnabled,
   prefs::kWebKitJavascriptEnabled,
   prefs::kWebKitLoadsImagesAutomatically,
@@ -1465,12 +1466,10 @@
     hs->SetPageTitle(entry.virtual_url(), entry.title());
 }
 
-int TabContents::GetZoomPercent(bool* enable_increment,
-                                bool* enable_decrement) {
-  *enable_decrement = *enable_increment = false;
+double TabContents::GetZoomLevel() const {
   HostZoomMap* zoom_map = profile()->GetHostZoomMap();
   if (!zoom_map)
-    return 100;
+    return 0;
 
   double zoom_level;
   if (temporary_zoom_settings_) {
@@ -1479,9 +1478,14 @@
   } else {
     zoom_level = zoom_map->GetZoomLevel(GetURL());
   }
+  return zoom_level;
+}
 
+int TabContents::GetZoomPercent(bool* enable_increment,
+                                bool* enable_decrement) {
+  *enable_decrement = *enable_increment = false;
   int percent = static_cast<int>(
-      WebKit::WebView::zoomLevelToZoomFactor(zoom_level) * 100);
+      WebKit::WebView::zoomLevelToZoomFactor(GetZoomLevel()) * 100);
   *enable_decrement = percent > minimum_zoom_percent_;
   *enable_increment = percent < maximum_zoom_percent_;
   return percent;
@@ -1759,6 +1763,10 @@
   render_view_host()->UpdateWebPreferences(GetWebkitPrefs());
 }
 
+void TabContents::UpdateZoomLevel() {
+  render_view_host()->SetZoomLevel(GetZoomLevel());
+}
+
 void TabContents::UpdateMaxPageIDIfNecessary(SiteInstance* site_instance,
                                              RenderViewHost* rvh) {
   // If we are creating a RVH for a restored controller, then we might
@@ -3081,6 +3089,8 @@
       } else if ((*pref_name_in == prefs::kDefaultCharset) ||
                  StartsWithASCII(*pref_name_in, "webkit.webprefs.", true)) {
         UpdateWebPreferences();
+      } else if (*pref_name_in == prefs::kDefaultZoomLevel) {
+        UpdateZoomLevel();
       } else {
         NOTREACHED() << "unexpected pref change notification" << *pref_name_in;
       }
diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h
index b78a83b..150131a 100644
--- a/chrome/browser/tab_contents/tab_contents.h
+++ b/chrome/browser/tab_contents/tab_contents.h
@@ -707,6 +707,9 @@
   // the page title and we know we want to update history.
   void UpdateHistoryPageTitle(const NavigationEntry& entry);
 
+  // Gets the zoom level for this tab.
+  double GetZoomLevel() const;
+
   // Gets the zoom percent for this tab.
   int GetZoomPercent(bool* enable_increment, bool* enable_decrement);
 
@@ -802,6 +805,9 @@
   // Send webkit specific settings to the renderer.
   void UpdateWebPreferences();
 
+  // Instruct the renderer to update the zoom level.
+  void UpdateZoomLevel();
+
   // If our controller was restored and the page id is > than the site
   // instance's page id, the site instances page id is updated as well as the
   // renderers max page id.