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 "content/public/browser/host_zoom_map.h" 6 7#include <algorithm> 8#include <string> 9#include <vector> 10 11#include "base/bind.h" 12#include "base/files/file_path.h" 13#include "base/files/file_util.h" 14#include "base/memory/ref_counted.h" 15#include "base/memory/scoped_ptr.h" 16#include "base/path_service.h" 17#include "base/prefs/pref_service.h" 18#include "base/strings/stringprintf.h" 19#include "base/values.h" 20#include "chrome/browser/chrome_page_zoom.h" 21#include "chrome/browser/profiles/profile.h" 22#include "chrome/browser/ui/browser.h" 23#include "chrome/browser/ui/tabs/tab_strip_model.h" 24#include "chrome/common/chrome_constants.h" 25#include "chrome/common/chrome_paths.h" 26#include "chrome/common/pref_names.h" 27#include "chrome/test/base/in_process_browser_test.h" 28#include "chrome/test/base/testing_profile.h" 29#include "chrome/test/base/ui_test_utils.h" 30#include "content/public/test/test_utils.h" 31#include "net/dns/mock_host_resolver.h" 32#include "net/test/embedded_test_server/embedded_test_server.h" 33#include "net/test/embedded_test_server/http_response.h" 34#include "testing/gmock/include/gmock/gmock.h" 35#include "url/gurl.h" 36 37namespace { 38 39class ZoomLevelChangeObserver { 40 public: 41 explicit ZoomLevelChangeObserver(Profile* profile) 42 : message_loop_runner_(new content::MessageLoopRunner) { 43 content::HostZoomMap* host_zoom_map = static_cast<content::HostZoomMap*>( 44 content::HostZoomMap::GetDefaultForBrowserContext(profile)); 45 subscription_ = host_zoom_map->AddZoomLevelChangedCallback(base::Bind( 46 &ZoomLevelChangeObserver::OnZoomLevelChanged, base::Unretained(this))); 47 } 48 49 void BlockUntilZoomLevelForHostHasChanged(const std::string& host) { 50 while (!std::count(changed_hosts_.begin(), changed_hosts_.end(), host)) { 51 message_loop_runner_->Run(); 52 message_loop_runner_ = new content::MessageLoopRunner; 53 } 54 changed_hosts_.clear(); 55 } 56 57 private: 58 void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change) { 59 changed_hosts_.push_back(change.host); 60 message_loop_runner_->Quit(); 61 } 62 63 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 64 std::vector<std::string> changed_hosts_; 65 scoped_ptr<content::HostZoomMap::Subscription> subscription_; 66 67 DISALLOW_COPY_AND_ASSIGN(ZoomLevelChangeObserver); 68}; 69 70} // namespace 71 72class HostZoomMapBrowserTest : public InProcessBrowserTest { 73 public: 74 HostZoomMapBrowserTest() {} 75 76 protected: 77 void SetDefaultZoomLevel(double level) { 78 browser()->profile()->GetPrefs()->SetDouble( 79 prefs::kDefaultZoomLevel, level); 80 } 81 82 double GetZoomLevel(const GURL& url) { 83 content::HostZoomMap* host_zoom_map = static_cast<content::HostZoomMap*>( 84 content::HostZoomMap::GetDefaultForBrowserContext( 85 browser()->profile())); 86 return host_zoom_map->GetZoomLevelForHostAndScheme(url.scheme(), 87 url.host()); 88 } 89 90 std::vector<std::string> GetHostsWithZoomLevels() { 91 typedef content::HostZoomMap::ZoomLevelVector ZoomLevelVector; 92 content::HostZoomMap* host_zoom_map = static_cast<content::HostZoomMap*>( 93 content::HostZoomMap::GetDefaultForBrowserContext( 94 browser()->profile())); 95 content::HostZoomMap::ZoomLevelVector zoom_levels = 96 host_zoom_map->GetAllZoomLevels(); 97 std::vector<std::string> results; 98 for (ZoomLevelVector::const_iterator it = zoom_levels.begin(); 99 it != zoom_levels.end(); ++it) 100 results.push_back(it->host); 101 return results; 102 } 103 104 std::vector<std::string> GetHostsWithZoomLevelsFromPrefs() { 105 PrefService* prefs = browser()->profile()->GetPrefs(); 106 const base::DictionaryValue* values = 107 prefs->GetDictionary(prefs::kPerHostZoomLevels); 108 std::vector<std::string> results; 109 if (values) { 110 for (base::DictionaryValue::Iterator it(*values); 111 !it.IsAtEnd(); it.Advance()) 112 results.push_back(it.key()); 113 } 114 return results; 115 } 116 117 GURL ConstructTestServerURL(const char* url_template) { 118 return GURL(base::StringPrintf( 119 url_template, embedded_test_server()->port())); 120 } 121 122 private: 123 scoped_ptr<net::test_server::HttpResponse> HandleRequest( 124 const net::test_server::HttpRequest& request) { 125 return scoped_ptr<net::test_server::HttpResponse>( 126 new net::test_server::BasicHttpResponse); 127 } 128 129 // BrowserTestBase: 130 virtual void SetUpOnMainThread() OVERRIDE { 131 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 132 embedded_test_server()->RegisterRequestHandler(base::Bind( 133 &HostZoomMapBrowserTest::HandleRequest, base::Unretained(this))); 134 host_resolver()->AddRule("*", "127.0.0.1"); 135 } 136 137 DISALLOW_COPY_AND_ASSIGN(HostZoomMapBrowserTest); 138}; 139 140class HostZoomMapSanitizationBrowserTest : public HostZoomMapBrowserTest { 141 public: 142 HostZoomMapSanitizationBrowserTest() {} 143 144 private: 145 // InProcessBrowserTest: 146 virtual bool SetUpUserDataDirectory() OVERRIDE { 147 // Zoom-related preferences demonstrating the two problems that could be 148 // caused by the bug. They incorrectly contain a per-host zoom level for the 149 // empty host; and a value for 'host1' that only differs from the default by 150 // epsilon. Neither should have been persisted. 151 const char kBrokenPrefs[] = 152 "{'profile': {" 153 " 'default_zoom_level': 1.2," 154 " 'per_host_zoom_levels': {'': 1.1, 'host1': 1.20001, 'host2': 1.3}" 155 "}}"; 156 std::string broken_prefs(kBrokenPrefs); 157 std::replace(broken_prefs.begin(), broken_prefs.end(), '\'', '\"'); 158 159 base::FilePath user_data_directory, path_to_prefs; 160 PathService::Get(chrome::DIR_USER_DATA, &user_data_directory); 161 path_to_prefs = user_data_directory 162 .AppendASCII(TestingProfile::kTestUserProfileDir) 163 .Append(chrome::kPreferencesFilename); 164 base::CreateDirectory(path_to_prefs.DirName()); 165 base::WriteFile(path_to_prefs, broken_prefs.c_str(), broken_prefs.size()); 166 return true; 167 } 168 169 DISALLOW_COPY_AND_ASSIGN(HostZoomMapSanitizationBrowserTest); 170}; 171 172// Regression test for crbug.com/364399. 173IN_PROC_BROWSER_TEST_F(HostZoomMapBrowserTest, ToggleDefaultZoomLevel) { 174 const double default_zoom_level = content::ZoomFactorToZoomLevel(1.5); 175 176 const char kTestURLTemplate1[] = "http://host1:%d/"; 177 const char kTestURLTemplate2[] = "http://host2:%d/"; 178 179 ZoomLevelChangeObserver observer(browser()->profile()); 180 181 GURL test_url1 = ConstructTestServerURL(kTestURLTemplate1); 182 ui_test_utils::NavigateToURL(browser(), test_url1); 183 184 SetDefaultZoomLevel(default_zoom_level); 185 observer.BlockUntilZoomLevelForHostHasChanged(test_url1.host()); 186 EXPECT_TRUE( 187 content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url1))); 188 189 GURL test_url2 = ConstructTestServerURL(kTestURLTemplate2); 190 ui_test_utils::NavigateToURLWithDisposition( 191 browser(), test_url2, NEW_FOREGROUND_TAB, 192 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 193 EXPECT_TRUE( 194 content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2))); 195 196 content::WebContents* web_contents = 197 browser()->tab_strip_model()->GetActiveWebContents(); 198 chrome_page_zoom::Zoom(web_contents, content::PAGE_ZOOM_OUT); 199 observer.BlockUntilZoomLevelForHostHasChanged(test_url2.host()); 200 EXPECT_FALSE( 201 content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2))); 202 203 chrome_page_zoom::Zoom(web_contents, content::PAGE_ZOOM_IN); 204 observer.BlockUntilZoomLevelForHostHasChanged(test_url2.host()); 205 EXPECT_TRUE( 206 content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2))); 207 208 // Now both tabs should be at the default zoom level, so there should not be 209 // any per-host values saved either to Pref, or internally in HostZoomMap. 210 EXPECT_TRUE(GetHostsWithZoomLevels().empty()); 211 EXPECT_TRUE(GetHostsWithZoomLevelsFromPrefs().empty()); 212} 213 214// Test that garbage data from crbug.com/364399 is cleared up on startup. 215IN_PROC_BROWSER_TEST_F(HostZoomMapSanitizationBrowserTest, ClearOnStartup) { 216 EXPECT_THAT(GetHostsWithZoomLevels(), testing::ElementsAre("host2")); 217 EXPECT_THAT(GetHostsWithZoomLevelsFromPrefs(), testing::ElementsAre("host2")); 218} 219